Merge branch 'develop' into adiDev
# Conflicts: # src/controllers/PositionController.ts
This commit is contained in:
commit
59a0d01f98
6 changed files with 721 additions and 530 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -1,25 +1,15 @@
|
|||
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";
|
||||
@Route("api/v2/org/api-service")
|
||||
import { OrgRevision } from "../entities/OrgRevision";
|
||||
import { ApiHistory } from "../entities/ApiHistory";
|
||||
import { SystemCode } from "./../interfaces/api-type";
|
||||
@Route("api/v1/org/api-service")
|
||||
@Tags("ApiKey")
|
||||
@Security("webServiceAuth")
|
||||
@Response(
|
||||
|
|
@ -28,6 +18,8 @@ import { RequestWithUserWebService } from "../middlewares/user";
|
|||
)
|
||||
export class ApiWebServiceController extends Controller {
|
||||
private apiNameRepository = AppDataSource.getRepository(ApiName);
|
||||
private orgRevisionRepository = AppDataSource.getRepository(OrgRevision);
|
||||
private apiHistoryRepository = AppDataSource.getRepository(ApiHistory);
|
||||
|
||||
/**
|
||||
* list fields by systems
|
||||
|
|
@ -36,47 +28,173 @@ 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,
|
||||
): Promise<HttpSuccess | HttpError> {
|
||||
// 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 tbMain: string = "";
|
||||
let condition: string = "1=1";
|
||||
if (system == "registry") {
|
||||
tbMain = "Profile";
|
||||
} else if (system == "registry_emp") {
|
||||
tbMain = "ProfileEmployee";
|
||||
condition = `ProfileEmployee.employeeClass = "PERM"`;
|
||||
} else if (system == "registry_temp") {
|
||||
tbMain = "ProfileEmployee";
|
||||
condition = `ProfileEmployee.employeeClass = "TEMP"`;
|
||||
} 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);
|
||||
const metadata = repo.metadata;
|
||||
|
||||
const relationMap: Record<string, string> = {};
|
||||
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 !== tbMain)),
|
||||
];
|
||||
|
||||
const queryBuilder = repo.createQueryBuilder(tbMain);
|
||||
|
||||
// join กับตารารอง
|
||||
if (propertyOtherKey.length > 0) {
|
||||
propertyOtherKey.forEach((tb) => {
|
||||
const relationName = relationMap[tb];
|
||||
if (relationName) {
|
||||
queryBuilder.leftJoin(
|
||||
`${tbMain}.${relationName === "next_holder" ? "current_holder" : relationName}`, // เช็คว่าถ้าเป็น next_holder ให้ใช้ current_holder แทน
|
||||
tb,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// // เพิ่ม 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(`${tbMain}.${pk}`)) {
|
||||
propertyKey.push(`${tbMain}.${pk}`);
|
||||
}
|
||||
});
|
||||
|
||||
const [items, total] = await queryBuilder
|
||||
.select(propertyKey)
|
||||
.where(condition)
|
||||
.orderBy(propertyKey[0], "ASC")
|
||||
.skip(offset)
|
||||
.take(pageSize)
|
||||
.getManyAndCount();
|
||||
|
||||
// ลบ Main.id
|
||||
// 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;
|
||||
// });
|
||||
|
||||
// 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,
|
||||
"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);
|
||||
|
||||
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;
|
||||
}
|
||||
});
|
||||
|
||||
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}`);
|
||||
const queryBuilder = AppDataSource.getRepository(Profile)
|
||||
.createQueryBuilder("Profile")
|
||||
.select(propertyKey);
|
||||
|
||||
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 flattenedItem;
|
||||
});
|
||||
return new HttpSuccess({ data: results, total });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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 &&
|
||||
|
|
|
|||
|
|
@ -2650,70 +2650,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,
|
||||
// current_holderId: Not(IsNull()),
|
||||
},
|
||||
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 ย้ายอัตรากำลัง
|
||||
*
|
||||
|
|
|
|||
16
src/interfaces/api-type.ts
Normal file
16
src/interfaces/api-type.ts
Normal file
|
|
@ -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 };
|
||||
Loading…
Add table
Add a link
Reference in a new issue