From fe3983f62eedecb56fabbd6139c324f874bef4f6 Mon Sep 17 00:00:00 2001 From: mamoss <> Date: Fri, 8 Aug 2025 16:42:39 +0700 Subject: [PATCH 01/13] test pr --- src/controllers/CommandController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/CommandController.ts b/src/controllers/CommandController.ts index 6724eabb..aebdb65f 100644 --- a/src/controllers/CommandController.ts +++ b/src/controllers/CommandController.ts @@ -6684,7 +6684,7 @@ export class CommandController extends Controller { historyAct.profileActpositionId = dataAct.id; await this.actpositionHistoryRepository.save(historyAct); - // 9. ปิดสถานะรักษาการ + // 9. ปิดสถานะรักษาการ. const existingActPositions = await this.actpositionRepository.find({ where: { profileId: item.posMasterChild.current_holderId, From 5865ada163c1c80a6d40bc056140456cb6944c48 Mon Sep 17 00:00:00 2001 From: Bright Date: Fri, 8 Aug 2025 17:14:29 +0700 Subject: [PATCH 02/13] update #141 --- src/controllers/ApiWebServiceController.ts | 140 ++++++++++++++++----- 1 file changed, 107 insertions(+), 33 deletions(-) diff --git a/src/controllers/ApiWebServiceController.ts b/src/controllers/ApiWebServiceController.ts index be60fb37..5ac66ed4 100644 --- a/src/controllers/ApiWebServiceController.ts +++ b/src/controllers/ApiWebServiceController.ts @@ -19,6 +19,7 @@ import { ApiName } from "../entities/ApiName"; import { Profile } from "../entities/Profile"; import { isPermissionRequest } from "../middlewares/authWebService"; import { RequestWithUserWebService } from "../middlewares/user"; +import { OrgRevision } from "../entities/OrgRevision"; @Route("api/v2/org/api-service") @Tags("ApiKey") @Security("webServiceAuth") @@ -28,7 +29,7 @@ import { RequestWithUserWebService } from "../middlewares/user"; ) export class ApiWebServiceController extends Controller { private apiNameRepository = AppDataSource.getRepository(ApiName); - + private orgRevisionRepository = AppDataSource.getRepository(OrgRevision); /** * list fields by systems * @summary รายการ fields ตาม systems @@ -41,42 +42,115 @@ export class ApiWebServiceController extends Controller { @Query("page") page: number = 1, @Query("pageSize") pageSize: number = 100, ): Promise { - // try { - const apiName = await this.apiNameRepository.findOne({ - where: { code }, - select: ["id", "code", "methodApi", "system", "isActive"], - relations: ["apiAttributes"], - order: { - apiAttributes: { - ordering: "ASC", - }, + + const apiName = await this.apiNameRepository.findOne({ + where: { code }, + select: ["id", "code", "methodApi", "system", "isActive"], + relations: ["apiAttributes"], + order: { + apiAttributes: { + ordering: "ASC", }, + }, + }); + + if (!apiName || apiName.system != system || !apiName.isActive || apiName.methodApi != "GET") { + throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบ API ที่ระบุ"); + } + await isPermissionRequest(request, apiName.id); + const offset = (page - 1) * pageSize; + + const propertyKey = apiName.apiAttributes.map((attr) => `${attr.tbName}.${attr.propertyKey}`); + + let Main:string = "" + let condition: string = "1=1" + if (system == "registry") { + Main = "Profile" + } + else if (system == "registry_emp") { + Main = "ProfileEmployee" + condition = `ProfileEmployee.employeeClass = "PERM"` + } + else if (system == "registry_temp") { + Main = "ProfileEmployee" + condition = `ProfileEmployee.employeeClass = "TEMP"` + } + else { + Main = "OrgRoot" + const revision = await this.orgRevisionRepository.findOne({ + select: ["id"], + where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false } + }); + condition = `OrgRoot.orgRevisionId = '${revision?.id}'` + } + + const repo = AppDataSource.getRepository(Main); + const metadata = repo.metadata; + + const relationMap: Record = {}; + metadata.relations.forEach((rel) => { + relationMap[rel.inverseEntityMetadata.name] = rel.propertyName; + }); + + // ดึงเฉพาะตารางรอง (ถ้าเลือกไว้) + let propertyOtherKey:any[] = []; + propertyOtherKey = [ + ...new Set( + propertyKey + .map((x) => x.split(".")[0]) + .filter((tb) => tb !== Main) + ) + ] + + const queryBuilder = repo.createQueryBuilder(Main) + + // join กับตารารอง + if (propertyOtherKey.length > 0) { + propertyOtherKey.forEach((tb) => { + const relationName = relationMap[tb]; + if (relationName) { + queryBuilder.leftJoinAndSelect(`${Main}.${relationName}`, tb); + } }); + } + + const [items, total] = await queryBuilder + .where(condition) + .orderBy(`${Main}.createdAt`, "DESC") + .skip(offset) + .take(pageSize) + .getManyAndCount(); - if (!apiName || apiName.system != system || !apiName.isActive || apiName.methodApi != "GET") { - throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบ API ที่ระบุ"); - } - await isPermissionRequest(request, apiName.id); - const offset = (page - 1) * pageSize; + const results = items.map(item => { + const result: any = {}; + propertyKey.forEach(key => { + const [table, field] = key.split("."); - const propertyKey = apiName.apiAttributes.map((attr) => `${attr.tbName}.${attr.propertyKey}`); - const queryBuilder = AppDataSource.getRepository(Profile) - .createQueryBuilder("Profile") - .select(propertyKey); + if (table === Main) { + result[field] = item[field]; + } + else { + const relationName = relationMap[table]; + if (!relationName) return; + if (!Array.isArray(result[relationName])) { + result[relationName] = item[relationName] + ? item[relationName].map((relItem: any) => { + const filteredRel: any = {}; + propertyKey + .filter(k => k.startsWith(`${table}.`)) + .forEach(k => { + const [, relField] = k.split("."); + filteredRel[relField] = relItem[relField]; + }); + return filteredRel; + }) + : []; + } + } + }); + return result; + }); - const [items, total] = await queryBuilder - .skip(offset) - .take(pageSize) - .orderBy("Profile.createdAt", "DESC") - .getManyAndCount(); - - return new HttpSuccess({ items, total }); - // } catch (error) { - // throw new HttpError( - // HttpStatusCode.INTERNAL_SERVER_ERROR, - // (error instanceof Error ? error.message : String(error)) || - // "เกิดข้อผิดพลาด ไม่สามารถแสดงรายการได้ กรุณาลองใหม่ในภายหลัง", - // ); - // } + return new HttpSuccess({ items: results, total }); } } From f827449cf0db610314950addeb6afe1f82e83ca9 Mon Sep 17 00:00:00 2001 From: mamoss <> Date: Fri, 8 Aug 2025 17:53:16 +0700 Subject: [PATCH 03/13] test. comment --- src/controllers/CommandController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/CommandController.ts b/src/controllers/CommandController.ts index aebdb65f..6724eabb 100644 --- a/src/controllers/CommandController.ts +++ b/src/controllers/CommandController.ts @@ -6684,7 +6684,7 @@ export class CommandController extends Controller { historyAct.profileActpositionId = dataAct.id; await this.actpositionHistoryRepository.save(historyAct); - // 9. ปิดสถานะรักษาการ. + // 9. ปิดสถานะรักษาการ const existingActPositions = await this.actpositionRepository.find({ where: { profileId: item.posMasterChild.current_holderId, From 56525e1a0a51b4ae60add2ef37526d19c52b0811 Mon Sep 17 00:00:00 2001 From: mamoss <> Date: Sat, 9 Aug 2025 20:31:07 +0700 Subject: [PATCH 04/13] =?UTF-8?q?1733-=E0=B8=9B=E0=B8=A3=E0=B8=B0=E0=B8=A7?= =?UTF-8?q?=E0=B8=B1=E0=B8=95=E0=B8=B4=E0=B8=95=E0=B8=B3=E0=B9=81=E0=B8=AB?= =?UTF-8?q?=E0=B8=99=E0=B9=88=E0=B8=87=20=E0=B8=82=E0=B8=AD=E0=B8=87?= =?UTF-8?q?=E0=B9=82=E0=B8=84=E0=B8=A3=E0=B8=87=E0=B8=AA=E0=B8=A3=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=E0=B8=87=E0=B8=9B=E0=B8=B1=E0=B8=88=E0=B8=88=E0=B8=B8?= =?UTF-8?q?=E0=B8=9A=E0=B8=B1=E0=B8=99=E0=B8=95=E0=B9=89=E0=B8=AD=E0=B8=87?= =?UTF-8?q?=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B9=81=E0=B8=AA=E0=B8=94=E0=B8=87?= =?UTF-8?q?=E0=B8=82=E0=B8=AD=E0=B8=87=E0=B9=81=E0=B8=9A=E0=B8=9A=E0=B8=A3?= =?UTF-8?q?=E0=B9=88=E0=B8=B2=E0=B8=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/PositionController.ts | 99 ++++++++++++++------------- 1 file changed, 51 insertions(+), 48 deletions(-) diff --git a/src/controllers/PositionController.ts b/src/controllers/PositionController.ts index 04c29c98..5b61583e 100644 --- a/src/controllers/PositionController.ts +++ b/src/controllers/PositionController.ts @@ -2582,69 +2582,72 @@ export class PositionController extends Controller { async getHistoryPosMater(@Path() id: string, @Request() request: RequestWithUser) { let _workflow = await new permission().Workflow(request, id, "SYS_ORG"); if (_workflow == false) await new permission().PermissionGet(request, "SYS_ORG"); - const posMaster = await this.posMasterRepository.findOne({ - where: { id }, - }); + + // ใช้ query builder สำหรับประสิทธิภาพที่ดีขึ้น + const queryBuilder = this.posMasterRepository + .createQueryBuilder("pm") + .leftJoinAndSelect("pm.orgRevision", "orgRevision") + .leftJoinAndSelect("pm.orgRoot", "orgRoot") + .leftJoinAndSelect("pm.orgChild1", "orgChild1") + .leftJoinAndSelect("pm.orgChild2", "orgChild2") + .leftJoinAndSelect("pm.orgChild3", "orgChild3") + .leftJoinAndSelect("pm.orgChild4", "orgChild4") + .leftJoinAndSelect("pm.current_holder", "current_holder") + .leftJoinAndSelect("pm.positions", "positions") + .leftJoinAndSelect("positions.posLevel", "posLevel") + .leftJoinAndSelect("positions.posType", "posType") + .leftJoinAndSelect("positions.posExecutive", "posExecutive"); + + // หาข้อมูลหลัก + const posMaster = await queryBuilder.where("pm.id = :id", { id }).getOne(); + if (!posMaster) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลตำแหน่งนี้"); } - const posMasters = await this.posMasterRepository.find({ - where: { - ancestorDNA: - posMaster.ancestorDNA == null || posMaster.ancestorDNA == "" - ? "123" - : posMaster.ancestorDNA, - }, - order: { lastUpdatedAt: "DESC" }, - relations: [ - "orgRoot", - "orgChild1", - "orgChild2", - "orgChild3", - "orgChild4", - "current_holder", - "positions", - "positions.posLevel", - "positions.posType", - "positions.posExecutive", - ], - }); + + // สร้าง conditions + const ancestorDNA = posMaster.ancestorDNA || "123"; + let historyQuery = queryBuilder.where("pm.ancestorDNA = :ancestorDNA", { ancestorDNA }); + + // เพิ่มเงื่อนไข draft + if ( + posMaster.orgRevision?.orgRevisionIsCurrent != false && + posMaster.orgRevision?.orgRevisionIsDraft != true + ) { + historyQuery = historyQuery.andWhere("orgRevision.orgRevisionIsDraft != :isDraft", { + isDraft: true, + }); + } + + const posMasters = await historyQuery.orderBy("pm.lastUpdatedAt", "DESC").getMany(); + const _data = posMasters.map((item) => ({ id: item.id, orgShortName: - item.orgRoot == null - ? null - : item.orgChild1 == null - ? item.orgRoot.orgRootShortName - : item.orgChild2 == null - ? item.orgChild1.orgChild1ShortName - : item.orgChild3 == null - ? item.orgChild2.orgChild2ShortName - : item.orgChild4 == null - ? item.orgChild3.orgChild3ShortName - : item.orgChild4.orgChild4ShortName, - lastUpdatedAt: item.lastUpdatedAt ? item.lastUpdatedAt : null, - posMasterNoPrefix: item.posMasterNoPrefix ? item.posMasterNoPrefix : null, - posMasterNo: item.posMasterNo ? item.posMasterNo : null, - posMasterNoSuffix: item.posMasterNoSuffix ? item.posMasterNoSuffix : null, - reason: item.reason ? item.reason : null, + item.orgChild4?.orgChild4ShortName || + item.orgChild3?.orgChild3ShortName || + item.orgChild2?.orgChild2ShortName || + item.orgChild1?.orgChild1ShortName || + item.orgRoot?.orgRootShortName || + null, + lastUpdatedAt: item.lastUpdatedAt, + posMasterNoPrefix: item.posMasterNoPrefix, + posMasterNo: item.posMasterNo, + posMasterNoSuffix: item.posMasterNoSuffix, + reason: item.reason, position: item.positions.map((x) => x.positionName).join("/"), posExecutive: item.positions - .filter((x) => x.posExecutive != null) - .map((x) => x.posExecutive?.posExecutiveName ?? null) + .filter((x) => x.posExecutive) + .map((x) => x.posExecutive!.posExecutiveName) .join("/"), posLevel: item.positions.map((x) => x.posLevel.posLevelName).join("/"), posType: item.positions.map((x) => x.posType.posTypeName).join("/"), fullname: - (item?.current_holder?.prefix ?? "") + - "" + - (item?.current_holder?.firstName ?? "") + - " " + - (item?.current_holder?.lastName ?? ""), + `${item.current_holder?.prefix || ""}${item.current_holder?.firstName || ""} ${item.current_holder?.lastName || ""}`.trim(), })); + return new HttpSuccess(_data); } - /** * API ย้ายอัตรากำลัง * From 5443e3612d607b8a7c6078d5166779378fce689e Mon Sep 17 00:00:00 2001 From: mamoss <> Date: Sat, 9 Aug 2025 20:39:26 +0700 Subject: [PATCH 05/13] =?UTF-8?q?1729=201729=201792-=E0=B8=AA=E0=B8=96?= =?UTF-8?q?=E0=B8=B2=E0=B8=99=E0=B8=B0=E0=B8=A3=E0=B8=B1=E0=B8=81=E0=B8=A9?= =?UTF-8?q?=E0=B8=B2=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B8=AB=E0=B8=A5=E0=B8=B1?= =?UTF-8?q?=E0=B8=87=E0=B8=AD=E0=B8=AD=E0=B8=81=E0=B8=84=E0=B8=B3=E0=B8=AA?= =?UTF-8?q?=E0=B8=B1=E0=B9=88=E0=B8=87=E0=B8=A3=E0=B8=B1=E0=B8=81=E0=B8=A9?= =?UTF-8?q?=E0=B8=B2=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B8=99=E0=B9=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=88=E0=B8=B0=E0=B8=95=E0=B9=89=E0=B8=AD=E0=B8=87=E0=B9=80?= =?UTF-8?q?=E0=B8=9B=E0=B9=87=E0=B8=99=E0=B9=83=E0=B8=8A=E0=B9=89=E0=B8=87?= =?UTF-8?q?=E0=B8=B2=E0=B8=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/CommandController.ts | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/controllers/CommandController.ts b/src/controllers/CommandController.ts index 6724eabb..9fbf9199 100644 --- a/src/controllers/CommandController.ts +++ b/src/controllers/CommandController.ts @@ -6673,23 +6673,11 @@ export class CommandController extends Controller { }; try { - // 8. บันทึกข้อมูลใหม่ - const dataAct = new ProfileActposition(); - Object.assign(dataAct, metaAct); - - const historyAct = new ProfileActpositionHistory(); - Object.assign(historyAct, { ...dataAct, id: undefined }); - - await this.actpositionRepository.save(dataAct); - historyAct.profileActpositionId = dataAct.id; - await this.actpositionHistoryRepository.save(historyAct); - - // 9. ปิดสถานะรักษาการ + // 8. ปิดสถานะรักษาการ const existingActPositions = await this.actpositionRepository.find({ where: { profileId: item.posMasterChild.current_holderId, status: true, - id: Not(dataAct.id), }, }); @@ -6702,6 +6690,17 @@ export class CommandController extends Controller { await this.actpositionRepository.save(updatedActPositions); } + + // 9. บันทึกข้อมูลใหม่ + const dataAct = new ProfileActposition(); + Object.assign(dataAct, metaAct); + + const historyAct = new ProfileActpositionHistory(); + Object.assign(historyAct, { ...dataAct, id: undefined }); + + await this.actpositionRepository.save(dataAct); + historyAct.profileActpositionId = dataAct.id; + await this.actpositionHistoryRepository.save(historyAct); } catch (error) { console.error(`Error processing item ${item.id}:`, error); throw new HttpError( From 0e5cf4f69a87a9bb8bbd0b4ddbf3d248dd8937a2 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Wed, 13 Aug 2025 10:35:07 +0700 Subject: [PATCH 06/13] fix api manage list response system --- src/controllers/ApiManageController.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/controllers/ApiManageController.ts b/src/controllers/ApiManageController.ts index 66fd875f..6a6e4247 100644 --- a/src/controllers/ApiManageController.ts +++ b/src/controllers/ApiManageController.ts @@ -368,6 +368,7 @@ export class ApiManageController extends Controller { if (system) { queryBuilder.andWhere("apiName.system = :system", { system }); } + // console.log("query ===> ", queryBuilder.getQuery()); const [apiNames, total] = await queryBuilder .skip(offset) @@ -377,7 +378,10 @@ export class ApiManageController extends Controller { .getManyAndCount(); return new HttpSuccess({ - data: apiNames, + data: apiNames.map((api) => ({ + ...api, + system: this.systems.find((s) => s.code === api.system)?.name || api.system, + })), total, }); } catch (error) { From b92419c4d4d8098f4d33e6ff86a56405f12bd4c7 Mon Sep 17 00:00:00 2001 From: Bright Date: Wed, 13 Aug 2025 11:45:34 +0700 Subject: [PATCH 07/13] update #141 --- src/controllers/ApiWebServiceController.ts | 49 +++++-------------- .../OrganizationDotnetController.ts | 6 ++- 2 files changed, 18 insertions(+), 37 deletions(-) diff --git a/src/controllers/ApiWebServiceController.ts b/src/controllers/ApiWebServiceController.ts index 5ac66ed4..2a958387 100644 --- a/src/controllers/ApiWebServiceController.ts +++ b/src/controllers/ApiWebServiceController.ts @@ -59,7 +59,6 @@ export class ApiWebServiceController extends Controller { } await isPermissionRequest(request, apiName.id); const offset = (page - 1) * pageSize; - const propertyKey = apiName.apiAttributes.map((attr) => `${attr.tbName}.${attr.propertyKey}`); let Main:string = "" @@ -81,9 +80,9 @@ export class ApiWebServiceController extends Controller { select: ["id"], where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false } }); - condition = `OrgRoot.orgRevisionId = '${revision?.id}'` + condition = `OrgRoot.orgRevisionId = "${revision?.id}"` } - + const repo = AppDataSource.getRepository(Main); const metadata = repo.metadata; @@ -101,7 +100,7 @@ export class ApiWebServiceController extends Controller { .filter((tb) => tb !== Main) ) ] - + const queryBuilder = repo.createQueryBuilder(Main) // join กับตารารอง @@ -109,48 +108,26 @@ export class ApiWebServiceController extends Controller { propertyOtherKey.forEach((tb) => { const relationName = relationMap[tb]; if (relationName) { - queryBuilder.leftJoinAndSelect(`${Main}.${relationName}`, tb); + queryBuilder.leftJoin(`${Main}.${relationName}`, tb); } }); } + // เพิ่ม Main.id เพราะจะใช้ pk ในการแมบและนับจำนวน + if (!propertyKey.includes(`${Main}.id`)) { + propertyKey.push(`${Main}.id`); + } + const [items, total] = await queryBuilder + .select(propertyKey) .where(condition) - .orderBy(`${Main}.createdAt`, "DESC") + // .orderBy(`${Main}.createdAt`, "DESC") .skip(offset) .take(pageSize) .getManyAndCount(); - const results = items.map(item => { - const result: any = {}; - propertyKey.forEach(key => { - const [table, field] = key.split("."); - - if (table === Main) { - result[field] = item[field]; - } - else { - const relationName = relationMap[table]; - if (!relationName) return; - if (!Array.isArray(result[relationName])) { - result[relationName] = item[relationName] - ? item[relationName].map((relItem: any) => { - const filteredRel: any = {}; - propertyKey - .filter(k => k.startsWith(`${table}.`)) - .forEach(k => { - const [, relField] = k.split("."); - filteredRel[relField] = relItem[relField]; - }); - return filteredRel; - }) - : []; - } - } - }); - return result; - }); - + // ลบ Main.id + const results = items.map(({ id, ...x }) => x); return new HttpSuccess({ items: results, total }); } } diff --git a/src/controllers/OrganizationDotnetController.ts b/src/controllers/OrganizationDotnetController.ts index 5abd6a13..16fcd163 100644 --- a/src/controllers/OrganizationDotnetController.ts +++ b/src/controllers/OrganizationDotnetController.ts @@ -693,7 +693,11 @@ export class OrganizationDotnetController extends Controller { } } } - let positionLeaveName = profile.posLevel?.posLevelName ?? null; + let positionLeaveName = + profile.posType == null && profile.posLevel == null + ? "" + : `${profile.posType?.posTypeShortName ?? ""} ${profile.posLevel?.posLevelName ?? ""}` + const _profileCurrent = profile?.current_holders?.find( (x) => x.orgRevision?.orgRevisionIsDraft === false && From 9ec19fd22b8630b368c11df59dc3f7c1398ca3d8 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Wed, 13 Aug 2025 14:50:39 +0700 Subject: [PATCH 08/13] fix api list filter isActive --- src/controllers/ApiManageController.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/controllers/ApiManageController.ts b/src/controllers/ApiManageController.ts index 6a6e4247..295fed4c 100644 --- a/src/controllers/ApiManageController.ts +++ b/src/controllers/ApiManageController.ts @@ -337,6 +337,7 @@ export class ApiManageController extends Controller { @Query("pageSize") pageSize: number = 10, @Query() keyword: string = "", @Query() system: "registry" | "registry_emp" | "registry_temp" | "organization" | "" = "", + @Query() isActive: boolean = true, ): Promise { try { if (!req.user.role.includes("SUPER_ADMIN")) { @@ -346,6 +347,7 @@ export class ApiManageController extends Controller { const queryBuilder = AppDataSource.getRepository(ApiName) .createQueryBuilder("apiName") + .where("apiName.isActive = :isActive", { isActive: isActive ?? true }) .select([ "apiName.id", "apiName.name", @@ -359,7 +361,7 @@ export class ApiManageController extends Controller { ]); if (keyword?.trim()) { - queryBuilder.where( + queryBuilder.andWhere( "(apiName.name LIKE :keyword OR apiName.code LIKE :keyword OR apiName.pathApi LIKE :keyword)", { keyword: `%${keyword.trim()}%` }, ); From 66b2f10feeacfaeae7b63c6fa5c121950530af99 Mon Sep 17 00:00:00 2001 From: Bright Date: Wed, 13 Aug 2025 16:14:44 +0700 Subject: [PATCH 09/13] update #141 --- src/controllers/ApiWebServiceController.ts | 55 +++++++++++++++++++--- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/src/controllers/ApiWebServiceController.ts b/src/controllers/ApiWebServiceController.ts index 2a958387..7c664da4 100644 --- a/src/controllers/ApiWebServiceController.ts +++ b/src/controllers/ApiWebServiceController.ts @@ -20,6 +20,7 @@ import { Profile } from "../entities/Profile"; import { isPermissionRequest } from "../middlewares/authWebService"; import { RequestWithUserWebService } from "../middlewares/user"; import { OrgRevision } from "../entities/OrgRevision"; +import { ApiHistory } from "../entities/ApiHistory"; @Route("api/v2/org/api-service") @Tags("ApiKey") @Security("webServiceAuth") @@ -30,6 +31,8 @@ import { OrgRevision } from "../entities/OrgRevision"; export class ApiWebServiceController extends Controller { private apiNameRepository = AppDataSource.getRepository(ApiName); private orgRevisionRepository = AppDataSource.getRepository(OrgRevision); + private apiHistoryRepository = AppDataSource.getRepository(ApiHistory); + /** * list fields by systems * @summary รายการ fields ตาม systems @@ -113,21 +116,59 @@ export class ApiWebServiceController extends Controller { }); } - // เพิ่ม Main.id เพราะจะใช้ pk ในการแมบและนับจำนวน - if (!propertyKey.includes(`${Main}.id`)) { - propertyKey.push(`${Main}.id`); - } + // // เพิ่ม Main.id เพราะจะใช้ pk ในการแมบและนับจำนวน + // if (!propertyKey.includes(`${Main}.id`)) { + // propertyKey.push(`${Main}.id`); + // } + + // add FK + let pk:string = "" + const primaryColumns = metadata.primaryColumns; + primaryColumns.forEach(col => { + pk = col.propertyName; + if (!propertyKey.includes(`${Main}.${pk}`)) { + propertyKey.push(`${Main}.${pk}`); + } + }); const [items, total] = await queryBuilder .select(propertyKey) .where(condition) - // .orderBy(`${Main}.createdAt`, "DESC") + .orderBy(propertyKey[0], "ASC") .skip(offset) .take(pageSize) .getManyAndCount(); // ลบ Main.id - const results = items.map(({ id, ...x }) => x); - return new HttpSuccess({ items: results, total }); + // const results = items.map(({ id, ...x }) => x); + // const results = items.map(({ pk, ...x }) => x); + + // const results = items.map(item => { + // primaryColumns.forEach(col => delete item[col.propertyName]); + // return item; + // }); + + // save api history + const history = { + headerApi: JSON.stringify({ + host: request.headers.host, + "x-api-key": request.headers["x-api-key"], + connection: request.headers.connection, + accept: request.headers.accept, + }), + tokenApi: Array.isArray(request.headers["x-api-key"]) + ? request.headers["x-api-key"][0] || "" + : request.headers["x-api-key"] || "", + requestApi: `${request.method} ${request.protocol}://${request.headers.host}${request.originalUrl || request.url}`, + responseApi: "OK", + ipApi: request.ip, + codeApi: code, + apiKeyId: request.user.id, + apiNameId: apiName.id, + createdFullName: request.user.name, + lastUpdateFullName: request.user.name, + }; + await this.apiHistoryRepository.save(history); + return new HttpSuccess({ items: items, total }); } } From 6e0a61bafa5662ae01ac92a6ed31381791066261 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Wed, 13 Aug 2025 16:47:01 +0700 Subject: [PATCH 10/13] api web service remove pk before response --- src/controllers/ApiWebServiceController.ts | 87 +++++++++------------- 1 file changed, 37 insertions(+), 50 deletions(-) diff --git a/src/controllers/ApiWebServiceController.ts b/src/controllers/ApiWebServiceController.ts index 7c664da4..124f0914 100644 --- a/src/controllers/ApiWebServiceController.ts +++ b/src/controllers/ApiWebServiceController.ts @@ -1,22 +1,9 @@ -import { - Controller, - Post, - Route, - Security, - Tags, - Body, - Path, - Request, - Response, - Get, - Query, -} from "tsoa"; +import { Controller, Route, Security, Tags, Path, Request, Response, Get, Query } from "tsoa"; import { AppDataSource } from "../database/data-source"; import HttpSuccess from "../interfaces/http-success"; import HttpStatusCode from "../interfaces/http-status"; import HttpError from "../interfaces/http-error"; import { ApiName } from "../entities/ApiName"; -import { Profile } from "../entities/Profile"; import { isPermissionRequest } from "../middlewares/authWebService"; import { RequestWithUserWebService } from "../middlewares/user"; import { OrgRevision } from "../entities/OrgRevision"; @@ -45,7 +32,6 @@ export class ApiWebServiceController extends Controller { @Query("page") page: number = 1, @Query("pageSize") pageSize: number = 100, ): Promise { - const apiName = await this.apiNameRepository.findOne({ where: { code }, select: ["id", "code", "methodApi", "system", "isActive"], @@ -64,67 +50,60 @@ export class ApiWebServiceController extends Controller { const offset = (page - 1) * pageSize; const propertyKey = apiName.apiAttributes.map((attr) => `${attr.tbName}.${attr.propertyKey}`); - let Main:string = "" - let condition: string = "1=1" + let Main: string = ""; + let condition: string = "1=1"; if (system == "registry") { - Main = "Profile" - } - else if (system == "registry_emp") { - Main = "ProfileEmployee" - condition = `ProfileEmployee.employeeClass = "PERM"` - } - else if (system == "registry_temp") { - Main = "ProfileEmployee" - condition = `ProfileEmployee.employeeClass = "TEMP"` - } - else { - Main = "OrgRoot" + Main = "Profile"; + } else if (system == "registry_emp") { + Main = "ProfileEmployee"; + condition = `ProfileEmployee.employeeClass = "PERM"`; + } else if (system == "registry_temp") { + Main = "ProfileEmployee"; + condition = `ProfileEmployee.employeeClass = "TEMP"`; + } else { + Main = "OrgRoot"; const revision = await this.orgRevisionRepository.findOne({ - select: ["id"], - where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false } - }); - condition = `OrgRoot.orgRevisionId = "${revision?.id}"` + select: ["id"], + where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, + }); + condition = `OrgRoot.orgRevisionId = "${revision?.id}"`; } const repo = AppDataSource.getRepository(Main); const metadata = repo.metadata; - + const relationMap: Record = {}; metadata.relations.forEach((rel) => { relationMap[rel.inverseEntityMetadata.name] = rel.propertyName; }); - + // ดึงเฉพาะตารางรอง (ถ้าเลือกไว้) - let propertyOtherKey:any[] = []; + let propertyOtherKey: any[] = []; propertyOtherKey = [ - ...new Set( - propertyKey - .map((x) => x.split(".")[0]) - .filter((tb) => tb !== Main) - ) - ] - - const queryBuilder = repo.createQueryBuilder(Main) + ...new Set(propertyKey.map((x) => x.split(".")[0]).filter((tb) => tb !== Main)), + ]; + + const queryBuilder = repo.createQueryBuilder(Main); // join กับตารารอง if (propertyOtherKey.length > 0) { propertyOtherKey.forEach((tb) => { const relationName = relationMap[tb]; if (relationName) { - queryBuilder.leftJoin(`${Main}.${relationName}`, tb); + queryBuilder.leftJoin(`${Main}.${relationName}`, tb); } }); } - + // // เพิ่ม Main.id เพราะจะใช้ pk ในการแมบและนับจำนวน // if (!propertyKey.includes(`${Main}.id`)) { // propertyKey.push(`${Main}.id`); // } // add FK - let pk:string = "" + let pk: string = ""; const primaryColumns = metadata.primaryColumns; - primaryColumns.forEach(col => { + primaryColumns.forEach((col) => { pk = col.propertyName; if (!propertyKey.includes(`${Main}.${pk}`)) { propertyKey.push(`${Main}.${pk}`); @@ -148,7 +127,15 @@ export class ApiWebServiceController extends Controller { // return item; // }); - // save api history + // split object id ออกก่อน return + const data = items.map((item) => { + const { [pk]: removedPk, ...x } = item; + return x; + }); + + // console.log("queryBuilder ===> ", queryBuilder.getQuery()); + + // save api history after query success const history = { headerApi: JSON.stringify({ host: request.headers.host, @@ -169,6 +156,6 @@ export class ApiWebServiceController extends Controller { lastUpdateFullName: request.user.name, }; await this.apiHistoryRepository.save(history); - return new HttpSuccess({ items: items, total }); + return new HttpSuccess({ data, total }); } } From 4318a7b631ee9d5b37d28bebfe581316d18b72cc Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Thu, 14 Aug 2025 11:17:13 +0700 Subject: [PATCH 11/13] fix api service v2 to v1 & fix type & review code --- src/controllers/ApiManageController.ts | 870 +++++++++++---------- src/controllers/ApiWebServiceController.ts | 30 +- src/interfaces/api-type.ts | 16 + 3 files changed, 485 insertions(+), 431 deletions(-) create mode 100644 src/interfaces/api-type.ts diff --git a/src/controllers/ApiManageController.ts b/src/controllers/ApiManageController.ts index 295fed4c..bc96af1a 100644 --- a/src/controllers/ApiManageController.ts +++ b/src/controllers/ApiManageController.ts @@ -1,3 +1,4 @@ +import { Position } from "./../entities/Position"; import { ApiKey } from "./../entities/ApiKey"; import { ApiAttribute } from "./../entities/ApiAttribute"; import { CreateApi } from "./../entities/ApiName"; @@ -49,7 +50,7 @@ import HttpError from "../interfaces/http-error"; import { RequestWithUser } from "../middlewares/user"; import { ApiName } from "../entities/ApiName"; import { Profile } from "../entities/Profile"; - +import { SystemDefinition, EntityDefinition, SystemCode } from "./../interfaces/api-type"; @Route("api/v1/org/api-manage") @Tags("ApiManage") @Security("bearerAuth") @@ -58,36 +59,37 @@ import { Profile } from "../entities/Profile"; "เกิดข้อผิดพลาด ไม่สามารถแสดงรายการได้ กรุณาลองใหม่ในภายหลัง", ) export class ApiManageController extends Controller { - // private apiNameRepository = AppDataSource.getRepository(ApiName); - private profileRepository = AppDataSource.getRepository(Profile); - private profileEmployeeRepository = AppDataSource.getRepository(ProfileEmployee); - private profileGovernmentRepository = AppDataSource.getRepository(ProfileGovernment); - private profileProfileSalaryRepository = AppDataSource.getRepository(ProfileSalary); - private profileAbilityRepository = AppDataSource.getRepository(ProfileAbility); - private profileAssessmentRepository = AppDataSource.getRepository(ProfileAssessment); - private profileCertificateRepository = AppDataSource.getRepository(ProfileCertificate); - private profileHonorRepository = AppDataSource.getRepository(ProfileHonor); - private profileInsigniaRepository = AppDataSource.getRepository(ProfileInsignia); - private profileLeaveRepository = AppDataSource.getRepository(ProfileLeave); - private profileChangeNameRepository = AppDataSource.getRepository(ProfileChangeName); - private profileDevelopmentRepository = AppDataSource.getRepository(ProfileDevelopment); - private profileDisciplineRepository = AppDataSource.getRepository(ProfileDiscipline); - private profileDutyRepository = AppDataSource.getRepository(ProfileDuty); - private profileEducationRepository = AppDataSource.getRepository(ProfileEducation); - private profileFamilyCoupleRepository = AppDataSource.getRepository(ProfileFamilyCouple); - private profileFamilyFatherRepository = AppDataSource.getRepository(ProfileFamilyFather); - private profileFamilyMotherRepository = AppDataSource.getRepository(ProfileFamilyMother); - private profileChildrenRepository = AppDataSource.getRepository(ProfileChildren); - private profileNopaidRepository = AppDataSource.getRepository(ProfileNopaid); - private profileOtherRepository = AppDataSource.getRepository(ProfileOther); - private orgRootRepository = AppDataSource.getRepository(OrgRoot); - private orgChild1Repository = AppDataSource.getRepository(OrgChild1); - private orgChild2Repository = AppDataSource.getRepository(OrgChild2); - private orgChild3Repository = AppDataSource.getRepository(OrgChild3); - private orgChild4Repository = AppDataSource.getRepository(OrgChild4); - private posMasterRepository = AppDataSource.getRepository(PosMaster); + private readonly profileRepository = AppDataSource.getRepository(Profile); + private readonly profileEmployeeRepository = AppDataSource.getRepository(ProfileEmployee); + private readonly profileGovernmentRepository = AppDataSource.getRepository(ProfileGovernment); + private readonly profileProfileSalaryRepository = AppDataSource.getRepository(ProfileSalary); + private readonly profileAbilityRepository = AppDataSource.getRepository(ProfileAbility); + private readonly profileAssessmentRepository = AppDataSource.getRepository(ProfileAssessment); + private readonly profileCertificateRepository = AppDataSource.getRepository(ProfileCertificate); + private readonly profileHonorRepository = AppDataSource.getRepository(ProfileHonor); + private readonly profileInsigniaRepository = AppDataSource.getRepository(ProfileInsignia); + private readonly profileLeaveRepository = AppDataSource.getRepository(ProfileLeave); + private readonly profileChangeNameRepository = AppDataSource.getRepository(ProfileChangeName); + private readonly profileDevelopmentRepository = AppDataSource.getRepository(ProfileDevelopment); + private readonly profileDisciplineRepository = AppDataSource.getRepository(ProfileDiscipline); + private readonly profileDutyRepository = AppDataSource.getRepository(ProfileDuty); + private readonly profileEducationRepository = AppDataSource.getRepository(ProfileEducation); + private readonly profileFamilyCoupleRepository = AppDataSource.getRepository(ProfileFamilyCouple); + private readonly profileFamilyFatherRepository = AppDataSource.getRepository(ProfileFamilyFather); + private readonly profileFamilyMotherRepository = AppDataSource.getRepository(ProfileFamilyMother); + private readonly profileChildrenRepository = AppDataSource.getRepository(ProfileChildren); + private readonly profileNopaidRepository = AppDataSource.getRepository(ProfileNopaid); + private readonly profileOtherRepository = AppDataSource.getRepository(ProfileOther); + private readonly orgRootRepository = AppDataSource.getRepository(OrgRoot); + private readonly orgChild1Repository = AppDataSource.getRepository(OrgChild1); + private readonly orgChild2Repository = AppDataSource.getRepository(OrgChild2); + private readonly orgChild3Repository = AppDataSource.getRepository(OrgChild3); + private readonly orgChild4Repository = AppDataSource.getRepository(OrgChild4); + private readonly posMasterRepository = AppDataSource.getRepository(PosMaster); + private readonly positionRepository = AppDataSource.getRepository(Position); - private systems = [ + // รายการระบบ + private readonly systems: SystemDefinition[] = [ { code: "registry", name: "ข้อมูลทะเบียนประวัติข้าราชการ", @@ -105,12 +107,236 @@ export class ApiManageController extends Controller { name: "ข้อมูลโครงสร้างและอัตรากำลัง", }, ]; + + // รายการเอนทิตีทั้งหมด + private readonly entities: EntityDefinition[] = [ + { + name: "Profile", + repository: this.profileRepository, + description: "ข้อมูลหลัก", + isMain: true, + system: ["registry"], + }, + { + name: "ProfileEmployee", + repository: this.profileEmployeeRepository, + description: "ข้อมูลหลัก", + isMain: true, + system: ["registry_emp", "registry_temp"], + }, + { + name: "ProfileGovernment", + repository: this.profileGovernmentRepository, + description: "ข้อมูลราชการ", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileSalary", + repository: this.profileProfileSalaryRepository, + description: "ข้อมูลตำแหน่ง/เงินเดือน", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileAbility", + repository: this.profileAbilityRepository, + description: "ข้อมูลความสามารถ", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileAssessment", + repository: this.profileAssessmentRepository, + description: "ข้อมูลการประเมิน", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileCertificate", + repository: this.profileCertificateRepository, + description: "ข้อมูลใบรับรอง", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileHonor", + repository: this.profileHonorRepository, + description: "ข้อมูลเกียรติบัตร", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileInsignia", + repository: this.profileInsigniaRepository, + description: "ข้อมูลเครื่องหมาย", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileLeave", + repository: this.profileLeaveRepository, + description: "ข้อมูลการลา", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileChangeName", + repository: this.profileChangeNameRepository, + description: "ข้อมูลการเปลี่ยนชื่อ", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileDevelopment", + repository: this.profileDevelopmentRepository, + description: "ข้อมูลการพัฒนา", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileDiscipline", + repository: this.profileDisciplineRepository, + description: "ข้อมูลวินัย", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileDuty", + repository: this.profileDutyRepository, + description: "ข้อมูลหน้าที่ปฏิบัติราชการ", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileEducation", + repository: this.profileEducationRepository, + description: "ข้อมูลการศึกษา", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileFamilyCouple", + repository: this.profileFamilyCoupleRepository, + description: "ข้อมูลคู่สมรส", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileFamilyFather", + repository: this.profileFamilyFatherRepository, + description: "ข้อมูลบิดา", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileFamilyMother", + repository: this.profileFamilyMotherRepository, + description: "ข้อมูลมารดา", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileChildren", + repository: this.profileChildrenRepository, + description: "ข้อมูลบุตร", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileNopaid", + repository: this.profileNopaidRepository, + description: "ข้อมูลการไม่จ่ายเงินเดือน", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileOther", + repository: this.profileOtherRepository, + description: "ข้อมูลอื่นๆ", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "OrgRoot", + repository: this.orgRootRepository, + description: "ข้อมูลหน่วยงาน", + isMain: true, + system: ["organization"], + }, + { + name: "OrgChild1", + repository: this.orgChild1Repository, + description: "ข้อมูลส่วนราชการ ระดับที่ 1", + system: ["organization"], + }, + { + name: "OrgChild2", + repository: this.orgChild2Repository, + description: "ข้อมูลส่วนราชการ ระดับที่ 2", + system: ["organization"], + }, + { + name: "OrgChild3", + repository: this.orgChild3Repository, + description: "ข้อมูลส่วนราชการ ระดับที่ 3", + system: ["organization"], + }, + { + name: "OrgChild4", + repository: this.orgChild4Repository, + description: "ข้อมูลส่วนราชการ ระดับที่ 4", + system: ["organization"], + }, + { + name: "PosMaster", + repository: this.posMasterRepository, + description: "ข้อมูลอัตรากำลัง", + isMain: true, + system: ["position"], + }, + { + name: "position", + repository: this.positionRepository, + description: "ข้อมูลตำแหน่ง", + system: ["position"], + }, + { + name: "OrgRoot", + repository: this.orgRootRepository, + description: "ข้อมูลหน่วยงาน", + system: ["position"], + }, + { + name: "OrgChild1", + repository: this.orgChild1Repository, + description: "ข้อมูลส่วนราชการ ระดับที่ 1", + system: ["position"], + }, + { + name: "OrgChild2", + repository: this.orgChild2Repository, + description: "ข้อมูลส่วนราชการ ระดับที่ 2", + system: ["position"], + }, + { + name: "OrgChild3", + repository: this.orgChild3Repository, + description: "ข้อมูลส่วนราชการ ระดับที่ 3", + system: ["position"], + }, + { + name: "OrgChild4", + repository: this.orgChild4Repository, + description: "ข้อมูลส่วนราชการ ระดับที่ 4", + system: ["position"], + }, + ]; + + private readonly DEFAULT_PAGE_SIZE = 10; // ขนาดหน้าเริ่มต้น + private readonly EXCLUDED_COLUMNS = ["createdUserId", "lastUpdateUserId"]; // ฟิลด์ที่ไม่ต้องการแสดงในผลลัพธ์ + + private validateSuperAdminRole(user: any): void { + if (!user.role.includes("SUPER_ADMIN")) { + throw new HttpError(HttpStatusCode.FORBIDDEN, "คุณไม่มีสิทธิ์ในการเข้าถึงข้อมูลนี้"); + } + } + + private generateApiCode(): string { + return Math.random().toString(36).substring(2, 10).toUpperCase(); + } + + private createApiPath(system: SystemCode = "registry", code: string): string { + return `/api/v1/org/api-service/${system}/${code}`; + } + /** * list systems * @summary รายการ systems */ @Get("/systems") - async listSystems() { + async listSystems(): Promise { return new HttpSuccess(this.systems); } @@ -121,194 +347,23 @@ export class ApiManageController extends Controller { @Get("/:system/fields") async listAttribute( @Request() req: RequestWithUser, - @Path("system") system: "registry" | "registry_emp" | "registry_temp" | "organization", + @Path("system") system: SystemCode, ): Promise { try { - if (!req.user.role.includes("SUPER_ADMIN")) { - throw new HttpError(HttpStatusCode.FORBIDDEN, "คุณไม่มีสิทธิ์ในการเข้าถึงข้อมูลนี้"); - } - const entities = [ - { - name: "Profile", - repository: this.profileRepository, - description: "ข้อมูลหลัก", - isMain: true, - system: ["registry"], - }, - { - name: "ProfileEmployee", - repository: this.profileEmployeeRepository, - description: "ข้อมูลหลัก", - isMain: true, - system: ["registry_emp", "registry_temp"], - }, - { - name: "ProfileGovernment", - repository: this.profileGovernmentRepository, - description: "ข้อมูลราชการ", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileSalary", - repository: this.profileProfileSalaryRepository, - description: "ข้อมูลตำแหน่ง/เงินเดือน", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileAbility", - repository: this.profileAbilityRepository, - description: "ข้อมูลความสามารถ", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileAssessment", - repository: this.profileAssessmentRepository, - description: "ข้อมูลการประเมิน", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileCertificate", - repository: this.profileCertificateRepository, - description: "ข้อมูลใบรับรอง", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileHonor", - repository: this.profileHonorRepository, - description: "ข้อมูลเกียรติบัตร", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileInsignia", - repository: this.profileInsigniaRepository, - description: "ข้อมูลเครื่องหมาย", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileLeave", - repository: this.profileLeaveRepository, - description: "ข้อมูลการลา", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileChangeName", - repository: this.profileChangeNameRepository, - description: "ข้อมูลการเปลี่ยนชื่อ", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileDevelopment", - repository: this.profileDevelopmentRepository, - description: "ข้อมูลการพัฒนา", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileDiscipline", - repository: this.profileDisciplineRepository, - description: "ข้อมูลวินัย", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileDuty", - repository: this.profileDutyRepository, - description: "ข้อมูลหน้าที่ปฏิบัติราชการ", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileEducation", - repository: this.profileEducationRepository, - description: "ข้อมูลการศึกษา", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileFamilyCouple", - repository: this.profileFamilyCoupleRepository, - description: "ข้อมูลคู่สมรส", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileFamilyFather", - repository: this.profileFamilyFatherRepository, - description: "ข้อมูลบิดา", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileFamilyMother", - repository: this.profileFamilyMotherRepository, - description: "ข้อมูลมารดา", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileChildren", - repository: this.profileChildrenRepository, - description: "ข้อมูลบุตร", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileNopaid", - repository: this.profileNopaidRepository, - description: "ข้อมูลการไม่จ่ายเงินเดือน", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileOther", - repository: this.profileOtherRepository, - description: "ข้อมูลอื่นๆ", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "OrgRoot", - repository: this.orgRootRepository, - description: "ข้อมูลหน่วยงาน", - isMain: true, - system: ["organization"], - }, - { - name: "OrgChild1", - repository: this.orgChild1Repository, - description: "ข้อมูลส่วนราชการ ระดับที่ 1", - system: ["organization"], - }, - { - name: "OrgChild2", - repository: this.orgChild2Repository, - description: "ข้อมูลส่วนราชการ ระดับที่ 2", - system: ["organization"], - }, - { - name: "OrgChild3", - repository: this.orgChild3Repository, - description: "ข้อมูลส่วนราชการ ระดับที่ 3", - system: ["organization"], - }, - { - name: "OrgChild4", - repository: this.orgChild4Repository, - description: "ข้อมูลส่วนราชการ ระดับที่ 4", - system: ["organization"], - }, - { - name: "PosMaster", - repository: this.posMasterRepository, - description: "ข้อมูลตำแหน่ง", - system: ["organization"], - }, - ]; + this.validateSuperAdminRole(req.user); - const result = entities - .filter((s) => s.system.includes(system)) + const result = this.entities + .filter((entity) => entity.system.includes(system)) .map(({ name, repository, description, isMain }) => ({ tb: name, description, isMain: isMain || false, propertys: repository.metadata.columns .filter( - (column) => - !column.isPrimary && - column.propertyName !== "createdUserId" && - column.propertyName !== "lastUpdateUserId", + (column: any) => + !column.isPrimary && !this.EXCLUDED_COLUMNS.includes(column.propertyName), ) - .map((column) => ({ + .map((column: any) => ({ propertyName: column.propertyName, type: typeof column.type === "string" ? column.type : "string", comment: column.comment, @@ -334,20 +389,18 @@ export class ApiManageController extends Controller { async listApi( @Request() req: RequestWithUser, @Query("page") page: number = 1, - @Query("pageSize") pageSize: number = 10, - @Query() keyword: string = "", - @Query() system: "registry" | "registry_emp" | "registry_temp" | "organization" | "" = "", - @Query() isActive: boolean = true, + @Query("pageSize") pageSize: number = this.DEFAULT_PAGE_SIZE, + @Query("keyword") keyword: string = "", + @Query("system") system: SystemCode | undefined = undefined, + @Query("isActive") isActive: boolean = true, ): Promise { try { - if (!req.user.role.includes("SUPER_ADMIN")) { - throw new HttpError(HttpStatusCode.FORBIDDEN, "คุณไม่มีสิทธิ์ในการเข้าถึงข้อมูลนี้"); - } - const offset = (page - 1) * pageSize; + this.validateSuperAdminRole(req.user); + const offset = (page - 1) * pageSize; const queryBuilder = AppDataSource.getRepository(ApiName) .createQueryBuilder("apiName") - .where("apiName.isActive = :isActive", { isActive: isActive ?? true }) + .where("apiName.isActive = :isActive", { isActive }) .select([ "apiName.id", "apiName.name", @@ -370,7 +423,6 @@ export class ApiManageController extends Controller { if (system) { queryBuilder.andWhere("apiName.system = :system", { system }); } - // console.log("query ===> ", queryBuilder.getQuery()); const [apiNames, total] = await queryBuilder .skip(offset) @@ -404,59 +456,54 @@ export class ApiManageController extends Controller { @Request() req: RequestWithUser, @Body() apiData: CreateApi, ): Promise { + const queryRunner = AppDataSource.createQueryRunner(); + await queryRunner.connect(); + await queryRunner.startTransaction(); + try { - const queryRunner = AppDataSource.createQueryRunner(); - await queryRunner.connect(); - await queryRunner.startTransaction(); + this.validateSuperAdminRole(req.user); - try { - if (!req.user.role.includes("SUPER_ADMIN")) { - throw new HttpError(HttpStatusCode.FORBIDDEN, "คุณไม่มีสิทธิ์ในการเข้าถึงข้อมูลนี้"); - } + const code = this.generateApiCode(); + const postData = { + name: apiData.name, + code, + pathApi: this.createApiPath(apiData.system as SystemCode, code), + methodApi: apiData.methodApi || "GET", + system: apiData.system || "registry", + isActive: apiData.isActive || false, + createdUserId: req.user?.sub, + createdFullName: req.user?.name || "", + }; - const code = Math.random().toString(36).substring(2, 10).toUpperCase(); - const postData = { - name: apiData.name, - code, - pathApi: `/api/v2/org/api-service/${apiData.system}/${code}`, - methodApi: apiData.methodApi || "GET", - system: apiData.system || "registry", - isActive: apiData.isActive || false, - createdUserId: req.user?.sub || undefined, - createdFullName: req.user?.name || "", - }; - const apiName = await queryRunner.manager.getRepository(ApiName).save(postData); + const apiName = await queryRunner.manager.getRepository(ApiName).save(postData); - if (apiData.apiAttributes?.length) { - let orderingCounter = 0; - const attributesToSave = apiData.apiAttributes.flatMap((attr) => - attr.propertyKey.map((propertyKey) => ({ - apiNameId: apiName.id, - tbName: attr.tbName, - propertyKey, - ordering: orderingCounter++, - createdUserId: req.user?.sub || undefined, - createdFullName: req.user?.name || "", - })), - ); + if (apiData.apiAttributes?.length) { + let orderingCounter = 0; + const attributesToSave = apiData.apiAttributes.flatMap((attr) => + attr.propertyKey.map((propertyKey) => ({ + apiNameId: apiName.id, + tbName: attr.tbName, + propertyKey, + ordering: orderingCounter++, + createdUserId: req.user?.sub, + createdFullName: req.user?.name || "", + })), + ); - await queryRunner.manager.getRepository(ApiAttribute).save(attributesToSave); - } - - await queryRunner.commitTransaction(); - return new HttpSuccess(apiName.id); - } catch (transactionError) { - await queryRunner.rollbackTransaction(); - throw transactionError; - } finally { - await queryRunner.release(); + await queryRunner.manager.getRepository(ApiAttribute).save(attributesToSave); } + + await queryRunner.commitTransaction(); + return new HttpSuccess(apiName.id); } catch (error) { + await queryRunner.rollbackTransaction(); throw new HttpError( HttpStatusCode.INTERNAL_SERVER_ERROR, (error instanceof Error ? error.message : String(error)) || "เกิดข้อผิดพลาด ไม่สามารถบันทึกข้อมูลได้ กรุณาลองใหม่ในภายหลัง", ); + } finally { + await queryRunner.release(); } } @@ -470,9 +517,7 @@ export class ApiManageController extends Controller { @Path("id") id: string, ): Promise { try { - if (!req.user.role.includes("SUPER_ADMIN")) { - throw new HttpError(HttpStatusCode.FORBIDDEN, "คุณไม่มีสิทธิ์ในการเข้าถึงข้อมูลนี้"); - } + this.validateSuperAdminRole(req.user); const apiName = await AppDataSource.getRepository(ApiName).findOne({ select: { @@ -493,6 +538,7 @@ export class ApiManageController extends Controller { }, }, }); + if (!apiName) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบ API ที่ต้องการแก้ไข"); } @@ -504,6 +550,7 @@ export class ApiManageController extends Controller { propertyKey: attr.propertyKey, })), }; + return new HttpSuccess(items); } catch (error) { throw new HttpError( @@ -524,63 +571,58 @@ export class ApiManageController extends Controller { @Path("id") id: string, @Body() apiData: CreateApi, ): Promise { + const queryRunner = AppDataSource.createQueryRunner(); + await queryRunner.connect(); + await queryRunner.startTransaction(); + try { - const queryRunner = AppDataSource.createQueryRunner(); - await queryRunner.connect(); - await queryRunner.startTransaction(); + this.validateSuperAdminRole(req.user); - try { - if (!req.user.role.includes("SUPER_ADMIN")) { - throw new HttpError(HttpStatusCode.FORBIDDEN, "คุณไม่มีสิทธิ์ในการเข้าถึงข้อมูลนี้"); - } - - const apiName = await queryRunner.manager.getRepository(ApiName).findOneBy({ id }); - if (!apiName) { - throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบ API ที่ต้องการแก้ไข"); - } - - const updateData = { - name: apiData.name, - pathApi: `/api/v2/org/api-service/${apiData.system}/${apiName.code}`, - methodApi: apiData.methodApi || "GET", - system: apiData.system || "registry", - isActive: apiData.isActive || false, - lastUpdateUserId: req.user?.sub || undefined, - lastUpdateFullName: req.user?.name || "", - }; - await queryRunner.manager.getRepository(ApiName).update(id, updateData); - - await queryRunner.manager.getRepository(ApiAttribute).delete({ apiNameId: id }); - if (apiData.apiAttributes?.length) { - let orderingCounter = 0; - const attributesToSave = apiData.apiAttributes.flatMap((attr) => - attr.propertyKey.map((propertyKey) => ({ - apiNameId: apiName.id, - tbName: attr.tbName, - propertyKey, - ordering: orderingCounter++, - lastUpdateUserId: req.user?.sub || undefined, - lastUpdateFullName: req.user?.name || "", - })), - ); - - await queryRunner.manager.getRepository(ApiAttribute).save(attributesToSave); - } - - await queryRunner.commitTransaction(); - return new HttpSuccess(); - } catch (transactionError) { - await queryRunner.rollbackTransaction(); - throw transactionError; - } finally { - await queryRunner.release(); + const apiName = await queryRunner.manager.getRepository(ApiName).findOneBy({ id }); + if (!apiName) { + throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบ API ที่ต้องการแก้ไข"); } + + const updateData = { + name: apiData.name, + pathApi: this.createApiPath(apiData.system as SystemCode, apiName.code), + methodApi: apiData.methodApi || "GET", + system: apiData.system || "registry", + isActive: apiData.isActive || false, + lastUpdateUserId: req.user?.sub, + lastUpdateFullName: req.user?.name || "", + }; + + await queryRunner.manager.getRepository(ApiName).update(id, updateData); + await queryRunner.manager.getRepository(ApiAttribute).delete({ apiNameId: id }); + + if (apiData.apiAttributes?.length) { + let orderingCounter = 0; + const attributesToSave = apiData.apiAttributes.flatMap((attr) => + attr.propertyKey.map((propertyKey) => ({ + apiNameId: apiName.id, + tbName: attr.tbName, + propertyKey, + ordering: orderingCounter++, + lastUpdateUserId: req.user?.sub, + lastUpdateFullName: req.user?.name || "", + })), + ); + + await queryRunner.manager.getRepository(ApiAttribute).save(attributesToSave); + } + + await queryRunner.commitTransaction(); + return new HttpSuccess(); } catch (error) { + await queryRunner.rollbackTransaction(); throw new HttpError( HttpStatusCode.INTERNAL_SERVER_ERROR, (error instanceof Error ? error.message : String(error)) || "เกิดข้อผิดพลาด ไม่สามารถบันทึกข้อมูลได้ กรุณาลองใหม่ในภายหลัง", ); + } finally { + await queryRunner.release(); } } @@ -593,46 +635,40 @@ export class ApiManageController extends Controller { @Request() req: RequestWithUser, @Path("id") id: string, ): Promise { + const queryRunner = AppDataSource.createQueryRunner(); + await queryRunner.connect(); + await queryRunner.startTransaction(); + try { - const queryRunner = AppDataSource.createQueryRunner(); - await queryRunner.connect(); - await queryRunner.startTransaction(); + this.validateSuperAdminRole(req.user); - try { - if (!req.user.role.includes("SUPER_ADMIN")) { - throw new HttpError(HttpStatusCode.FORBIDDEN, "คุณไม่มีสิทธิ์ในการเข้าถึงข้อมูลนี้"); - } + const checkUsed = await AppDataSource.getRepository(ApiKey) + .createQueryBuilder("apiKey") + .innerJoin("apiKey.apiNames", "apiName") + .where("apiName.id = :id", { id }) + .getCount(); - const checkUsed = await AppDataSource.getRepository(ApiKey) - .createQueryBuilder("apiKey") - .innerJoin("apiKey.apiNames", "apiName") - .where("apiName.id = :id", { id }) - .getCount(); - - if (checkUsed > 0) { - throw new HttpError( - HttpStatusCode.BAD_REQUEST, - "ไม่สามารถลบ API นี้ได้ เนื่องจากมีการใช้งานอยู่", - ); - } - - await queryRunner.manager.getRepository(ApiAttribute).delete({ apiNameId: id }); - await queryRunner.manager.getRepository(ApiName).delete({ id }); - - await queryRunner.commitTransaction(); - return new HttpSuccess(); - } catch (transactionError) { - await queryRunner.rollbackTransaction(); - throw transactionError; - } finally { - await queryRunner.release(); + if (checkUsed > 0) { + throw new HttpError( + HttpStatusCode.BAD_REQUEST, + "ไม่สามารถลบ API นี้ได้ เนื่องจากมีการใช้งานอยู่", + ); } + + await queryRunner.manager.getRepository(ApiAttribute).delete({ apiNameId: id }); + await queryRunner.manager.getRepository(ApiName).delete({ id }); + + await queryRunner.commitTransaction(); + return new HttpSuccess(); } catch (error) { + await queryRunner.rollbackTransaction(); throw new HttpError( HttpStatusCode.INTERNAL_SERVER_ERROR, (error instanceof Error ? error.message : String(error)) || "เกิดข้อผิดพลาด ไม่สามารถบันทึกข้อมูลได้ กรุณาลองใหม่ในภายหลัง", ); + } finally { + await queryRunner.release(); } } @@ -641,94 +677,94 @@ export class ApiManageController extends Controller { * @summary รายการ systems */ @Get("/manual/swagger") - async getManualRequestWebService() { - const json: any = {}; - // create swagger documentation for manual request only ApiWebServiceController - json["openapi"] = "3.0.0"; - json["info"] = { - title: "Request Web Service", - version: "1.0.0", - description: "This is a manual request web service.", - }; - json["servers"] = [ - { - url: process.env.API_URL?.replace("/api/v1", "") || "http://localhost:13009", + async getManualRequestWebService(): Promise { + const json = { + openapi: "3.0.0", + info: { + title: "Request Web Service", + version: "1.0.0", + description: "This is a manual request web service.", }, - { - url: "http://localhost:13009", - description: "Local server", - }, - ]; - json["paths"] = { - "/api/v2/org/api-service/{system}/{code}": { - get: { - summary: "Get Registry Data", - parameters: [ - { - name: "system", - in: "path", - required: true, - schema: { - type: "string", - enum: ["registry", "registry_emp", "registry_temp", "organization"], + servers: [ + { + url: process.env.API_URL?.replace("/api/v1", "") || "http://localhost:13009", + }, + { + url: "http://localhost:13009", + description: "Local server", + }, + ], + paths: { + "/api/v1/org/api-service/{system}/{code}": { + get: { + summary: "Get Registry Data", + parameters: [ + { + name: "system", + in: "path", + required: true, + schema: { + type: "string", + enum: ["registry", "registry_emp", "registry_temp", "organization", "position"], + }, }, - }, - { - name: "code", - in: "path", - required: true, - schema: { - type: "string", + { + name: "code", + in: "path", + required: true, + schema: { + type: "string", + }, }, - }, - { - name: "page", - in: "query", - required: false, - schema: { - type: "integer", - default: 1, + { + name: "page", + in: "query", + required: false, + schema: { + type: "integer", + default: 1, + }, }, - }, - { - name: "pageSize", - in: "query", - required: false, - schema: { - type: "integer", - default: 100, + { + name: "pageSize", + in: "query", + required: false, + schema: { + type: "integer", + default: 100, + }, + }, + ], + responses: { + 200: { + description: "Successful response", + }, + 400: { + description: "Bad request", + }, + 500: { + description: "Internal server error", }, - }, - ], - responses: { - 200: { - description: "Successful response", - }, - 400: { - description: "Bad request", - }, - 500: { - description: "Internal server error", }, }, }, }, - }; - - json["components"] = { - securitySchemes: { - ApiKeyAuth: { - type: "apiKey", - in: "header", - name: "X-API-Key", + components: { + securitySchemes: { + ApiKeyAuth: { + type: "apiKey", + in: "header", + name: "X-API-Key", + }, }, }, + security: [ + { + ApiKeyAuth: [], + }, + ], }; - json["security"] = [ - { - ApiKeyAuth: [], - }, - ]; + return new HttpSuccess(json); } } diff --git a/src/controllers/ApiWebServiceController.ts b/src/controllers/ApiWebServiceController.ts index 124f0914..672d3169 100644 --- a/src/controllers/ApiWebServiceController.ts +++ b/src/controllers/ApiWebServiceController.ts @@ -8,7 +8,8 @@ import { isPermissionRequest } from "../middlewares/authWebService"; import { RequestWithUserWebService } from "../middlewares/user"; import { OrgRevision } from "../entities/OrgRevision"; import { ApiHistory } from "../entities/ApiHistory"; -@Route("api/v2/org/api-service") +import { SystemCode } from "./../interfaces/api-type"; +@Route("api/v1/org/api-service") @Tags("ApiKey") @Security("webServiceAuth") @Response( @@ -27,7 +28,8 @@ export class ApiWebServiceController extends Controller { @Get("/:system/:code") async listAttribute( @Request() request: RequestWithUserWebService, - @Path("system") system: "registry" | "registry_emp" | "registry_temp" | "organization", + @Path("system") + system: SystemCode, @Path("code") code: string, @Query("page") page: number = 1, @Query("pageSize") pageSize: number = 100, @@ -50,18 +52,18 @@ export class ApiWebServiceController extends Controller { const offset = (page - 1) * pageSize; const propertyKey = apiName.apiAttributes.map((attr) => `${attr.tbName}.${attr.propertyKey}`); - let Main: string = ""; + let tbMain: string = ""; let condition: string = "1=1"; if (system == "registry") { - Main = "Profile"; + tbMain = "Profile"; } else if (system == "registry_emp") { - Main = "ProfileEmployee"; + tbMain = "ProfileEmployee"; condition = `ProfileEmployee.employeeClass = "PERM"`; } else if (system == "registry_temp") { - Main = "ProfileEmployee"; + tbMain = "ProfileEmployee"; condition = `ProfileEmployee.employeeClass = "TEMP"`; } else { - Main = "OrgRoot"; + tbMain = "OrgRoot"; const revision = await this.orgRevisionRepository.findOne({ select: ["id"], where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, @@ -69,7 +71,7 @@ export class ApiWebServiceController extends Controller { condition = `OrgRoot.orgRevisionId = "${revision?.id}"`; } - const repo = AppDataSource.getRepository(Main); + const repo = AppDataSource.getRepository(tbMain); const metadata = repo.metadata; const relationMap: Record = {}; @@ -80,17 +82,17 @@ export class ApiWebServiceController extends Controller { // ดึงเฉพาะตารางรอง (ถ้าเลือกไว้) let propertyOtherKey: any[] = []; propertyOtherKey = [ - ...new Set(propertyKey.map((x) => x.split(".")[0]).filter((tb) => tb !== Main)), + ...new Set(propertyKey.map((x) => x.split(".")[0]).filter((tb) => tb !== tbMain)), ]; - const queryBuilder = repo.createQueryBuilder(Main); + const queryBuilder = repo.createQueryBuilder(tbMain); // join กับตารารอง if (propertyOtherKey.length > 0) { propertyOtherKey.forEach((tb) => { const relationName = relationMap[tb]; if (relationName) { - queryBuilder.leftJoin(`${Main}.${relationName}`, tb); + queryBuilder.leftJoin(`${tbMain}.${relationName}`, tb); } }); } @@ -105,8 +107,8 @@ export class ApiWebServiceController extends Controller { const primaryColumns = metadata.primaryColumns; primaryColumns.forEach((col) => { pk = col.propertyName; - if (!propertyKey.includes(`${Main}.${pk}`)) { - propertyKey.push(`${Main}.${pk}`); + if (!propertyKey.includes(`${tbMain}.${pk}`)) { + propertyKey.push(`${tbMain}.${pk}`); } }); @@ -133,7 +135,7 @@ export class ApiWebServiceController extends Controller { return x; }); - // console.log("queryBuilder ===> ", queryBuilder.getQuery()); + console.log("queryBuilder ===> ", queryBuilder.getQuery()); // save api history after query success const history = { diff --git a/src/interfaces/api-type.ts b/src/interfaces/api-type.ts new file mode 100644 index 00000000..0ddff375 --- /dev/null +++ b/src/interfaces/api-type.ts @@ -0,0 +1,16 @@ +type SystemCode = "registry" | "registry_emp" | "registry_temp" | "organization" | "position"; + +interface SystemDefinition { + code: SystemCode; + name: string; +} + +interface EntityDefinition { + name: string; + repository: any; + description: string; + isMain?: boolean; + system: SystemCode[]; +} + +export { SystemCode, SystemDefinition, EntityDefinition }; From 6563fa825d959c8022f7cb30b5a3eac07b7d446f Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Thu, 14 Aug 2025 12:20:59 +0700 Subject: [PATCH 12/13] api web service add position system --- src/controllers/ApiManageController.ts | 14 ++++++- src/controllers/ApiWebServiceController.ts | 45 ++++++++++++++++++++-- 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/src/controllers/ApiManageController.ts b/src/controllers/ApiManageController.ts index bc96af1a..a84c122e 100644 --- a/src/controllers/ApiManageController.ts +++ b/src/controllers/ApiManageController.ts @@ -104,7 +104,11 @@ export class ApiManageController extends Controller { }, { code: "organization", - name: "ข้อมูลโครงสร้างและอัตรากำลัง", + name: "ข้อมูลโครงสร้าง", + }, + { + code: "position", + name: "ข้อมูลอัตรากำลัง", }, ]; @@ -277,7 +281,7 @@ export class ApiManageController extends Controller { system: ["position"], }, { - name: "position", + name: "Position", repository: this.positionRepository, description: "ข้อมูลตำแหน่ง", system: ["position"], @@ -312,6 +316,12 @@ export class ApiManageController extends Controller { description: "ข้อมูลส่วนราชการ ระดับที่ 4", system: ["position"], }, + { + name: "Profile", + repository: this.profileRepository, + description: "ข้อมูลคนครอง", + system: ["position"], + }, ]; private readonly DEFAULT_PAGE_SIZE = 10; // ขนาดหน้าเริ่มต้น diff --git a/src/controllers/ApiWebServiceController.ts b/src/controllers/ApiWebServiceController.ts index 672d3169..267922c7 100644 --- a/src/controllers/ApiWebServiceController.ts +++ b/src/controllers/ApiWebServiceController.ts @@ -62,13 +62,20 @@ export class ApiWebServiceController extends Controller { } else if (system == "registry_temp") { tbMain = "ProfileEmployee"; condition = `ProfileEmployee.employeeClass = "TEMP"`; - } else { + } else if (system == "organization") { tbMain = "OrgRoot"; const revision = await this.orgRevisionRepository.findOne({ select: ["id"], where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, }); condition = `OrgRoot.orgRevisionId = "${revision?.id}"`; + } else if (system == "position") { + tbMain = "PosMaster"; + const revision = await this.orgRevisionRepository.findOne({ + select: ["id"], + where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, + }); + condition = `PosMaster.orgRevisionId = "${revision?.id}"`; } const repo = AppDataSource.getRepository(tbMain); @@ -92,7 +99,10 @@ export class ApiWebServiceController extends Controller { propertyOtherKey.forEach((tb) => { const relationName = relationMap[tb]; if (relationName) { - queryBuilder.leftJoin(`${tbMain}.${relationName}`, tb); + queryBuilder.leftJoin( + `${tbMain}.${relationName === "next_holder" ? "current_holder" : relationName}`, // เช็คว่าถ้าเป็น next_holder ให้ใช้ current_holder แทน + tb, + ); } }); } @@ -135,7 +145,7 @@ export class ApiWebServiceController extends Controller { return x; }); - console.log("queryBuilder ===> ", queryBuilder.getQuery()); + // console.log("queryBuilder ===> ", queryBuilder.getQuery()); // save api history after query success const history = { @@ -158,6 +168,33 @@ export class ApiWebServiceController extends Controller { lastUpdateFullName: request.user.name, }; await this.apiHistoryRepository.save(history); - return new HttpSuccess({ data, total }); + + const results = data.map((item) => { + const flattenedItem: any = {}; + + // Extract nested object properties to top level + Object.keys(item).forEach((key) => { + const value = item[key]; + if (value && typeof value === "object") { + // if (Array.isArray(value) && value.length === 1) { + // // If array has single item, extract it as object + // Object.assign(flattenedItem, value[0]); + // } else + if (!Array.isArray(value)) { + // Merge nested object properties to top level + Object.assign(flattenedItem, value); + } else { + // Keep arrays with multiple items or empty arrays as is + flattenedItem[key] = value; + } + } else { + // Keep primitive values as is + flattenedItem[key] = value; + } + }); + + return flattenedItem; + }); + return new HttpSuccess({ data: results, total }); } } From c382fe3ba7e5b736789db1d7b68c5eedc4f99131 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Thu, 14 Aug 2025 12:44:38 +0700 Subject: [PATCH 13/13] fix bug query default value system --- src/controllers/ApiManageController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/ApiManageController.ts b/src/controllers/ApiManageController.ts index a84c122e..01a27eb5 100644 --- a/src/controllers/ApiManageController.ts +++ b/src/controllers/ApiManageController.ts @@ -401,7 +401,7 @@ export class ApiManageController extends Controller { @Query("page") page: number = 1, @Query("pageSize") pageSize: number = this.DEFAULT_PAGE_SIZE, @Query("keyword") keyword: string = "", - @Query("system") system: SystemCode | undefined = undefined, + @Query("system") system: SystemCode | "" = "", @Query("isActive") isActive: boolean = true, ): Promise { try {