diff --git a/src/controllers/WorkflowController.ts b/src/controllers/WorkflowController.ts index ea55ecf2..06b2059e 100644 --- a/src/controllers/WorkflowController.ts +++ b/src/controllers/WorkflowController.ts @@ -811,299 +811,200 @@ export class WorkflowController extends Controller { type?: string | null; }, ) { - let posMasterUser = null; - if (body.keycloakId) { - if (body.type == "employee") { - posMasterUser = await this.posMasterEmpRepo.findOne({ - where: { - orgRevision: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, - current_holder: { keycloak: body.keycloakId }, - }, - relations: ["current_holder", "current_holder.posType", "current_holder.posLevel"], - }); - } else { - posMasterUser = await this.posMasterRepo.findOne({ - where: { - orgRevision: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, - current_holder: { keycloak: body.keycloakId }, - }, - relations: ["current_holder", "current_holder.posType", "current_holder.posLevel"], - }); - } + const userKeycloak = body.keycloakId ?? request.user.sub; + + // 1. Cache user lookup - ใช้ select เฉพาะ field ที่จำเป็น + const userSelectFields = { + id: true, + orgRootId: true, + orgChild1Id: true, + orgChild2Id: true, + orgChild3Id: true, + orgChild4Id: true, + orgRevisionId: true, + current_holder: { + id: true, + posType: { id: true, posTypeName: true }, + posLevel: { id: true, posLevelName: true }, + }, + }; + + let posMasterUser: any = null; + + if (body.type === "employee") { + posMasterUser = await this.posMasterEmpRepo.findOne({ + where: { + orgRevision: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, + current_holder: { keycloak: userKeycloak }, + }, + select: userSelectFields, + relations: ["current_holder", "current_holder.posType", "current_holder.posLevel"], + }); } else { - if (body.type == "employee") { - posMasterUser = await this.posMasterEmpRepo.findOne({ - where: { - orgRevision: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, - current_holder: { keycloak: request.user.sub }, - }, - relations: ["current_holder", "current_holder.posType", "current_holder.posLevel"], - }); - } else { - posMasterUser = await this.posMasterRepo.findOne({ - where: { - orgRevision: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, - current_holder: { keycloak: request.user.sub }, - }, - relations: ["current_holder", "current_holder.posType", "current_holder.posLevel"], - }); - } + posMasterUser = await this.posMasterRepo.findOne({ + where: { + orgRevision: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, + current_holder: { keycloak: userKeycloak }, + }, + select: userSelectFields, + relations: ["current_holder", "current_holder.posType", "current_holder.posLevel"], + }); } - if (!posMasterUser || !posMasterUser.orgRootId) { + if (!posMasterUser?.orgRootId) { throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบตำแหน่งผู้ใช้งาน"); } - let condition: any = []; + // 2. Pre-calculate conditions - ย้ายออกมาข้างนอก + const posType = posMasterUser.current_holder?.posType?.posTypeName; + const posLevel = posMasterUser.current_holder?.posLevel?.posLevelName; - let conditionOfficer: any = { + const isLowLevel = + (posType === "ทั่วไป" && ["ชำนาญงาน", "ปฏิบัติงาน"].includes(posLevel)) || + (posType === "วิชาการ" && ["ปฏิบัติการ", "ชำนาญการ"].includes(posLevel)); + + const isMidLevel = + (posType === "ทั่วไป" && posLevel === "อาวุโส") || + (posType === "วิชาการ" && posLevel === "ชำนาญการพิเศษ") || + (posType === "อำนวยการ" && posLevel === "ต้น"); + + // 3. สร้าง conditions แบบ optimized + const baseCondition = { isDirector: true, - isOfficer: true, orgRevisionId: posMasterUser.orgRevisionId, + }; + + const conditionOfficer = { + ...baseCondition, + isOfficer: true, orgChild1Id: IsNull(), }; - if (type.trim().toUpperCase() == "OPERATE" || body.type == "employee") { - condition.push({ - isDirector: true, - orgRootId: posMasterUser.orgRootId, - orgRevisionId: posMasterUser.orgRevisionId, - orgChild1Id: IsNull(), - }); - condition.push({ - isDirector: true, - orgRootId: posMasterUser.orgRootId, - orgRevisionId: posMasterUser.orgRevisionId, - orgChild1Id: posMasterUser.orgChild1Id, - orgChild2Id: IsNull(), - }); - condition.push({ - isDirector: true, - orgRootId: posMasterUser.orgRootId, - orgRevisionId: posMasterUser.orgRevisionId, - orgChild1Id: posMasterUser.orgChild1Id, - orgChild2Id: posMasterUser.orgChild2Id, - orgChild3Id: IsNull(), - }); - condition.push({ - isDirector: true, - orgRootId: posMasterUser.orgRootId, - orgRevisionId: posMasterUser.orgRevisionId, - orgChild1Id: posMasterUser.orgChild1Id, - orgChild2Id: posMasterUser.orgChild2Id, - orgChild3Id: posMasterUser.orgChild3Id, - orgChild4Id: IsNull(), - }); - condition.push({ - isDirector: true, - orgRootId: posMasterUser.orgRootId, - orgRevisionId: posMasterUser.orgRevisionId, - orgChild1Id: posMasterUser.orgChild1Id, - orgChild2Id: posMasterUser.orgChild2Id, - orgChild3Id: posMasterUser.orgChild3Id, - orgChild4Id: posMasterUser.orgChild4Id, - }); - } else { - condition = [ + let mainConditions: any[] = []; + + if (type.trim().toUpperCase() === "OPERATE" || body.type === "employee") { + mainConditions = [ + { ...baseCondition, orgRootId: posMasterUser.orgRootId, orgChild1Id: IsNull() }, { - isDirector: true, + ...baseCondition, orgRootId: posMasterUser.orgRootId, - orgRevisionId: posMasterUser.orgRevisionId, + orgChild1Id: posMasterUser.orgChild1Id, + orgChild2Id: IsNull(), + }, + { + ...baseCondition, + orgRootId: posMasterUser.orgRootId, + orgChild1Id: posMasterUser.orgChild1Id, + orgChild2Id: posMasterUser.orgChild2Id, + orgChild3Id: IsNull(), + }, + { + ...baseCondition, + orgRootId: posMasterUser.orgRootId, + orgChild1Id: posMasterUser.orgChild1Id, + orgChild2Id: posMasterUser.orgChild2Id, + orgChild3Id: posMasterUser.orgChild3Id, + orgChild4Id: IsNull(), + }, + { + ...baseCondition, + orgRootId: posMasterUser.orgRootId, + orgChild1Id: posMasterUser.orgChild1Id, + orgChild2Id: posMasterUser.orgChild2Id, + orgChild3Id: posMasterUser.orgChild3Id, + orgChild4Id: posMasterUser.orgChild4Id, }, ]; - if ( - (posMasterUser.current_holder.posType.posTypeName == "ทั่วไป" && - posMasterUser.current_holder.posLevel.posLevelName == "ชำนาญงาน") || - (posMasterUser.current_holder.posType.posTypeName == "ทั่วไป" && - posMasterUser.current_holder.posLevel.posLevelName == "ปฏิบัติงาน") || - (posMasterUser.current_holder.posType.posTypeName == "วิชาการ" && - posMasterUser.current_holder.posLevel.posLevelName == "ปฏิบัติการ") || - (posMasterUser.current_holder.posType.posTypeName == "วิชาการ" && - posMasterUser.current_holder.posLevel.posLevelName == "ชำนาญการ") - ) { - condition = { - isDirector: true, + } else if (isLowLevel) { + mainConditions = [ + { + ...baseCondition, orgRootId: posMasterUser.orgRootId, - orgRevisionId: posMasterUser.orgRevisionId, orgChild1Id: IsNull(), orgChild2Id: IsNull(), orgChild3Id: IsNull(), orgChild4Id: IsNull(), - }; - } else if ( - (posMasterUser.current_holder.posType.posTypeName == "ทั่วไป" && - posMasterUser.current_holder.posLevel.posLevelName == "อาวุโส") || - (posMasterUser.current_holder.posType.posTypeName == "วิชาการ" && - posMasterUser.current_holder.posLevel.posLevelName == "ชำนาญการพิเศษ") || - (posMasterUser.current_holder.posType.posTypeName == "อำนวยการ" && - posMasterUser.current_holder.posLevel.posLevelName == "ต้น") - ) { - condition = { - isDirector: true, + }, + ]; + } else if (isMidLevel) { + mainConditions = [ + { + ...baseCondition, isDeputy: true, - orgRevisionId: posMasterUser.orgRevisionId, orgChild1Id: IsNull(), orgChild2Id: IsNull(), orgChild3Id: IsNull(), orgChild4Id: IsNull(), - }; - } else { - } + }, + ]; + } else { + mainConditions = [{ ...baseCondition, orgRootId: posMasterUser.orgRootId }]; } - if (body.isAct == true) { - const [lists, total] = await AppDataSource.getRepository(viewDirectorActing) - .createQueryBuilder("viewDirectorActing") - .andWhere( - new Brackets((qb) => { - qb.orWhere(condition).orWhere(conditionOfficer); - }), - ) - .andWhere( - new Brackets((qb) => { - qb.orWhere( - body.keyword != null && body.keyword != "" - ? "CONCAT(viewDirectorActing.prefix,viewDirectorActing.firstName,' ',viewDirectorActing.lastName) LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ) - .orWhere( - body.keyword != null && body.keyword != "" - ? "viewDirectorActing.citizenId LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ) - .orWhere( - body.keyword != null && body.keyword != "" - ? "viewDirectorActing.position LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ) - .orWhere( - body.keyword != null && body.keyword != "" - ? "viewDirectorActing.posLevel LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ) - .orWhere( - body.keyword != null && body.keyword != "" - ? "viewDirectorActing.posType LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ) - .orWhere( - body.keyword != null && body.keyword != "" - ? "viewDirectorActing.actFullName LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ) - .orWhere( - body.keyword != null && body.keyword != "" - ? "viewDirectorActing.posNo LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ) - .orWhere( - body.keyword != null && body.keyword != "" - ? "viewDirectorActing.posExecutiveName LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ); - }), - ) - .skip((body.page - 1) * body.pageSize) - .take(body.pageSize) - .getManyAndCount(); - return new HttpSuccess({ data: lists, total }); - } else { - const [lists, total] = await AppDataSource.getRepository(viewDirector) - .createQueryBuilder("viewDirector") - .andWhere( - new Brackets((qb) => { - qb.orWhere(condition).orWhere(conditionOfficer); - }), - ) - .andWhere( - new Brackets((qb) => { - qb.orWhere( - body.keyword != null && body.keyword != "" - ? "CONCAT(viewDirector.prefix,viewDirector.firstName,' ',viewDirector.lastName) LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ) - .orWhere( - body.keyword != null && body.keyword != "" - ? "viewDirector.citizenId LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ) - .orWhere( - body.keyword != null && body.keyword != "" - ? "viewDirector.position LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ) - .orWhere( - body.keyword != null && body.keyword != "" - ? "viewDirector.posLevel LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ) - .orWhere( - body.keyword != null && body.keyword != "" - ? "viewDirector.posType LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ) - .orWhere( - body.keyword != null && body.keyword != "" - ? "viewDirector.posNo LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ) - .orWhere( - body.keyword != null && body.keyword != "" - ? "viewDirector.posExecutiveName LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ); - }), - ) - .skip((body.page - 1) * body.pageSize) - .take(body.pageSize) - .getManyAndCount(); - return new HttpSuccess({ data: lists, total }); + // 4. สร้าง optimized query builder + const repository = body.isAct + ? AppDataSource.getRepository(viewDirectorActing) + : AppDataSource.getRepository(viewDirector); + + const queryBuilder = repository.createQueryBuilder("entity"); + + // 5. แยก WHERE conditions ให้เร็วขึ้น + queryBuilder.where( + new Brackets((qb) => { + mainConditions.forEach((condition, index) => { + if (index === 0) { + qb.where(condition); + } else { + qb.orWhere(condition); + } + }); + qb.orWhere(conditionOfficer); + }), + ); + + // 6. ปรับ search conditions ให้เร็วขึ้น + if (body.keyword?.trim()) { + const keyword = `%${body.keyword.trim()}%`; + const searchFields = [ + "CONCAT(entity.prefix, entity.firstName, ' ', entity.lastName)", + "entity.citizenId", + "entity.position", + "entity.posLevel", + "entity.posType", + "entity.posNo", + "entity.posExecutiveName", + ]; + + if (body.isAct) { + searchFields.push("entity.actFullName"); + } + + queryBuilder.andWhere( + `(${searchFields.map((field) => `${field} LIKE :keyword`).join(" OR ")})`, + { keyword }, + ); } + + // 7. Execute พร้อมกัน - ใช้ Promise.all + const [data, total] = await Promise.all([ + queryBuilder + .skip((body.page - 1) * body.pageSize) + .take(body.pageSize) + .getMany(), + queryBuilder.getCount(), + ]); + + // 8. ปรับ response mapping (ถ้าจำเป็น) + const processedData = body.isAct + ? data + : data.map((x: any) => ({ + ...x, + posExecutiveNameOrg: + x.posExecutiveName + + (x.orgChild4 ?? x.orgChild3 ?? x.orgChild2 ?? x.orgChild1 ?? x.orgRoot ?? ""), + })); + + return new HttpSuccess({ data: processedData, total }); } /** * @@ -1122,290 +1023,185 @@ export class WorkflowController extends Controller { keycloakId?: string | null; }, ) { - let posMasterUser: PosMaster = new PosMaster(); - if (body.keycloakId) { - posMasterUser = (await this.posMasterRepo.findOne({ - where: { - orgRevision: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, - current_holder: { keycloak: body.keycloakId }, - }, - relations: ["current_holder", "current_holder.posType", "current_holder.posLevel"], - })) as PosMaster; - } else { - posMasterUser = (await this.posMasterRepo.findOne({ - where: { - orgRevision: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, - current_holder: { keycloak: request.user.sub }, - }, - relations: ["current_holder", "current_holder.posType", "current_holder.posLevel"], - })) as PosMaster; + const userKeycloak = body.keycloakId ?? request.user.sub; + + // 1. ใช้ select เฉพาะ field ที่จำเป็น - เหมือน getProfilePlacement + const userSelectFields = { + id: true, + orgRootId: true, + orgChild1Id: true, + orgChild2Id: true, + orgChild3Id: true, + orgChild4Id: true, + orgRevisionId: true, + current_holder: { + id: true, + posType: { id: true, posTypeName: true }, + posLevel: { id: true, posLevelName: true }, + }, + }; + + const posMasterUser = await this.posMasterRepo.findOne({ + where: { + orgRevision: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, + current_holder: { keycloak: userKeycloak }, + }, + select: userSelectFields, + relations: ["current_holder", "current_holder.posType", "current_holder.posLevel"], + }); + + if (!posMasterUser?.orgRootId) { + throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบตำแหน่งผู้ใช้งาน"); } - if (!posMasterUser || !posMasterUser.orgRootId) - throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบตำแหน่งผู้ใช้งาน"); + // 2. Pre-calculate conditions - ปรับให้เหมือน getProfilePlacement + const posType = posMasterUser.current_holder?.posType?.posTypeName; + const posLevel = posMasterUser.current_holder?.posLevel?.posLevelName; - let condition: any = []; + const isLowLevel = + (posType === "ทั่วไป" && ["ชำนาญงาน", "ปฏิบัติงาน"].includes(posLevel)) || + (posType === "วิชาการ" && ["ปฏิบัติการ", "ชำนาญการ"].includes(posLevel)); - let conditionOfficer: any = { + const isMidLevel = + (posType === "ทั่วไป" && posLevel === "อาวุโส") || + (posType === "วิชาการ" && posLevel === "ชำนาญการพิเศษ") || + (posType === "อำนวยการ" && posLevel === "ต้น"); + + // 3. สร้าง conditions แบบ optimized + const baseCondition = { isDirector: true, - isOfficer: true, orgRevisionId: posMasterUser.orgRevisionId, + }; + + const conditionOfficer = { + ...baseCondition, + isOfficer: true, orgChild1Id: IsNull(), }; - if (type.trim().toUpperCase() == "OPERATE") { - condition.push({ - isDirector: true, - orgRootId: posMasterUser.orgRootId, - orgRevisionId: posMasterUser.orgRevisionId, - orgChild1Id: IsNull(), - }); - condition.push({ - isDirector: true, - orgRootId: posMasterUser.orgRootId, - orgRevisionId: posMasterUser.orgRevisionId, - orgChild1Id: posMasterUser.orgChild1Id, - orgChild2Id: IsNull(), - }); - condition.push({ - isDirector: true, - orgRootId: posMasterUser.orgRootId, - orgRevisionId: posMasterUser.orgRevisionId, - orgChild1Id: posMasterUser.orgChild1Id, - orgChild2Id: posMasterUser.orgChild2Id, - orgChild3Id: IsNull(), - }); - condition.push({ - isDirector: true, - orgRootId: posMasterUser.orgRootId, - orgRevisionId: posMasterUser.orgRevisionId, - orgChild1Id: posMasterUser.orgChild1Id, - orgChild2Id: posMasterUser.orgChild2Id, - orgChild3Id: posMasterUser.orgChild3Id, - orgChild4Id: IsNull(), - }); - condition.push({ - isDirector: true, - orgRootId: posMasterUser.orgRootId, - orgRevisionId: posMasterUser.orgRevisionId, - orgChild1Id: posMasterUser.orgChild1Id, - orgChild2Id: posMasterUser.orgChild2Id, - orgChild3Id: posMasterUser.orgChild3Id, - orgChild4Id: posMasterUser.orgChild4Id, - }); - } else { - condition = [ + let mainConditions: any[] = []; + + if (type.trim().toUpperCase() === "OPERATE") { + mainConditions = [ + { ...baseCondition, orgRootId: posMasterUser.orgRootId, orgChild1Id: IsNull() }, { - isDirector: true, + ...baseCondition, orgRootId: posMasterUser.orgRootId, - orgRevisionId: posMasterUser.orgRevisionId, + orgChild1Id: posMasterUser.orgChild1Id, + orgChild2Id: IsNull(), + }, + { + ...baseCondition, + orgRootId: posMasterUser.orgRootId, + orgChild1Id: posMasterUser.orgChild1Id, + orgChild2Id: posMasterUser.orgChild2Id, + orgChild3Id: IsNull(), + }, + { + ...baseCondition, + orgRootId: posMasterUser.orgRootId, + orgChild1Id: posMasterUser.orgChild1Id, + orgChild2Id: posMasterUser.orgChild2Id, + orgChild3Id: posMasterUser.orgChild3Id, + orgChild4Id: IsNull(), + }, + { + ...baseCondition, + orgRootId: posMasterUser.orgRootId, + orgChild1Id: posMasterUser.orgChild1Id, + orgChild2Id: posMasterUser.orgChild2Id, + orgChild3Id: posMasterUser.orgChild3Id, + orgChild4Id: posMasterUser.orgChild4Id, }, ]; - if ( - (posMasterUser.current_holder.posType.posTypeName == "ทั่วไป" && - posMasterUser.current_holder.posLevel.posLevelName == "ชำนาญงาน") || - (posMasterUser.current_holder.posType.posTypeName == "ทั่วไป" && - posMasterUser.current_holder.posLevel.posLevelName == "ปฏิบัติงาน") || - (posMasterUser.current_holder.posType.posTypeName == "วิชาการ" && - posMasterUser.current_holder.posLevel.posLevelName == "ปฏิบัติการ") || - (posMasterUser.current_holder.posType.posTypeName == "วิชาการ" && - posMasterUser.current_holder.posLevel.posLevelName == "ชำนาญการ") - ) { - condition = { - isDirector: true, + } else if (isLowLevel) { + mainConditions = [ + { + ...baseCondition, orgRootId: posMasterUser.orgRootId, - orgRevisionId: posMasterUser.orgRevisionId, orgChild1Id: IsNull(), orgChild2Id: IsNull(), orgChild3Id: IsNull(), orgChild4Id: IsNull(), - }; - } else if ( - (posMasterUser.current_holder.posType.posTypeName == "ทั่วไป" && - posMasterUser.current_holder.posLevel.posLevelName == "อาวุโส") || - (posMasterUser.current_holder.posType.posTypeName == "วิชาการ" && - posMasterUser.current_holder.posLevel.posLevelName == "ชำนาญการพิเศษ") || - (posMasterUser.current_holder.posType.posTypeName == "อำนวยการ" && - posMasterUser.current_holder.posLevel.posLevelName == "ต้น") - ) { - condition = { - isDirector: true, + }, + ]; + } else if (isMidLevel) { + mainConditions = [ + { + ...baseCondition, isDeputy: true, - orgRevisionId: posMasterUser.orgRevisionId, orgChild1Id: IsNull(), orgChild2Id: IsNull(), orgChild3Id: IsNull(), orgChild4Id: IsNull(), - }; - } else { - } + }, + ]; + } else { + mainConditions = [{ ...baseCondition, orgRootId: posMasterUser.orgRootId }]; } - if (body.isAct == true) { - const [lists, total] = await AppDataSource.getRepository(viewDirectorActing) - .createQueryBuilder("viewDirectorActing") - .andWhere( - new Brackets((qb) => { - qb.orWhere(condition).orWhere(conditionOfficer); - }), - ) - .andWhere( - new Brackets((qb) => { - qb.orWhere( - body.keyword != null && body.keyword != "" - ? "CONCAT(viewDirectorActing.prefix,viewDirectorActing.firstName,' ',viewDirectorActing.lastName) LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ) - .orWhere( - body.keyword != null && body.keyword != "" - ? "viewDirectorActing.citizenId LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ) - .orWhere( - body.keyword != null && body.keyword != "" - ? "viewDirectorActing.position LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ) - .orWhere( - body.keyword != null && body.keyword != "" - ? "viewDirectorActing.posLevel LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ) - .orWhere( - body.keyword != null && body.keyword != "" - ? "viewDirectorActing.posType LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ) - .orWhere( - body.keyword != null && body.keyword != "" - ? "viewDirectorActing.actFullName LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ) - .orWhere( - body.keyword != null && body.keyword != "" - ? "viewDirectorActing.posNo LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ) - .orWhere( - body.keyword != null && body.keyword != "" - ? "viewDirectorActing.posExecutiveName LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ); - }), - ) - .skip((body.page - 1) * body.pageSize) - .take(body.pageSize) - .getManyAndCount(); - const data = lists.map((x) => ({ - ...x, - posExecutiveNameOrg: - x.posExecutiveName + - (x.orgChild4 ?? x.orgChild3 ?? x.orgChild2 ?? x.orgChild1 ?? x.orgRoot ?? ""), - })); - return new HttpSuccess({ data: data, total }); - } else { - const [lists, total] = await AppDataSource.getRepository(viewDirector) - .createQueryBuilder("viewDirector") - .andWhere( - new Brackets((qb) => { - qb.orWhere(condition).orWhere(conditionOfficer); - }), - ) - .andWhere( - new Brackets((qb) => { - qb.orWhere( - body.keyword != null && body.keyword != "" - ? "CONCAT(viewDirector.prefix,viewDirector.firstName,' ',viewDirector.lastName) LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ) - .orWhere( - body.keyword != null && body.keyword != "" - ? "viewDirector.citizenId LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ) - .orWhere( - body.keyword != null && body.keyword != "" - ? "viewDirector.position LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ) - .orWhere( - body.keyword != null && body.keyword != "" - ? "viewDirector.posLevel LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ) - .orWhere( - body.keyword != null && body.keyword != "" - ? "viewDirector.posType LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ) - .orWhere( - body.keyword != null && body.keyword != "" - ? "viewDirector.posNo LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ) - .orWhere( - body.keyword != null && body.keyword != "" - ? "viewDirector.posExecutiveName LIKE :keyword" - : "1=1", - { - keyword: `%${body.keyword}%`, - }, - ); - }), - ) - .skip((body.page - 1) * body.pageSize) - .take(body.pageSize) - .getManyAndCount(); - const data = lists.map((x) => ({ - ...x, - posExecutiveNameOrg: - x.posExecutiveName + - (x.orgChild4 ?? x.orgChild3 ?? x.orgChild2 ?? x.orgChild1 ?? x.orgRoot ?? ""), - })); - return new HttpSuccess({ data: data, total }); + // 4. สร้าง optimized query builder + const repository = body.isAct + ? AppDataSource.getRepository(viewDirectorActing) + : AppDataSource.getRepository(viewDirector); + + const queryBuilder = repository.createQueryBuilder("entity"); + + // 5. แยก WHERE conditions ให้เร็วขึ้น + queryBuilder.where( + new Brackets((qb) => { + mainConditions.forEach((condition, index) => { + if (index === 0) { + qb.where(condition); + } else { + qb.orWhere(condition); + } + }); + qb.orWhere(conditionOfficer); + }), + ); + + // 6. ปรับ search conditions ให้เร็วขึ้น - แบบเดียวกับ getProfilePlacement + if (body.keyword?.trim()) { + const keyword = `%${body.keyword.trim()}%`; + const searchFields = [ + "CONCAT(entity.prefix, entity.firstName, ' ', entity.lastName)", + "entity.citizenId", + "entity.position", + "entity.posLevel", + "entity.posType", + "entity.posNo", + "entity.posExecutiveName", + ]; + + if (body.isAct) { + searchFields.push("entity.actFullName"); + } + + queryBuilder.andWhere( + `(${searchFields.map((field) => `${field} LIKE :keyword`).join(" OR ")})`, + { keyword }, + ); } + + // 7. Execute พร้อมกัน - ใช้ Promise.all + const [data, total] = await Promise.all([ + queryBuilder + .skip((body.page - 1) * body.pageSize) + .take(body.pageSize) + .getMany(), + queryBuilder.getCount(), + ]); + + // 8. ปรับ response mapping + const processedData = data.map((x: any) => ({ + ...x, + posExecutiveNameOrg: + x.posExecutiveName + + (x.orgChild4 ?? x.orgChild3 ?? x.orgChild2 ?? x.orgChild1 ?? x.orgRoot ?? ""), + })); + + return new HttpSuccess({ data: processedData, total }); } /**