251129:1700 update to 1.4.5

This commit is contained in:
admin
2025-11-29 16:50:34 +07:00
parent f7a43600a3
commit a78c9941be
55 changed files with 14641 additions and 2090 deletions
@@ -0,0 +1,164 @@
// File: src/modules/circulation/circulation-workflow.service.ts
import { Injectable, Logger, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { DataSource, Repository } from 'typeorm';
// Modules
import { WorkflowEngineService } from '../workflow-engine/workflow-engine.service';
// Entities
import { CirculationStatusCode } from './entities/circulation-status-code.entity';
import { Circulation } from './entities/circulation.entity';
// DTOs
import { WorkflowTransitionDto } from '../workflow-engine/dto/workflow-transition.dto';
@Injectable()
export class CirculationWorkflowService {
private readonly logger = new Logger(CirculationWorkflowService.name);
private readonly WORKFLOW_CODE = 'CIRCULATION_INTERNAL_V1';
constructor(
private readonly workflowEngine: WorkflowEngineService,
@InjectRepository(Circulation)
private readonly circulationRepo: Repository<Circulation>,
@InjectRepository(CirculationStatusCode)
private readonly statusRepo: Repository<CirculationStatusCode>,
private readonly dataSource: DataSource,
) {}
/**
* เริ่มต้นใบเวียน (Start Circulation)
* ปกติจะเริ่มเมื่อสร้าง Circulation หรือเมื่อกดส่ง
*/
async startCirculation(circulationId: number, userId: number) {
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
const circulation = await this.circulationRepo.findOne({
where: { id: circulationId },
});
if (!circulation) {
throw new NotFoundException(
`Circulation ID ${circulationId} not found`,
);
}
// Context อาจประกอบด้วย Department หรือ Priority
const context = {
organizationId: circulation.organization,
creatorId: userId,
};
// Create Instance (Entity Type = 'circulation')
const instance = await this.workflowEngine.createInstance(
this.WORKFLOW_CODE,
'circulation',
circulation.id.toString(),
context,
);
// Auto start (OPEN -> IN_REVIEW)
const transitionResult = await this.workflowEngine.processTransition(
instance.id,
'START',
userId,
'Start Circulation Process',
{},
);
// Sync Status
await this.syncStatus(
circulation,
transitionResult.nextState,
queryRunner,
);
await queryRunner.commitTransaction();
return {
instanceId: instance.id,
currentState: transitionResult.nextState,
};
} catch (error) {
await queryRunner.rollbackTransaction();
this.logger.error(`Failed to start circulation: ${error}`);
throw error;
} finally {
await queryRunner.release();
}
}
/**
* รับทราบ/ดำเนินการในใบเวียน (Acknowledge / Action)
*/
async processAction(
instanceId: string,
userId: number,
dto: WorkflowTransitionDto,
) {
// ส่งให้ Engine
const result = await this.workflowEngine.processTransition(
instanceId,
dto.action,
userId,
dto.comment,
dto.payload,
);
// Sync Status กลับ
const instance = await this.workflowEngine.getInstanceById(instanceId);
if (instance && instance.entityType === 'circulation') {
const circulation = await this.circulationRepo.findOne({
where: { id: parseInt(instance.entityId) },
});
if (circulation) {
await this.syncStatus(circulation, result.nextState);
}
}
return result;
}
/**
* Helper: Map Workflow State -> Circulation Status (OPEN, IN_REVIEW, COMPLETED)
*/
private async syncStatus(
circulation: Circulation,
workflowState: string,
queryRunner?: any,
) {
const statusMap: Record<string, string> = {
DRAFT: 'OPEN',
ROUTING: 'IN_REVIEW',
COMPLETED: 'COMPLETED',
CANCELLED: 'CANCELLED',
};
const targetCode = statusMap[workflowState] || 'IN_REVIEW';
// เนื่องจาก circulation เก็บ status_code เป็น String ในตารางเลย (ตาม Schema v1.4.4)
// หรืออาจเป็น Relation ID ขึ้นอยู่กับ Implementation จริง
// สมมติว่าเป็น String Code ตาม Schema:
circulation.statusCode = targetCode;
// ถ้าจบแล้ว ให้ลงเวลาปิด
if (targetCode === 'COMPLETED') {
circulation.closedAt = new Date();
}
const manager = queryRunner
? queryRunner.manager
: this.circulationRepo.manager;
await manager.save(circulation);
this.logger.log(
`Synced Circulation #${circulation.id}: State=${workflowState} -> Status=${targetCode}`,
);
}
}
@@ -1,13 +1,15 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Circulation } from './entities/circulation.entity';
import { CirculationRouting } from './entities/circulation-routing.entity';
import { CirculationStatusCode } from './entities/circulation-status-code.entity';
import { Circulation } from './entities/circulation.entity';
import { CirculationService } from './circulation.service';
import { CirculationController } from './circulation.controller';
import { UserModule } from '../user/user.module';
import { WorkflowEngineModule } from '../workflow-engine/workflow-engine.module';
import { CirculationWorkflowService } from './circulation-workflow.service';
import { CirculationController } from './circulation.controller';
import { CirculationService } from './circulation.service';
@Module({
imports: [
@@ -17,9 +19,10 @@ import { UserModule } from '../user/user.module';
CirculationStatusCode,
]),
UserModule,
WorkflowEngineModule,
],
controllers: [CirculationController],
providers: [CirculationService],
providers: [CirculationService, CirculationWorkflowService],
exports: [CirculationService],
})
export class CirculationModule {}