summaryrefslogtreecommitdiff
path: root/lib/knox-sync/title-sync-service.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/knox-sync/title-sync-service.ts')
-rw-r--r--lib/knox-sync/title-sync-service.ts87
1 files changed, 60 insertions, 27 deletions
diff --git a/lib/knox-sync/title-sync-service.ts b/lib/knox-sync/title-sync-service.ts
index e7bc13bd..5a8ce42a 100644
--- a/lib/knox-sync/title-sync-service.ts
+++ b/lib/knox-sync/title-sync-service.ts
@@ -4,17 +4,23 @@ import * as cron from 'node-cron';
import db from '@/db/db';
import { title as titleTable } from '@/db/schema/knox/titles';
import { getTitlesByCompany, Title } from '@/lib/knox-api/employee/employee';
-import { sql } from 'drizzle-orm';
-
-const COMPANIES = (process.env.KNOX_COMPANY_CODES || 'P2')
- .split(',')
- .map((c) => c.trim())
- .filter(Boolean);
+import { eq } from 'drizzle-orm';
+import {
+ KNOX_COMPANIES,
+ delay,
+ API_CALL_DELAY_MS,
+ checkTimeRestriction,
+ checkApiLimitStatus,
+ initializeApiTracker,
+ logSyncStart,
+ logSyncComplete,
+ logSchedulerInfo,
+} from './common';
const CRON_STRING = process.env.KNOX_TITLE_SYNC_CRON || '30 4 * * *';
const DO_FIRST_RUN = process.env.KNOX_TITLE_SYNC_FIRST_RUN === 'true';
-async function upsertTitles(titles: Title[]) {
+async function insertTitles(titles: Title[]) {
if (!titles.length) return;
const rows = titles.map((t) => ({
@@ -26,39 +32,66 @@ async function upsertTitles(titles: Title[]) {
raw: t as unknown as Record<string, unknown>,
}));
- await db
- .insert(titleTable)
- .values(rows)
- .onConflictDoUpdate({
- target: [titleTable.companyCode, titleTable.titleCode],
- set: {
- titleName: sql.raw('excluded.title_name'),
- enTitleName: sql.raw('excluded.en_title_name'),
- sortOrder: sql.raw('excluded.sort_order'),
- raw: sql.raw('excluded.raw'),
- updatedAt: sql.raw('CURRENT_TIMESTAMP'),
- },
- });
+ await db.insert(titleTable).values(rows);
+}
+
+/**
+ * 회사별 기존 직급 데이터 삭제
+ */
+async function deleteExistingTitles(companyCode: string): Promise<number> {
+ const result = await db
+ .delete(titleTable)
+ .where(eq(titleTable.companyCode, companyCode));
+
+ const deletedCount = result.rowCount || 0;
+ console.log(`[KNOX-SYNC] ${companyCode}: 기존 직급 데이터 ${deletedCount}건 삭제 완료`);
+ return deletedCount;
}
export async function syncKnoxTitles(): Promise<void> {
- console.log('[KNOX-SYNC] 직급 동기화 시작');
+ logSyncStart('직급');
+ initializeApiTracker(); // API 추적기 초기화
+
+ let totalApiCalls = 0;
+ const startTime = Date.now();
+ let currentDelay = API_CALL_DELAY_MS;
- for (const companyCode of COMPANIES) {
+ for (const companyCode of KNOX_COMPANIES) {
try {
+ console.log(`[KNOX-SYNC] ${companyCode}: 직급 동기화 시작 (Delete-Insert 방식)`);
+
+ // 1단계: 기존 데이터 삭제
+ await deleteExistingTitles(companyCode);
+
+ // 2단계: 회사 간 딜레이 적용 (첫 번째 회사 제외)
+ if (totalApiCalls > 0) {
+ await delay(currentDelay);
+ }
+
+ // 3단계: 새로운 직급 데이터 조회 및 삽입
const titles = await getTitlesByCompany(companyCode);
- await upsertTitles(titles);
- console.log(`[KNOX-SYNC] 직급 동기화 완료 - ${companyCode}: ${titles.length}건`);
+
+ // API 제한 체크 및 delay 조절
+ const limitStatus = checkApiLimitStatus(1);
+ currentDelay = limitStatus.recommendedDelay;
+ totalApiCalls++;
+
+ await insertTitles(titles);
+ console.log(`[KNOX-SYNC] ${companyCode}: 직급 동기화 완료 - ${titles.length}건, API 호출 1회`);
} catch (err) {
- console.error(`[KNOX-SYNC] 직급 동기화 오류 (company ${companyCode})`, err);
+ console.error(`[KNOX-SYNC] ${companyCode}: 직급 동기화 오류`, err);
}
}
- console.log('[KNOX-SYNC] 직급 동기화 전체 완료');
+ logSyncComplete('직급', totalApiCalls, startTime);
}
export async function startKnoxTitleSyncScheduler() {
if (DO_FIRST_RUN) {
+ if (!checkTimeRestriction()) {
+ return;
+ }
+
syncKnoxTitles().catch(console.error);
}
@@ -66,5 +99,5 @@ export async function startKnoxTitleSyncScheduler() {
syncKnoxTitles().catch(console.error);
});
- console.log(`[KNOX-SYNC] 직급 동기화 스케줄러 등록 (${CRON_STRING})`);
+ logSchedulerInfo('직급', CRON_STRING);
} \ No newline at end of file