cronjob ส่งข้อมูลผู้เกษียณไปให้ระบบพ้นราชการ #2330
All checks were successful
Build & Deploy on Dev / build (push) Successful in 1m17s

This commit is contained in:
harid 2026-05-05 16:43:47 +07:00
parent e6c3e80a3d
commit 93d4857ea1
4 changed files with 175 additions and 12 deletions

View file

@ -19,6 +19,7 @@ import { ScriptProfileOrgController } from "./controllers/ScriptProfileOrgContro
import { DateSerializer } from "./interfaces/date-serializer";
import { initWebSocket } from "./services/webSocket";
import { RetirementService } from "./services/RetirementService";
async function main() {
await AppDataSource.initialize();
@ -114,6 +115,17 @@ async function main() {
}
});
// Cron job for posting retirement data to Exprofile - every day at 04:30:00 on the 1st of October
const cronTime_PostRetire = "0 30 4 1 10 *";
cron.schedule(cronTime_PostRetire, async () => {
try {
const retirementService = new RetirementService();
await retirementService.cronjobPostRetireToExprofile();
} catch (error) {
console.error("[Cronjob] Error executing cronjobPostRetireToExprofile:", error);
}
});
// app.listen(APP_PORT, APP_HOST, () => console.log(`Listening on: http://localhost:${APP_PORT}`));
const server = app.listen(
APP_PORT,

View file

@ -104,6 +104,7 @@ import { PostRetireToExprofile } from "./ExRetirementController";
import { LeaveType } from "../entities/LeaveType";
import { KeycloakAttributeService } from "../services/KeycloakAttributeService";
import { reOrderCommandRecivesAndDelete } from "../services/CommandService";
import { RetirementService } from "../services/RetirementService";
@Route("api/v1/org/command")
@Tags("Command")
@Security("bearerAuth")
@ -1608,8 +1609,7 @@ export class CommandController extends Controller {
return new HttpSuccess();
}
// @Get("XXX")
async cronjobUpdateRetirementStatus(/*@Request() request: RequestWithUser*/) {
async cronjobUpdateRetirementStatus() {
const adminToken = (await getToken()) ?? "";
const today = new Date();
today.setUTCHours(0, 0, 0, 0);
@ -1887,6 +1887,21 @@ export class CommandController extends Controller {
return new HttpSuccess();
}
/**
* API cronjobPostRetireToExprofile
* @summary (Exprofile)
*/
@Get("cronjob/cronjobPostRetireToExprofile")
async runCronjobPostRetireToExprofile() {
try {
const retirementService = new RetirementService();
const result = await retirementService.cronjobPostRetireToExprofile();
return new HttpSuccess(result);
} catch (error: any) {
throw new HttpError(HttpStatusCode.INTERNAL_SERVER_ERROR, error.message || "เกิดข้อผิดพลาด");
}
}
/**
* API tab4
*

View file

@ -237,16 +237,19 @@ export async function PostRetireToExprofile(
continue;
}
addLogSequence(request, {
action: "request",
status: "error",
description: "unconnected to exprofile api",
request: {
method: "POST",
url: API_URL_BANGKOK + "/importData",
response: JSON.stringify(error),
},
});
// เช็ค request ก่อนเรียก addLogSequence (สำหรับ cronjob ที่ส่ง null)
if (request) {
addLogSequence(request, {
action: "request",
status: "error",
description: "unconnected to exprofile api",
request: {
method: "POST",
url: API_URL_BANGKOK + "/importData",
response: JSON.stringify(error),
},
});
}
throw new HttpError(HttpStatusCode.INTERNAL_SERVER_ERROR, "ไม่สามารถติดต่อ API ได้");
}

View file

@ -0,0 +1,133 @@
import { AppDataSource } from "../database/data-source";
import { Profile } from "../entities/Profile";
import { PostRetireToExprofile } from "../controllers/ExRetirementController";
import { Between, MoreThanOrEqual } from "typeorm";
const BATCH_SIZE = 100;
const CONCURRENT_PER_BATCH = 10; // ส่ง parallel ทีละ 10 คนในแต่ละ batch
export class RetirementService {
private profileRepository = AppDataSource.getRepository(Profile);
/**
* Cronjob (Exprofile)
* 04:30:00 1
*
* :
* - Query profiles leaveDate = 1 leaveType = "RETIRE"
* - Batch 100 records
* - Concurrent 10 (parallel) batch
* - fail log error
*/
async cronjobPostRetireToExprofile(): Promise<{
success: number;
failed: number;
failedProfiles: Array<{ id: string; name: string; error: string }>;
}> {
const result = {
success: 0,
failed: 0,
failedProfiles: [] as Array<{ id: string; name: string; error: string }>,
};
try {
// หาวันที่ 1 ตุลาคมของปีปัจจุบัน
const now = new Date();
const currentYear = now.getFullYear();
// สร้างวันที่ 1 ตุลาคมของปีปัจจุบัน (เวลา 00:00:00)
const startDate = new Date(currentYear, 9, 1, 0, 0, 0); // Month 9 = October (0-indexed)
const endDate = new Date(currentYear, 9, 1, 23, 59, 59);
// Query profiles ที่ leaveDate อยู่ในวันที่ 1 ตุลาคม และ leaveType = "RETIRE"
const profiles = await this.profileRepository.find({
where: [
{ leaveDate: Between(startDate, endDate), leaveType: "RETIRE" as any },
{ leaveDate: MoreThanOrEqual(startDate), leaveType: "RETIRE" as any },
],
relations: ["posLevel", "posType"],
});
// Filter เอาเฉพาะวันที่ 1 ตุลาคมเท่านั้น
const filteredProfiles = profiles.filter(p => {
if (!p.leaveDate) return false;
const leaveDate = new Date(p.leaveDate);
return (
leaveDate.getFullYear() === currentYear &&
leaveDate.getMonth() === 9 && // October
leaveDate.getDate() === 1
);
});
if (filteredProfiles.length === 0) {
return result;
}
// แบ่ง batch ทีละ 100 records
for (let i = 0; i < filteredProfiles.length; i += BATCH_SIZE) {
const batch = filteredProfiles.slice(i, i + BATCH_SIZE);
// แบ่งเป็น chunk เล็กๆ ทีละ CONCURRENT_PER_BATCH เพื่อส่ง parallel
for (let j = 0; j < batch.length; j += CONCURRENT_PER_BATCH) {
const chunk = batch.slice(j, j + CONCURRENT_PER_BATCH);
// ส่ง parallel ในแต่ละ chunk
await Promise.all(
chunk.map(async (profile) => {
try {
await this.postSingleProfileToExprofile(profile);
result.success++;
} catch (error: any) {
result.failed++;
const errorInfo = {
id: profile.id,
name: `${profile.prefix}${profile.firstName} ${profile.lastName}`,
error: error.message || String(error),
};
result.failedProfiles.push(errorInfo);
}
})
);
}
}
} catch (error: any) {
throw error;
}
return result;
}
/**
* profile Exprofile
*/
private async postSingleProfileToExprofile(profile: Profile): Promise<void> {
if (!profile.leaveDate) {
return;
}
if (!profile.citizenId) {
return;
}
const retireYear = profile.leaveDate.getFullYear();
const retireDate = new Date(profile.leaveDate);
// ส่งไปยัง Exprofile
PostRetireToExprofile(
null,
profile.citizenId,
profile.prefix || "",
profile.firstName || "",
profile.lastName || "",
retireYear.toString(),
profile.position || "",
profile.posType?.posTypeName || "",
profile.posLevel?.posLevelName || "",
retireDate,
profile.org || "",
profile.leaveReason || "เกษียณอายุราชการ"
);
}
}