2025-08-13 16:47:01 +07:00
|
|
|
import { Controller, Route, Security, Tags, Path, Request, Response, Get, Query } from "tsoa";
|
2025-08-07 13:05:58 +07:00
|
|
|
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";
|
2025-08-08 09:26:48 +07:00
|
|
|
import { isPermissionRequest } from "../middlewares/authWebService";
|
|
|
|
|
import { RequestWithUserWebService } from "../middlewares/user";
|
2025-08-08 17:14:29 +07:00
|
|
|
import { OrgRevision } from "../entities/OrgRevision";
|
2025-08-13 16:14:44 +07:00
|
|
|
import { ApiHistory } from "../entities/ApiHistory";
|
2025-08-14 11:17:13 +07:00
|
|
|
import { SystemCode } from "./../interfaces/api-type";
|
|
|
|
|
@Route("api/v1/org/api-service")
|
2025-08-07 13:05:58 +07:00
|
|
|
@Tags("ApiKey")
|
2025-08-08 09:26:48 +07:00
|
|
|
@Security("webServiceAuth")
|
2025-08-07 13:05:58 +07:00
|
|
|
@Response(
|
|
|
|
|
HttpStatusCode.INTERNAL_SERVER_ERROR,
|
|
|
|
|
"เกิดข้อผิดพลาด ไม่สามารถแสดงรายการได้ กรุณาลองใหม่ในภายหลัง",
|
|
|
|
|
)
|
|
|
|
|
export class ApiWebServiceController extends Controller {
|
|
|
|
|
private apiNameRepository = AppDataSource.getRepository(ApiName);
|
2025-08-08 17:14:29 +07:00
|
|
|
private orgRevisionRepository = AppDataSource.getRepository(OrgRevision);
|
2025-08-13 16:14:44 +07:00
|
|
|
private apiHistoryRepository = AppDataSource.getRepository(ApiHistory);
|
|
|
|
|
|
2025-08-07 13:05:58 +07:00
|
|
|
/**
|
|
|
|
|
* list fields by systems
|
|
|
|
|
* @summary รายการ fields ตาม systems
|
|
|
|
|
*/
|
|
|
|
|
@Get("/:system/:code")
|
|
|
|
|
async listAttribute(
|
2025-08-08 09:26:48 +07:00
|
|
|
@Request() request: RequestWithUserWebService,
|
2025-08-14 11:17:13 +07:00
|
|
|
@Path("system")
|
|
|
|
|
system: SystemCode,
|
2025-08-07 13:05:58 +07:00
|
|
|
@Path("code") code: string,
|
|
|
|
|
@Query("page") page: number = 1,
|
|
|
|
|
@Query("pageSize") pageSize: number = 100,
|
|
|
|
|
): Promise<HttpSuccess | HttpError> {
|
2025-08-08 17:14:29 +07:00
|
|
|
const apiName = await this.apiNameRepository.findOne({
|
|
|
|
|
where: { code },
|
|
|
|
|
select: ["id", "code", "methodApi", "system", "isActive"],
|
|
|
|
|
relations: ["apiAttributes"],
|
|
|
|
|
order: {
|
|
|
|
|
apiAttributes: {
|
|
|
|
|
ordering: "ASC",
|
2025-08-07 13:05:58 +07:00
|
|
|
},
|
2025-08-08 17:14:29 +07:00
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
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}`);
|
2025-08-07 13:05:58 +07:00
|
|
|
|
2025-08-14 11:17:13 +07:00
|
|
|
let tbMain: string = "";
|
2025-08-13 16:47:01 +07:00
|
|
|
let condition: string = "1=1";
|
2025-08-08 17:14:29 +07:00
|
|
|
if (system == "registry") {
|
2025-08-14 11:17:13 +07:00
|
|
|
tbMain = "Profile";
|
2025-08-13 16:47:01 +07:00
|
|
|
} else if (system == "registry_emp") {
|
2025-08-14 11:17:13 +07:00
|
|
|
tbMain = "ProfileEmployee";
|
2025-08-13 16:47:01 +07:00
|
|
|
condition = `ProfileEmployee.employeeClass = "PERM"`;
|
|
|
|
|
} else if (system == "registry_temp") {
|
2025-08-14 11:17:13 +07:00
|
|
|
tbMain = "ProfileEmployee";
|
2025-08-13 16:47:01 +07:00
|
|
|
condition = `ProfileEmployee.employeeClass = "TEMP"`;
|
2025-08-14 12:20:59 +07:00
|
|
|
} else if (system == "organization") {
|
2025-08-14 11:17:13 +07:00
|
|
|
tbMain = "OrgRoot";
|
2025-08-08 17:14:29 +07:00
|
|
|
const revision = await this.orgRevisionRepository.findOne({
|
2025-08-13 16:47:01 +07:00
|
|
|
select: ["id"],
|
|
|
|
|
where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false },
|
|
|
|
|
});
|
|
|
|
|
condition = `OrgRoot.orgRevisionId = "${revision?.id}"`;
|
2025-08-14 12:20:59 +07:00
|
|
|
} else if (system == "position") {
|
|
|
|
|
tbMain = "PosMaster";
|
|
|
|
|
const revision = await this.orgRevisionRepository.findOne({
|
|
|
|
|
select: ["id"],
|
|
|
|
|
where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false },
|
|
|
|
|
});
|
|
|
|
|
condition = `PosMaster.orgRevisionId = "${revision?.id}"`;
|
2025-08-08 17:14:29 +07:00
|
|
|
}
|
2025-08-13 11:45:34 +07:00
|
|
|
|
2025-08-14 11:17:13 +07:00
|
|
|
const repo = AppDataSource.getRepository(tbMain);
|
2025-08-08 17:14:29 +07:00
|
|
|
const metadata = repo.metadata;
|
2025-08-13 16:47:01 +07:00
|
|
|
|
2025-08-08 17:14:29 +07:00
|
|
|
const relationMap: Record<string, string> = {};
|
|
|
|
|
metadata.relations.forEach((rel) => {
|
|
|
|
|
relationMap[rel.inverseEntityMetadata.name] = rel.propertyName;
|
|
|
|
|
});
|
2025-08-13 16:47:01 +07:00
|
|
|
|
2025-08-08 17:14:29 +07:00
|
|
|
// ดึงเฉพาะตารางรอง (ถ้าเลือกไว้)
|
2025-08-13 16:47:01 +07:00
|
|
|
let propertyOtherKey: any[] = [];
|
2025-08-08 17:14:29 +07:00
|
|
|
propertyOtherKey = [
|
2025-08-14 11:17:13 +07:00
|
|
|
...new Set(propertyKey.map((x) => x.split(".")[0]).filter((tb) => tb !== tbMain)),
|
2025-08-13 16:47:01 +07:00
|
|
|
];
|
|
|
|
|
|
2025-08-14 11:17:13 +07:00
|
|
|
const queryBuilder = repo.createQueryBuilder(tbMain);
|
2025-08-07 13:05:58 +07:00
|
|
|
|
2025-08-08 17:14:29 +07:00
|
|
|
// join กับตารารอง
|
|
|
|
|
if (propertyOtherKey.length > 0) {
|
|
|
|
|
propertyOtherKey.forEach((tb) => {
|
|
|
|
|
const relationName = relationMap[tb];
|
|
|
|
|
if (relationName) {
|
2025-08-14 12:20:59 +07:00
|
|
|
queryBuilder.leftJoin(
|
|
|
|
|
`${tbMain}.${relationName === "next_holder" ? "current_holder" : relationName}`, // เช็คว่าถ้าเป็น next_holder ให้ใช้ current_holder แทน
|
|
|
|
|
tb,
|
|
|
|
|
);
|
2025-08-08 17:14:29 +07:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-08-13 16:47:01 +07:00
|
|
|
|
2025-08-13 16:14:44 +07:00
|
|
|
// // เพิ่ม Main.id เพราะจะใช้ pk ในการแมบและนับจำนวน
|
|
|
|
|
// if (!propertyKey.includes(`${Main}.id`)) {
|
|
|
|
|
// propertyKey.push(`${Main}.id`);
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// add FK
|
2025-08-13 16:47:01 +07:00
|
|
|
let pk: string = "";
|
2025-08-13 16:14:44 +07:00
|
|
|
const primaryColumns = metadata.primaryColumns;
|
2025-08-13 16:47:01 +07:00
|
|
|
primaryColumns.forEach((col) => {
|
2025-08-13 16:14:44 +07:00
|
|
|
pk = col.propertyName;
|
2025-08-14 11:17:13 +07:00
|
|
|
if (!propertyKey.includes(`${tbMain}.${pk}`)) {
|
|
|
|
|
propertyKey.push(`${tbMain}.${pk}`);
|
2025-08-13 16:14:44 +07:00
|
|
|
}
|
|
|
|
|
});
|
2025-08-13 11:45:34 +07:00
|
|
|
|
2025-08-08 17:14:29 +07:00
|
|
|
const [items, total] = await queryBuilder
|
2025-08-13 11:45:34 +07:00
|
|
|
.select(propertyKey)
|
2025-08-08 17:14:29 +07:00
|
|
|
.where(condition)
|
2025-08-13 16:14:44 +07:00
|
|
|
.orderBy(propertyKey[0], "ASC")
|
2025-08-08 17:14:29 +07:00
|
|
|
.skip(offset)
|
|
|
|
|
.take(pageSize)
|
|
|
|
|
.getManyAndCount();
|
|
|
|
|
|
2025-08-13 11:45:34 +07:00
|
|
|
// ลบ Main.id
|
2025-08-13 16:14:44 +07:00
|
|
|
// 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;
|
|
|
|
|
// });
|
|
|
|
|
|
2025-08-13 16:47:01 +07:00
|
|
|
// split object id ออกก่อน return
|
|
|
|
|
const data = items.map((item) => {
|
|
|
|
|
const { [pk]: removedPk, ...x } = item;
|
|
|
|
|
return x;
|
|
|
|
|
});
|
|
|
|
|
|
2025-08-14 12:20:59 +07:00
|
|
|
// console.log("queryBuilder ===> ", queryBuilder.getQuery());
|
2025-08-13 16:47:01 +07:00
|
|
|
|
|
|
|
|
// save api history after query success
|
2025-08-13 16:14:44 +07:00
|
|
|
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);
|
2025-08-14 12:20:59 +07:00
|
|
|
|
|
|
|
|
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 });
|
2025-08-07 13:05:58 +07:00
|
|
|
}
|
|
|
|
|
}
|