cdhแก้คิวรี่ให้เร็วขึ้น สร้าง workflow

This commit is contained in:
mamoss 2025-10-09 23:24:04 +07:00
parent fd17f366b8
commit 847d9c302f
2 changed files with 241 additions and 265 deletions

View file

@ -55,29 +55,35 @@ export class WorkflowController extends Controller {
fullName?: string | null;
},
) {
// ขั้นที่ 1: ทำการค้นหา profile และ metaWorkflow แบบ parallel
const [userProfileOfficer, userProfileEmployee, metaWorkflow] = await Promise.all([
this.profileRepo.findOne({
where: { keycloak: req.user.sub },
select: ["id", "keycloak"],
}),
this.profileEmployeeRepo.findOne({
where: { keycloak: req.user.sub },
select: ["id", "keycloak"],
}),
this.metaWorkflowRepo.findOne({
where: {
sysName: body.sysName,
posLevelName: body.posLevelName,
posTypeName: body.posTypeName,
},
}),
]);
// กำหนด profile type และ profile
let profileType = "OFFICER";
let profile: any = await this.profileRepo.findOne({
where: {
keycloak: req.user.sub,
},
});
let profile: any = userProfileOfficer;
if (!profile) {
profileType = "EMPLOYEE";
profile = await this.profileEmployeeRepo.findOne({
where: {
keycloak: req.user.sub,
},
});
profile = userProfileEmployee;
if (!profile) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลผู้ใช้งาน");
}
const metaWorkflow = await this.metaWorkflowRepo.findOne({
where: {
sysName: body.sysName,
posLevelName: body.posLevelName,
posTypeName: body.posTypeName,
},
});
if (!metaWorkflow) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบกระบวนการนี้ได้");
const meta = {
@ -88,6 +94,8 @@ export class WorkflowController extends Controller {
createdAt: new Date(),
lastUpdatedAt: new Date(),
};
// ขั้นที่ 2: สร้าง workflow และดึง metaState แบบ parallel
const workflow = new Workflow();
Object.assign(workflow, {
...metaWorkflow,
@ -97,117 +105,130 @@ export class WorkflowController extends Controller {
profileType: profileType,
system: body.sysName,
});
await this.workflowRepo.save(workflow);
const metaState = await this.metaStateRepo.find({
where: {
metaWorkflowId: metaWorkflow.id,
},
order: { order: "ASC" },
const [savedWorkflow, metaStates] = await Promise.all([
this.workflowRepo.save(workflow),
this.metaStateRepo.find({
where: { metaWorkflowId: metaWorkflow.id },
order: { order: "ASC" },
}),
]);
// ขั้นที่ 3: สร้าง states ทั้งหมดในครั้งเดียว
const statesToCreate = metaStates.map((item) => {
const state = new State();
Object.assign(state, { ...item, id: undefined, workflowId: savedWorkflow.id, ...meta });
return state;
});
await Promise.all(
metaState.map(async (item) => {
const state = new State();
Object.assign(state, { ...item, id: undefined, workflowId: workflow.id, ...meta });
await this.stateRepo.save(state);
if (state.order == 1) {
workflow.stateId = state.id;
await this.workflowRepo.save(workflow);
}
const metaStateOperator = await this.metaStateOperatorRepo.find({
where: {
metaStateId: item.id,
},
const savedStates = await this.stateRepo.save(statesToCreate);
// ขั้นที่ 4: อัปเดต workflow.stateId กับ state แรก
const firstState = savedStates.find((state) => state.order === 1);
if (firstState) {
savedWorkflow.stateId = firstState.id;
await this.workflowRepo.save(savedWorkflow);
}
// ขั้นที่ 5: ดึง metaStateOperators ทั้งหมดและสร้าง stateOperators
const metaStateIds = metaStates.map((item) => item.id);
const allMetaStateOperators = await this.metaStateOperatorRepo.find({
where: { metaStateId: In(metaStateIds) },
});
// สร้าง stateOperators ทั้งหมดในครั้งเดียว
const stateOperatorsToCreate: StateOperator[] = [];
allMetaStateOperators.forEach((metaStateOp) => {
const correspondingState = savedStates.find(
(state) =>
metaStates.find((metaState) => metaState.id === metaStateOp.metaStateId)?.order ===
state.order,
);
if (correspondingState) {
const stateOperator = new StateOperator();
Object.assign(stateOperator, {
...metaStateOp,
id: undefined,
stateId: correspondingState.id,
...meta,
});
await Promise.all(
metaStateOperator.map(async (item1) => {
const stateOperator = new StateOperator();
Object.assign(stateOperator, { ...item1, id: undefined, stateId: state.id, ...meta });
await this.stateOperatorRepo.save(stateOperator);
}),
);
}),
);
let num = 1;
const stateOperatorUser = new StateOperatorUser();
stateOperatorsToCreate.push(stateOperator);
}
});
await this.stateOperatorRepo.save(stateOperatorsToCreate);
// ขั้นที่ 6: สร้าง StateOperatorUsers แบบ bulk
const stateOperatorUsersToCreate: StateOperatorUser[] = [];
let orderNum = 1;
// เพิ่ม Owner ก่อน
if (profile) {
Object.assign(stateOperatorUser, {
profileId: profileType == "OFFICER" ? profile.id : null,
profileEmployeeId: profileType != "OFFICER" ? profile.id : null,
const ownerStateOperatorUser = new StateOperatorUser();
Object.assign(ownerStateOperatorUser, {
profileId: profileType === "OFFICER" ? profile.id : null,
profileEmployeeId: profileType !== "OFFICER" ? profile.id : null,
profileType: profileType,
operator: "Owner",
order: num,
workflowId: workflow.id,
order: orderNum,
workflowId: savedWorkflow.id,
...meta,
});
stateOperatorUsersToCreate.push(ownerStateOperatorUser);
}
await this.stateOperatorUserRepo.save(stateOperatorUser);
const profileOfficer = await this.posMasterRepo.find({
// ดึงข้อมูล profileOfficers และสร้าง StateOperatorUsers
const profileOfficers = await this.posMasterRepo.find({
where: {
posMasterAssigns: { assignId: body.sysName },
orgRevision: { orgRevisionIsDraft: false, orgRevisionIsCurrent: true },
current_holderId: Not(IsNull()), // เพิ่มเงื่อนไขนี้เพื่อกรองเฉพาะที่มี current_holder
},
relations: ["orgChild1"],
select: ["current_holderId", "orgChild1"], // เลือกเฉพาะ field ที่จำเป็น
});
await Promise.all(
profileOfficer.map(async (item, i) => {
if (item.current_holderId) {
num = num + 1;
if (item.orgChild1 == null || item.orgChild1.isOfficer == false) {
const stateOperatorUser = new StateOperatorUser();
Object.assign(stateOperatorUser, {
profileId: item.current_holderId,
operator: "Officer",
profileType: "OFFICER",
order: num,
workflowId: workflow.id,
...meta,
});
await this.stateOperatorUserRepo.save(stateOperatorUser);
} else {
const stateOperatorUser = new StateOperatorUser();
Object.assign(stateOperatorUser, {
profileId: item.current_holderId,
operator: "PersonnelOfficer",
profileType: "OFFICER",
order: num,
workflowId: workflow.id,
...meta,
});
await this.stateOperatorUserRepo.save(stateOperatorUser);
}
}
}),
// สร้าง StateOperatorUsers สำหรับ officers
profileOfficers.forEach((item) => {
if (item.current_holderId) {
orderNum += 1;
const isPersonnelOfficer = item.orgChild1?.isOfficer === true;
const officerStateOperatorUser = new StateOperatorUser();
Object.assign(officerStateOperatorUser, {
profileId: item.current_holderId,
operator: isPersonnelOfficer ? "PersonnelOfficer" : "Officer",
profileType: "OFFICER",
order: orderNum,
workflowId: savedWorkflow.id,
...meta,
});
stateOperatorUsersToCreate.push(officerStateOperatorUser);
}
});
// บันทึก StateOperatorUsers ทั้งหมดในครั้งเดียว
await this.stateOperatorUserRepo.save(stateOperatorUsersToCreate);
// ขั้นที่ 7: ส่ง notification (ใช้ข้อมูลที่มีอยู่แล้วแทนการ query ใหม่)
const firstStateOperators = stateOperatorsToCreate.filter((so) =>
savedStates.find((state) => state.id === so.stateId && state.order === 1),
);
const _workflow = await this.workflowRepo.findOne({
where: { id: workflow.id },
relations: ["stateOperatorUsers"],
});
if (!_workflow) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่สามารถดำเนินการกระบวนการนี้ได้");
const _state = await this.stateRepo.findOne({
where: {
id: _workflow.stateId,
},
relations: ["stateOperators"],
});
if (!_state) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลขั้นตอนการอนุมัติ");
let profileNow = _workflow.stateOperatorUsers
.filter((x) => _state.stateOperators.map((s) => s.operator).includes(x.operator))
.map((x) => ({
receiverUserId: x.profileType == "OFFICER" ? x.profileId : x.profileEmployeeId,
const notificationReceivers = stateOperatorUsersToCreate
.filter((user) => firstStateOperators.some((op) => op.operator === user.operator))
.map((user) => ({
receiverUserId: user.profileType === "OFFICER" ? user.profileId : user.profileEmployeeId,
notiLink: "",
}));
await new CallAPI()
// ส่ง notification แบบ fire-and-forget
new CallAPI()
.PostData(req, "/placement/noti/profiles", {
// subject: `รายการถูกส่ง`,
// body: `รายการถูกส่ง`,
subject: `แจ้ง${workflow.name}ของ ${body.fullName}`,
body: `แจ้ง${workflow.name}ของ ${body.fullName}`,
receiverUserIds: profileNow,
payload: "", //แนบไฟล์
subject: `แจ้ง${savedWorkflow.name}ของ ${body.fullName}`,
body: `แจ้ง${savedWorkflow.name}ของ ${body.fullName}`,
receiverUserIds: notificationReceivers,
payload: "",
isSendMail: true,
isSendInbox: true,
isSendNotification: true,
@ -809,8 +830,8 @@ export class WorkflowController extends Controller {
pageSize: number;
keycloakId?: string | null;
type?: string | null;
sortBy?: string | null,
descending?: boolean,
sortBy?: string | null;
descending?: boolean;
},
) {
const userKeycloak = body.keycloakId ?? request.user.sub;
@ -990,10 +1011,10 @@ export class WorkflowController extends Controller {
if (body.sortBy) {
queryBuilder = queryBuilder.orderBy(
`entity.${body.sortBy}`,
body.descending ? "DESC" : "ASC"
body.descending ? "DESC" : "ASC",
);
}
// 7. Execute พร้อมกัน - ใช้ Promise.all
const [data, total] = await Promise.all([
queryBuilder