diff --git a/src/controllers/OrganizationDotnetController.ts b/src/controllers/OrganizationDotnetController.ts index db161fc7..cd4bad67 100644 --- a/src/controllers/OrganizationDotnetController.ts +++ b/src/controllers/OrganizationDotnetController.ts @@ -73,29 +73,10 @@ export class OrganizationDotnetController extends Controller { role?: string | null; nodeId?: string | null; node?: number | null; + page: number; + pageSize: number; }, ) { - // const profileRepository = AppDataSource.getRepository(Profile); - // const queryBuilder = profileRepository - // .createQueryBuilder("profile") - // .leftJoinAndSelect("profile.posLevel", "posLevel") - // .leftJoinAndSelect("profile.posType", "posType") - // if (body.citizenId || body.firstName || body.lastName) { - // queryBuilder.where( - // new Brackets((qb) => { - // if (body.citizenId) { - // qb.orWhere("profile.citizenId LIKE :citizenId", { citizenId: `%${body.citizenId}%` }); - // } - // if (body.firstName) { - // qb.orWhere("profile.firstName LIKE :firstName", { firstName: `%${body.firstName}%` }); - // } - // if (body.lastName) { - // qb.orWhere("profile.lastName LIKE :lastName", { lastName: `%${body.lastName}%` }); - // } - // }), - // ); - // } - // const profiles = await queryBuilder.getMany(); let condition = "1=1"; let conditionParams = {}; if (body.role === "CHILD") { @@ -156,7 +137,7 @@ export class OrganizationDotnetController extends Controller { if (!findRevision) { throw new HttpError(HttpStatus.NOT_FOUND, "not found. OrgRevision"); } - const profiles = await this.profileRepo + const [profiles, total] = await this.profileRepo .createQueryBuilder("profile") .leftJoinAndSelect("profile.posLevel", "posLevel") .leftJoinAndSelect("profile.posType", "posType") @@ -170,20 +151,22 @@ export class OrganizationDotnetController extends Controller { .andWhere( new Brackets((qb) => { if (body.citizenId) { - qb.orWhere("profile.citizenId LIKE :citizenId", { citizenId: `%${body.citizenId}%` }); + qb.andWhere("profile.citizenId LIKE :citizenId", { citizenId: `%${body.citizenId}%` }); } if (body.firstName) { - qb.orWhere("profile.firstName LIKE :firstName", { firstName: `%${body.firstName}%` }); + qb.andWhere("profile.firstName LIKE :firstName", { firstName: `%${body.firstName}%` }); } if (body.lastName) { - qb.orWhere("profile.lastName LIKE :lastName", { lastName: `%${body.lastName}%` }); + qb.andWhere("profile.lastName LIKE :lastName", { lastName: `%${body.lastName}%` }); } }), ) .andWhere(condition, conditionParams) - .getMany(); + .skip((body.page - 1) * body.pageSize) + .take(body.pageSize) + .getManyAndCount(); - return new HttpSuccess(profiles); + return new HttpSuccess({ data: profiles, total: total }); } /** @@ -202,40 +185,10 @@ export class OrganizationDotnetController extends Controller { role?: string | null; nodeId?: string | null; node?: number | null; + page: number; + pageSize: number; }, ) { - // const profileRepository = AppDataSource.getRepository(ProfileEmployee); - // const queryBuilder = profileRepository - // .createQueryBuilder("profile") - // .leftJoinAndSelect("profile.posLevel", "posLevel") - // .leftJoinAndSelect("profile.posType", "posType") - // .leftJoinAndSelect("profile.profileSalary", "profileSalary") - // .leftJoinAndSelect("profile.current_holders", "current_holders") - // .leftJoinAndSelect("current_holders.orgRoot", "orgRoot") - // .leftJoinAndSelect("current_holders.orgChild1", "orgChild1") - // .leftJoinAndSelect("current_holders.orgChild2", "orgChild2") - // .leftJoinAndSelect("current_holders.orgChild3", "orgChild3") - // .leftJoinAndSelect("current_holders.orgChild4", "orgChild4") - // .orderBy("profileSalary.order", "DESC"); - - // if (body.citizenId || body.firstName || body.lastName) { - // queryBuilder.where( - // new Brackets((qb) => { - // if (body.citizenId) { - // qb.orWhere("profile.citizenId LIKE :citizenId", { citizenId: `%${body.citizenId}%` }); - // } - // if (body.firstName) { - // qb.orWhere("profile.firstName LIKE :firstName", { firstName: `%${body.firstName}%` }); - // } - // if (body.lastName) { - // qb.orWhere("profile.lastName LIKE :lastName", { lastName: `%${body.lastName}%` }); - // } - // }), - // ); - // } - - // const profileEmp = await queryBuilder.getMany(); - let condition = "1=1"; let conditionParams = {}; if (body.role === "CHILD") { @@ -296,7 +249,7 @@ export class OrganizationDotnetController extends Controller { if (!findRevision) { throw new HttpError(HttpStatus.NOT_FOUND, "not found. OrgRevision"); } - const profileEmp = await this.profileEmpRepo + const [profileEmp, total] = await this.profileEmpRepo .createQueryBuilder("profile") .leftJoinAndSelect("profile.posLevel", "posLevel") .leftJoinAndSelect("profile.posType", "posType") @@ -311,110 +264,112 @@ export class OrganizationDotnetController extends Controller { .andWhere( new Brackets((qb) => { if (body.citizenId) { - qb.orWhere("profile.citizenId LIKE :citizenId", { citizenId: `%${body.citizenId}%` }); + qb.andWhere("profile.citizenId LIKE :citizenId", { citizenId: `%${body.citizenId}%` }); } if (body.firstName) { - qb.orWhere("profile.firstName LIKE :firstName", { firstName: `%${body.firstName}%` }); + qb.andWhere("profile.firstName LIKE :firstName", { firstName: `%${body.firstName}%` }); } if (body.lastName) { - qb.orWhere("profile.lastName LIKE :lastName", { lastName: `%${body.lastName}%` }); + qb.andWhere("profile.lastName LIKE :lastName", { lastName: `%${body.lastName}%` }); } }), ) .andWhere(condition, conditionParams) .orderBy("profileSalary.order", "DESC") - .getMany(); + .skip((body.page - 1) * body.pageSize) + .take(body.pageSize) + .getManyAndCount(); - const profileEmp_ = await Promise.all( - profileEmp.map((item: ProfileEmployee) => { - const rootName = - item.current_holders.length == 0 - ? null - : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgRoot - ?.orgRootName; - const shortName = - item.current_holders.length == 0 - ? null - : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) != null && - item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgChild4 != - null - ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgChild4.orgChild4ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.posMasterNo}` - : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) != null && - item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) - ?.orgChild3 != null - ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgChild3.orgChild3ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.posMasterNo}` - : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) != null && - item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) - ?.orgChild2 != null - ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgChild2.orgChild2ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.posMasterNo}` - : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) != null && - item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) - ?.orgChild1 != null - ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgChild1.orgChild1ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.posMasterNo}` - : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) != - null && - item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) - ?.orgRoot != null - ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgRoot.orgRootShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.posMasterNo}` - : null; - return { - oc: rootName, - id: item.id, - createdAt: item.createdAt, - createdUserId: item.createdUserId, - lastUpdatedAt: item.lastUpdatedAt, - lastUpdateUserId: item.lastUpdateUserId, - createdFullName: item.createdFullName, - lastUpdateFullName: item.lastUpdateFullName, - avatar: item.avatar, - avatarName: item.avatarName, - rank: item.rank, - prefix: item.prefix, - firstName: item.firstName, - lastName: item.lastName, - citizenId: item.citizenId, - position: item.position, - posLevelId: item.posLevelId, - posTypeId: item.posTypeId, - email: item.email, - phone: item.phone, - keycloak: item.keycloak, - isProbation: item.isProbation, - isLeave: item.isLeave, - leaveReason: item.leaveReason, - dateLeave: item.dateLeave, - dateRetire: item.dateRetire, - dateAppoint: item.dateAppoint, - dateRetireLaw: item.dateRetireLaw, - dateStart: item.dateStart, - govAgeAbsent: item.govAgeAbsent, - govAgePlus: item.govAgePlus, - birthDate: item.birthDate ?? new Date(), - reasonSameDate: item.reasonSameDate, - ethnicity: item.ethnicity, - telephoneNumber: item.phone, - nationality: item.nationality, - gender: item.gender, - relationship: item.relationship, - religion: item.religion, - bloodGroup: item.bloodGroup, - registrationAddress: item.registrationAddress, - registrationProvinceId: item.registrationProvinceId, - registrationDistrictId: item.registrationDistrictId, - registrationSubDistrictId: item.registrationSubDistrictId, - registrationZipCode: item.registrationZipCode, - currentAddress: item.currentAddress, - currentProvinceId: item.currentProvinceId, - currentDistrictId: item.currentDistrictId, - currentSubDistrictId: item.currentSubDistrictId, - currentZipCode: item.currentZipCode, - posLevel: item.posLevel, - posType: item.posType, - posNo: shortName, - }; - }), - ); - return new HttpSuccess(profileEmp_); + // const profileEmp_ = await Promise.all( + // profileEmp.map((item: ProfileEmployee) => { + // const rootName = + // item.current_holders.length == 0 + // ? null + // : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgRoot + // ?.orgRootName; + // const shortName = + // item.current_holders.length == 0 + // ? null + // : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) != null && + // item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgChild4 != + // null + // ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgChild4.orgChild4ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.posMasterNo}` + // : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) != null && + // item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) + // ?.orgChild3 != null + // ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgChild3.orgChild3ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.posMasterNo}` + // : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) != null && + // item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) + // ?.orgChild2 != null + // ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgChild2.orgChild2ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.posMasterNo}` + // : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) != null && + // item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) + // ?.orgChild1 != null + // ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgChild1.orgChild1ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.posMasterNo}` + // : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) != + // null && + // item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) + // ?.orgRoot != null + // ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgRoot.orgRootShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.posMasterNo}` + // : null; + // return { + // oc: rootName, + // id: item.id, + // createdAt: item.createdAt, + // createdUserId: item.createdUserId, + // lastUpdatedAt: item.lastUpdatedAt, + // lastUpdateUserId: item.lastUpdateUserId, + // createdFullName: item.createdFullName, + // lastUpdateFullName: item.lastUpdateFullName, + // avatar: item.avatar, + // avatarName: item.avatarName, + // rank: item.rank, + // prefix: item.prefix, + // firstName: item.firstName, + // lastName: item.lastName, + // citizenId: item.citizenId, + // position: item.position, + // posLevelId: item.posLevelId, + // posTypeId: item.posTypeId, + // email: item.email, + // phone: item.phone, + // keycloak: item.keycloak, + // isProbation: item.isProbation, + // isLeave: item.isLeave, + // leaveReason: item.leaveReason, + // dateLeave: item.dateLeave, + // dateRetire: item.dateRetire, + // dateAppoint: item.dateAppoint, + // dateRetireLaw: item.dateRetireLaw, + // dateStart: item.dateStart, + // govAgeAbsent: item.govAgeAbsent, + // govAgePlus: item.govAgePlus, + // birthDate: item.birthDate ?? new Date(), + // reasonSameDate: item.reasonSameDate, + // ethnicity: item.ethnicity, + // telephoneNumber: item.phone, + // nationality: item.nationality, + // gender: item.gender, + // relationship: item.relationship, + // religion: item.religion, + // bloodGroup: item.bloodGroup, + // registrationAddress: item.registrationAddress, + // registrationProvinceId: item.registrationProvinceId, + // registrationDistrictId: item.registrationDistrictId, + // registrationSubDistrictId: item.registrationSubDistrictId, + // registrationZipCode: item.registrationZipCode, + // currentAddress: item.currentAddress, + // currentProvinceId: item.currentProvinceId, + // currentDistrictId: item.currentDistrictId, + // currentSubDistrictId: item.currentSubDistrictId, + // currentZipCode: item.currentZipCode, + // posLevel: item.posLevel, + // posType: item.posType, + // posNo: shortName, + // }; + // }), + // ); + return new HttpSuccess({ data: profileEmp, total: total }); } /** diff --git a/src/controllers/WorkflowController.ts b/src/controllers/WorkflowController.ts index 0938b267..e12cdd0a 100644 --- a/src/controllers/WorkflowController.ts +++ b/src/controllers/WorkflowController.ts @@ -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