260322:1648 Correct Coresspondence / Doing RFA / Correct CI
This commit is contained in:
@@ -21,7 +21,7 @@ export class WorkflowDslParser {
|
||||
async parse(dslJson: string): Promise<WorkflowDefinition> {
|
||||
try {
|
||||
// Step 1: Parse JSON
|
||||
const rawDsl = JSON.parse(dslJson);
|
||||
const rawDsl = JSON.parse(dslJson) as unknown;
|
||||
|
||||
// Step 2: Validate with Zod schema
|
||||
const dsl = WorkflowDslSchema.parse(rawDsl);
|
||||
@@ -139,7 +139,7 @@ export class WorkflowDslParser {
|
||||
const definition = new WorkflowDefinition();
|
||||
definition.workflow_code = dsl.name;
|
||||
// Map Semver (1.0.0) to version int (1)
|
||||
const majorVersion = parseInt(dsl.version.split('.')[0], 10);
|
||||
const majorVersion = Number(dsl.version.split('.')[0]);
|
||||
definition.version = isNaN(majorVersion) ? 1 : majorVersion;
|
||||
definition.description = dsl.description;
|
||||
definition.dsl = dsl;
|
||||
@@ -182,7 +182,7 @@ export class WorkflowDslParser {
|
||||
*/
|
||||
validateOnly(dslJson: string): { valid: boolean; errors?: string[] } {
|
||||
try {
|
||||
const rawDsl = JSON.parse(dslJson);
|
||||
const rawDsl = JSON.parse(dslJson) as unknown;
|
||||
const dsl = WorkflowDslSchema.parse(rawDsl);
|
||||
this.validateStateMachine(dsl);
|
||||
return { valid: true };
|
||||
|
||||
@@ -21,5 +21,5 @@ export class EvaluateWorkflowDto {
|
||||
@ApiProperty({ description: 'Context', example: { userId: 1 } })
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
context?: Record<string, any>;
|
||||
context?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
@@ -6,5 +6,5 @@ import { CreateWorkflowDefinitionDto } from './create-workflow-definition.dto';
|
||||
// PartialType จะทำให้ทุก field ใน CreateDto กลายเป็น Optional (?)
|
||||
// เหมาะสำหรับ PATCH method
|
||||
export class UpdateWorkflowDefinitionDto extends PartialType(
|
||||
CreateWorkflowDefinitionDto,
|
||||
CreateWorkflowDefinitionDto
|
||||
) {}
|
||||
|
||||
@@ -26,5 +26,5 @@ export class WorkflowTransitionDto {
|
||||
})
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
payload?: Record<string, any>;
|
||||
payload?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ export class WorkflowHistory {
|
||||
nullable: true,
|
||||
comment: 'Snapshot of Context or Metadata',
|
||||
})
|
||||
metadata?: Record<string, any>;
|
||||
metadata?: Record<string, unknown>;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at' })
|
||||
createdAt!: Date;
|
||||
|
||||
@@ -70,7 +70,7 @@ export class WorkflowInstance {
|
||||
// Context: เก็บตัวแปรที่จำเป็นสำหรับการตัดสินใจใน Workflow
|
||||
// เช่น { "amount": 500000, "requester_role": "ENGINEER", "approver_ids": [1, 2] }
|
||||
@Column({ type: 'json', nullable: true, comment: 'Runtime Context Data' })
|
||||
context?: Record<string, any>;
|
||||
context?: Record<string, unknown>;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at' })
|
||||
createdAt!: Date;
|
||||
|
||||
@@ -255,7 +255,9 @@ export class WorkflowDslService {
|
||||
|
||||
// Create a function that returns the expression result
|
||||
// "context" is available inside the expression
|
||||
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
||||
const func = new Function('context', `return ${expression};`);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
return !!func(context);
|
||||
} catch (error: unknown) {
|
||||
this.logger.error(
|
||||
|
||||
@@ -115,7 +115,7 @@ export class WorkflowEngineController {
|
||||
summary: 'ดึงรายการปุ่ม Action ที่สามารถกดได้ ณ สถานะปัจจุบัน',
|
||||
})
|
||||
@RequirePermission('document.view') // ผู้ที่มีสิทธิ์ดูเอกสาร ควรดู Action ได้
|
||||
async getAvailableActions(@Param('id') instanceId: string) {
|
||||
getAvailableActions(@Param('id') _instanceId: string) {
|
||||
// Note: Logic การดึง Action ตาม Instance ID จะถูก Implement ใน Task ถัดไป
|
||||
return { message: 'Pending implementation in Service layer' };
|
||||
}
|
||||
|
||||
@@ -90,9 +90,9 @@ export class WorkflowEngineService {
|
||||
|
||||
const saved = await this.workflowDefRepo.save(entity);
|
||||
this.logger.log(
|
||||
`Created Workflow Definition: ${(saved as WorkflowDefinition).workflow_code} v${(saved as WorkflowDefinition).version}`
|
||||
`Created Workflow Definition: ${saved.workflow_code} v${saved.version}`
|
||||
);
|
||||
return saved as WorkflowDefinition;
|
||||
return saved;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -258,7 +258,7 @@ export class WorkflowEngineService {
|
||||
action: string,
|
||||
userId: number,
|
||||
comment?: string,
|
||||
payload: Record<string, any> = {}
|
||||
payload: Record<string, unknown> = {}
|
||||
) {
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
await queryRunner.connect();
|
||||
@@ -341,7 +341,7 @@ export class WorkflowEngineService {
|
||||
|
||||
// [NEW] Dispatch Events (Async) ผ่าน WorkflowEventService
|
||||
if (eventsToDispatch && eventsToDispatch.length > 0) {
|
||||
this.eventService.dispatchEvents(
|
||||
void this.eventService.dispatchEvents(
|
||||
instance.id,
|
||||
eventsToDispatch,
|
||||
updatedContext
|
||||
@@ -368,7 +368,7 @@ export class WorkflowEngineService {
|
||||
/**
|
||||
* (Utility) Evaluate แบบไม่บันทึกผล (Dry Run) สำหรับ Test หรือ Preview
|
||||
*/
|
||||
async evaluate(dto: EvaluateWorkflowDto): Promise<any> {
|
||||
async evaluate(dto: EvaluateWorkflowDto): Promise<unknown> {
|
||||
const definition = await this.workflowDefRepo.findOne({
|
||||
where: { workflow_code: dto.workflow_code, is_active: true },
|
||||
order: { version: 'DESC' },
|
||||
@@ -401,9 +401,8 @@ export class WorkflowEngineService {
|
||||
action: string,
|
||||
returnToSequence?: number
|
||||
): TransitionResult {
|
||||
switch (action) {
|
||||
case WorkflowAction.APPROVE:
|
||||
case WorkflowAction.ACKNOWLEDGE:
|
||||
const act = action.toUpperCase();
|
||||
switch (act) {
|
||||
case 'APPROVE':
|
||||
case 'ACKNOWLEDGE':
|
||||
if (currentSequence >= totalSteps) {
|
||||
@@ -418,7 +417,6 @@ export class WorkflowEngineService {
|
||||
shouldUpdateStatus: false,
|
||||
};
|
||||
|
||||
case WorkflowAction.REJECT:
|
||||
case 'REJECT':
|
||||
return {
|
||||
nextStepSequence: null,
|
||||
@@ -426,8 +424,7 @@ export class WorkflowEngineService {
|
||||
documentStatus: 'REJECTED',
|
||||
};
|
||||
|
||||
case WorkflowAction.RETURN:
|
||||
case 'RETURN':
|
||||
case 'RETURN': {
|
||||
const targetStep = returnToSequence || currentSequence - 1;
|
||||
if (targetStep < 1) {
|
||||
throw new BadRequestException('Cannot return beyond the first step');
|
||||
@@ -437,6 +434,7 @@ export class WorkflowEngineService {
|
||||
shouldUpdateStatus: true,
|
||||
documentStatus: 'REVISE_REQUIRED',
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
this.logger.warn(
|
||||
|
||||
@@ -25,10 +25,10 @@ export class WorkflowEventService {
|
||||
/**
|
||||
* ประมวลผลรายการ Events ที่เกิดจากการเปลี่ยนสถานะ
|
||||
*/
|
||||
async dispatchEvents(
|
||||
dispatchEvents(
|
||||
instanceId: string,
|
||||
events: RawEvent[],
|
||||
context: Record<string, any>
|
||||
context: Record<string, unknown>
|
||||
) {
|
||||
if (!events || events.length === 0) return;
|
||||
|
||||
@@ -37,13 +37,15 @@ export class WorkflowEventService {
|
||||
);
|
||||
|
||||
// ทำแบบ Async ไม่รอผล (Fire-and-forget) เพื่อไม่ให้กระทบ Response Time ของ User
|
||||
Promise.allSettled(
|
||||
void Promise.allSettled(
|
||||
events.map((event) => this.processSingleEvent(instanceId, event, context))
|
||||
).then((results) => {
|
||||
// Log errors if any
|
||||
results.forEach((res, idx) => {
|
||||
if (res.status === 'rejected') {
|
||||
this.logger.error(`Failed to process event [${idx}]: ${res.reason}`);
|
||||
this.logger.error(
|
||||
`Failed to process event [${idx}]: ${String(res.reason)}`
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -54,13 +56,14 @@ export class WorkflowEventService {
|
||||
event: RawEvent,
|
||||
context: Record<string, unknown>
|
||||
) {
|
||||
await Promise.resolve();
|
||||
try {
|
||||
switch (event.type) {
|
||||
case 'notify':
|
||||
await this.handleNotify(event, context);
|
||||
this.handleNotify(event, context);
|
||||
break;
|
||||
case 'webhook':
|
||||
await this.handleWebhook(event, context);
|
||||
this.handleWebhook(event, context);
|
||||
break;
|
||||
case 'auto_action':
|
||||
// Logic สำหรับ Auto Transition (เช่น ถ้าผ่านเงื่อนไข ให้ไปต่อเลย)
|
||||
@@ -70,17 +73,16 @@ export class WorkflowEventService {
|
||||
this.logger.warn(`Unknown event type: ${event.type}`);
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error(`Error processing event ${event.type}: ${error}`);
|
||||
this.logger.error(
|
||||
`Error processing event ${event.type}: ${String(error)}`
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Handlers ---
|
||||
|
||||
private async handleNotify(
|
||||
event: RawEvent,
|
||||
_context: Record<string, unknown>
|
||||
) {
|
||||
private handleNotify(event: RawEvent, _context: Record<string, unknown>) {
|
||||
// Mockup: ในของจริงจะเรียก NotificationService.send()
|
||||
// const recipients = this.resolveRecipients(event.target, context);
|
||||
this.logger.log(
|
||||
@@ -88,10 +90,7 @@ export class WorkflowEventService {
|
||||
);
|
||||
}
|
||||
|
||||
private async handleWebhook(
|
||||
event: RawEvent,
|
||||
_context: Record<string, unknown>
|
||||
) {
|
||||
private handleWebhook(event: RawEvent, _context: Record<string, unknown>) {
|
||||
// Mockup: เรียก HttpService.post()
|
||||
this.logger.log(
|
||||
`[EVENT] Webhook to: "${event.target}" | Payload: ${JSON.stringify(event.payload)}`
|
||||
|
||||
Reference in New Issue
Block a user