Compare commits
No commits in common. "dev" and "v1.0.213" have entirely different histories.
3 changed files with 19 additions and 145 deletions
|
|
@ -23,7 +23,7 @@ interface CachedToken {
|
||||||
}
|
}
|
||||||
const API_URL_BANGKOK = "https://exprofile.bangkok.go.th/API";
|
const API_URL_BANGKOK = "https://exprofile.bangkok.go.th/API";
|
||||||
const clientId = "e5f6ad6ce374177eef023bf5d0c018b6";
|
const clientId = "e5f6ad6ce374177eef023bf5d0c018b6";
|
||||||
const clientSecret = "5EhOvN5DwHOKakupqT9FmCk7MOwpT3zLqLPkPh4ZhJpxBN2nMG";
|
const clientSecret = "5EhOvN5DwHOKakupqT9FmCk7MOwpT3zLqLPkPh4ZhJpxBN2nMG@2022";
|
||||||
|
|
||||||
class TokenCache {
|
class TokenCache {
|
||||||
private static cache: Map<string, CachedToken> = new Map();
|
private static cache: Map<string, CachedToken> = new Map();
|
||||||
|
|
@ -43,35 +43,6 @@ class TokenCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// network/DNS error codes ที่ถือว่าเป็น transient (ควร retry)
|
|
||||||
const TRANSIENT_NETWORK_CODES = [
|
|
||||||
"EAI_AGAIN", // DNS lookup ล้มเหลวชั่วคราว
|
|
||||||
"ENOTFOUND",
|
|
||||||
"ECONNRESET",
|
|
||||||
"ETIMEDOUT",
|
|
||||||
"ECONNREFUSED",
|
|
||||||
"EHOSTUNREACH",
|
|
||||||
"ENETUNREACH",
|
|
||||||
];
|
|
||||||
|
|
||||||
// ตรวจว่า error ควร retry: ครอบ network/DNS (ไม่มี response หรือ code ตรง TRANSIENT) และ HTTP 5xx
|
|
||||||
function isTransientError(error: any): boolean {
|
|
||||||
if (!error) return false;
|
|
||||||
if (!error.response || TRANSIENT_NETWORK_CODES.includes(error.code)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return error.response?.status >= 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
// หน่วงเวลา (backoff) ระหว่าง retry — ใช้รูปแบบ setTimeout เดียวกับ keycloak/index.ts
|
|
||||||
function sleep(ms: number): Promise<void> {
|
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
||||||
}
|
|
||||||
|
|
||||||
// config retry สำหรับเรียก Exprofile
|
|
||||||
const EXPROFILE_MAX_RETRIES = 3; // จากเดิม 2
|
|
||||||
const EXPROFILE_BACKOFF_BASE_MS = 1000; // backoff = base * 2^retryCount → 1s, 2s
|
|
||||||
|
|
||||||
@Route("api/v1/org/ex/retirement")
|
@Route("api/v1/org/ex/retirement")
|
||||||
@Tags("ExRetirement")
|
@Tags("ExRetirement")
|
||||||
@Security("bearerAuth")
|
@Security("bearerAuth")
|
||||||
|
|
@ -89,7 +60,7 @@ export class ExRetirementController extends Controller {
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
let retryCount = 0;
|
let retryCount = 0;
|
||||||
const maxRetries = EXPROFILE_MAX_RETRIES;
|
const maxRetries = 2;
|
||||||
|
|
||||||
while (retryCount < maxRetries) {
|
while (retryCount < maxRetries) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -121,14 +92,11 @@ export class ExRetirementController extends Controller {
|
||||||
// return res.data;
|
// return res.data;
|
||||||
return new HttpSuccess(res.data.data);
|
return new HttpSuccess(res.data.data);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (isTransientError(error) && retryCount < maxRetries - 1) {
|
if (error.response?.status === 500 && retryCount < maxRetries - 1) {
|
||||||
TokenCache.delete(`${clientId}:${clientSecret}`);
|
TokenCache.delete(`${clientId}:${clientSecret}`);
|
||||||
await sleep(EXPROFILE_BACKOFF_BASE_MS * 2 ** retryCount);
|
|
||||||
retryCount++;
|
retryCount++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// log error message
|
|
||||||
console.error('getData getOfficerRetireData error:', error);
|
|
||||||
throw new HttpError(HttpStatusCode.INTERNAL_SERVER_ERROR, "ไม่สามารถติดต่อ API ได้");
|
throw new HttpError(HttpStatusCode.INTERNAL_SERVER_ERROR, "ไม่สามารถติดต่อ API ได้");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -137,7 +105,7 @@ export class ExRetirementController extends Controller {
|
||||||
@Get("/document/{documentId}")
|
@Get("/document/{documentId}")
|
||||||
async getDocument(@Path("documentId") officerDocumentID: string, @Request() req: any) {
|
async getDocument(@Path("documentId") officerDocumentID: string, @Request() req: any) {
|
||||||
let retryCount = 0;
|
let retryCount = 0;
|
||||||
const maxRetries = EXPROFILE_MAX_RETRIES;
|
const maxRetries = 2;
|
||||||
while (retryCount < maxRetries) {
|
while (retryCount < maxRetries) {
|
||||||
try {
|
try {
|
||||||
const token = await getToken(clientId, clientSecret);
|
const token = await getToken(clientId, clientSecret);
|
||||||
|
|
@ -166,13 +134,11 @@ export class ExRetirementController extends Controller {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (isTransientError(error) && retryCount < maxRetries - 1) {
|
if (error.response?.status === 500 && retryCount < maxRetries - 1) {
|
||||||
TokenCache.delete(`${clientId}:${clientSecret}`);
|
TokenCache.delete(`${clientId}:${clientSecret}`);
|
||||||
await sleep(EXPROFILE_BACKOFF_BASE_MS * 2 ** retryCount);
|
|
||||||
retryCount++;
|
retryCount++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
console.error('getData document error:', error);
|
|
||||||
throw new HttpError(HttpStatusCode.INTERNAL_SERVER_ERROR, "ไม่สามารถติดต่อ API ได้");
|
throw new HttpError(HttpStatusCode.INTERNAL_SERVER_ERROR, "ไม่สามารถติดต่อ API ได้");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -202,13 +168,7 @@ async function getToken(ClientID: string, ClientSecret: string): Promise<string>
|
||||||
TokenCache.set(cacheKey, token);
|
TokenCache.set(cacheKey, token);
|
||||||
return token;
|
return token;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// log แบบกระชับ (ลด log noise จากการ dump object ใหญ่ตามที่เห็นใน log จริง)
|
return Promise.reject({ message: "Error occurred", error });
|
||||||
console.error(
|
|
||||||
"getToken error:",
|
|
||||||
error instanceof Error ? `${error.name}: ${error.message}` : error,
|
|
||||||
);
|
|
||||||
// โยน AxiosError ตัวจริงออกไปให้ caller อ่าน code / response / status เพื่อตัดสินใจ retry
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -234,7 +194,7 @@ export async function PostRetireToExprofile(
|
||||||
}
|
}
|
||||||
|
|
||||||
let retryCount = 0;
|
let retryCount = 0;
|
||||||
const maxRetries = EXPROFILE_MAX_RETRIES;
|
const maxRetries = 2;
|
||||||
|
|
||||||
while (retryCount < maxRetries) {
|
while (retryCount < maxRetries) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -271,9 +231,8 @@ export async function PostRetireToExprofile(
|
||||||
|
|
||||||
return res.data;
|
return res.data;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (isTransientError(error) && retryCount < maxRetries - 1) {
|
if (error.response?.status === 500 && retryCount < maxRetries - 1) {
|
||||||
TokenCache.delete(`${clientId}:${clientSecret}`);
|
TokenCache.delete(`${clientId}:${clientSecret}`);
|
||||||
await sleep(EXPROFILE_BACKOFF_BASE_MS * 2 ** retryCount);
|
|
||||||
retryCount++;
|
retryCount++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -292,7 +251,6 @@ export async function PostRetireToExprofile(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error('importOfficerRetireData error', error);
|
|
||||||
throw new HttpError(HttpStatusCode.INTERNAL_SERVER_ERROR, "ไม่สามารถติดต่อ API ได้");
|
throw new HttpError(HttpStatusCode.INTERNAL_SERVER_ERROR, "ไม่สามารถติดต่อ API ได้");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11451,10 +11451,11 @@ export class ProfileController extends Controller {
|
||||||
system?: string;
|
system?: string;
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
// ค้นหารายชื่อถ้าไม่ส่ง system มาให้ default ตามทะเบียนประวัติ
|
// comment ออกก่อนเพราะยังไม่ได้ใช้
|
||||||
let _system: string = "SYS_REGISTRY_OFFICER";
|
// // ค้นหารายชื่อถ้าไม่ส่ง system มาให้ default ตามทะเบียนประวัติ
|
||||||
if (body.system) _system = body.system;
|
// let _system: string = "SYS_REGISTRY_OFFICER";
|
||||||
let _data = await new permission().PermissionOrgList(request, _system);
|
// if (body.system) _system = body.system;
|
||||||
|
// let _data = await new permission().PermissionOrgList(request, _system);
|
||||||
const findRevision = await this.orgRevisionRepo.findOne({
|
const findRevision = await this.orgRevisionRepo.findOne({
|
||||||
where: { orgRevisionIsCurrent: true },
|
where: { orgRevisionIsCurrent: true },
|
||||||
});
|
});
|
||||||
|
|
@ -11499,50 +11500,10 @@ export class ProfileController extends Controller {
|
||||||
.leftJoinAndSelect("current_holders.orgChild2", "orgChild2")
|
.leftJoinAndSelect("current_holders.orgChild2", "orgChild2")
|
||||||
.leftJoinAndSelect("current_holders.orgChild3", "orgChild3")
|
.leftJoinAndSelect("current_holders.orgChild3", "orgChild3")
|
||||||
.leftJoinAndSelect("current_holders.orgChild4", "orgChild4")
|
.leftJoinAndSelect("current_holders.orgChild4", "orgChild4")
|
||||||
.where("profile.isActive = :isActive AND profile.isDelete = :isDelete", {
|
.where(body.system ? "profile.isActive = :isActive" : "profile.isDelete = :isDelete", {
|
||||||
isActive: true,
|
isActive: false,
|
||||||
isDelete: false,
|
isDelete: true,
|
||||||
})
|
})
|
||||||
.andWhere(
|
|
||||||
_data.root != undefined && _data.root != null
|
|
||||||
? _data.root[0] != null
|
|
||||||
? `current_holders.orgRootId IN (:...root)`
|
|
||||||
: `current_holders.orgRootId is null`
|
|
||||||
: "1=1",
|
|
||||||
{ root: _data.root },
|
|
||||||
)
|
|
||||||
.andWhere(
|
|
||||||
_data.child1 != undefined && _data.child1 != null
|
|
||||||
? _data.child1[0] != null
|
|
||||||
? `current_holders.orgChild1Id IN (:...child1)`
|
|
||||||
: `current_holders.orgChild1Id is null`
|
|
||||||
: "1=1",
|
|
||||||
{ child1: _data.child1 },
|
|
||||||
)
|
|
||||||
.andWhere(
|
|
||||||
_data.child2 != undefined && _data.child2 != null
|
|
||||||
? _data.child2[0] != null
|
|
||||||
? `current_holders.orgChild2Id IN (:...child2)`
|
|
||||||
: `current_holders.orgChild2Id is null`
|
|
||||||
: "1=1",
|
|
||||||
{ child2: _data.child2 },
|
|
||||||
)
|
|
||||||
.andWhere(
|
|
||||||
_data.child3 != undefined && _data.child3 != null
|
|
||||||
? _data.child3[0] != null
|
|
||||||
? `current_holders.orgChild3Id IN (:...child3)`
|
|
||||||
: `current_holders.orgChild3Id is null`
|
|
||||||
: "1=1",
|
|
||||||
{ child3: _data.child3 },
|
|
||||||
)
|
|
||||||
.andWhere(
|
|
||||||
_data.child4 != undefined && _data.child4 != null
|
|
||||||
? _data.child4[0] != null
|
|
||||||
? `current_holders.orgChild4Id IN (:...child4)`
|
|
||||||
: `current_holders.orgChild4Id is null`
|
|
||||||
: "1=1",
|
|
||||||
{ child4: _data.child4 },
|
|
||||||
)
|
|
||||||
.andWhere(
|
.andWhere(
|
||||||
new Brackets((qb) => {
|
new Brackets((qb) => {
|
||||||
qb.orWhere(body.keyword ? queryLike : "1=1", { keyword: `%${body.keyword}%` });
|
qb.orWhere(body.keyword ? queryLike : "1=1", { keyword: `%${body.keyword}%` });
|
||||||
|
|
|
||||||
|
|
@ -6172,7 +6172,6 @@ export class ProfileEmployeeController extends Controller {
|
||||||
*/
|
*/
|
||||||
@Post("search-personal-no-keycloak")
|
@Post("search-personal-no-keycloak")
|
||||||
async getProfileBySearchKeywordNoKeyCloak(
|
async getProfileBySearchKeywordNoKeyCloak(
|
||||||
@Request() request: RequestWithUser,
|
|
||||||
@Query("page") page: number = 1,
|
@Query("page") page: number = 1,
|
||||||
@Query("pageSize") pageSize: number = 10,
|
@Query("pageSize") pageSize: number = 10,
|
||||||
@Body()
|
@Body()
|
||||||
|
|
@ -6182,10 +6181,6 @@ export class ProfileEmployeeController extends Controller {
|
||||||
system?: string;
|
system?: string;
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
// ค้นหารายชื่อถ้าไม่ส่ง system มาให้ default ตามทะเบียนประวัติ
|
|
||||||
let _system: string = "SYS_REGISTRY_EMP";
|
|
||||||
if (body.system) _system = body.system;
|
|
||||||
let _data = await new permission().PermissionOrgList(request, _system);
|
|
||||||
const findRevision = await this.orgRevisionRepo.findOne({
|
const findRevision = await this.orgRevisionRepo.findOne({
|
||||||
where: { orgRevisionIsCurrent: true },
|
where: { orgRevisionIsCurrent: true },
|
||||||
});
|
});
|
||||||
|
|
@ -6230,50 +6225,10 @@ export class ProfileEmployeeController extends Controller {
|
||||||
.leftJoinAndSelect("current_holders.orgChild2", "orgChild2")
|
.leftJoinAndSelect("current_holders.orgChild2", "orgChild2")
|
||||||
.leftJoinAndSelect("current_holders.orgChild3", "orgChild3")
|
.leftJoinAndSelect("current_holders.orgChild3", "orgChild3")
|
||||||
.leftJoinAndSelect("current_holders.orgChild4", "orgChild4")
|
.leftJoinAndSelect("current_holders.orgChild4", "orgChild4")
|
||||||
.where("profile.isActive = :isActive AND profile.isDelete = :isDelete", {
|
.where(body.system ? "profile.isActive = :isActive" : "profile.isDelete = :isDelete", {
|
||||||
isActive: true,
|
isActive: false,
|
||||||
isDelete: false,
|
isDelete: true,
|
||||||
})
|
})
|
||||||
.andWhere(
|
|
||||||
_data.root != undefined && _data.root != null
|
|
||||||
? _data.root[0] != null
|
|
||||||
? `current_holders.orgRootId IN (:...root)`
|
|
||||||
: `current_holders.orgRootId is null`
|
|
||||||
: "1=1",
|
|
||||||
{ root: _data.root },
|
|
||||||
)
|
|
||||||
.andWhere(
|
|
||||||
_data.child1 != undefined && _data.child1 != null
|
|
||||||
? _data.child1[0] != null
|
|
||||||
? `current_holders.orgChild1Id IN (:...child1)`
|
|
||||||
: `current_holders.orgChild1Id is null`
|
|
||||||
: "1=1",
|
|
||||||
{ child1: _data.child1 },
|
|
||||||
)
|
|
||||||
.andWhere(
|
|
||||||
_data.child2 != undefined && _data.child2 != null
|
|
||||||
? _data.child2[0] != null
|
|
||||||
? `current_holders.orgChild2Id IN (:...child2)`
|
|
||||||
: `current_holders.orgChild2Id is null`
|
|
||||||
: "1=1",
|
|
||||||
{ child2: _data.child2 },
|
|
||||||
)
|
|
||||||
.andWhere(
|
|
||||||
_data.child3 != undefined && _data.child3 != null
|
|
||||||
? _data.child3[0] != null
|
|
||||||
? `current_holders.orgChild3Id IN (:...child3)`
|
|
||||||
: `current_holders.orgChild3Id is null`
|
|
||||||
: "1=1",
|
|
||||||
{ child3: _data.child3 },
|
|
||||||
)
|
|
||||||
.andWhere(
|
|
||||||
_data.child4 != undefined && _data.child4 != null
|
|
||||||
? _data.child4[0] != null
|
|
||||||
? `current_holders.orgChild4Id IN (:...child4)`
|
|
||||||
: `current_holders.orgChild4Id is null`
|
|
||||||
: "1=1",
|
|
||||||
{ child4: _data.child4 },
|
|
||||||
)
|
|
||||||
.andWhere(
|
.andWhere(
|
||||||
new Brackets((qb) => {
|
new Brackets((qb) => {
|
||||||
qb.orWhere(body.keyword ? queryLike : "1=1", { keyword: `%${body.keyword}%` });
|
qb.orWhere(body.keyword ? queryLike : "1=1", { keyword: `%${body.keyword}%` });
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue