260322:1648 Correct Coresspondence / Doing RFA / Correct CI
CI Pipeline / build (push) Failing after 12m41s
Build and Deploy / deploy (push) Failing after 2m44s

This commit is contained in:
admin
2026-03-22 16:48:12 +07:00
parent e5deedb42e
commit 11984bfa29
683 changed files with 105251 additions and 29068 deletions
@@ -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)}`