summaryrefslogtreecommitdiff
path: root/lib/knox-sync/organization-sync-service.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/knox-sync/organization-sync-service.ts')
-rw-r--r--lib/knox-sync/organization-sync-service.ts132
1 files changed, 132 insertions, 0 deletions
diff --git a/lib/knox-sync/organization-sync-service.ts b/lib/knox-sync/organization-sync-service.ts
new file mode 100644
index 00000000..0b77174b
--- /dev/null
+++ b/lib/knox-sync/organization-sync-service.ts
@@ -0,0 +1,132 @@
+'use server';
+
+import * as cron from 'node-cron';
+import db from '@/db/db';
+import { organization as organizationTable } from '@/db/schema/knox/organization';
+import {
+ searchOrganizations,
+ Organization,
+} from '@/lib/knox-api/employee/employee';
+import { sql } from 'drizzle-orm';
+
+// 동기화 대상 회사 코드 (쉼표로 구분된 ENV)
+const COMPANIES = (process.env.KNOX_COMPANY_CODES || 'P2')
+ .split(',')
+ .map((c) => c.trim())
+ .filter(Boolean);
+
+// CRON 스케줄 (기본: 매일 04:15)
+const CRON_STRING = process.env.KNOX_ORGANIZATION_SYNC_CRON || '15 4 * * *';
+
+// 애플리케이션 기동 시 최초 한 번 실행 여부
+const DO_FIRST_RUN = process.env.KNOX_ORGANIZATION_SYNC_FIRST_RUN === 'true';
+
+async function upsertOrganizations(orgs: Organization[]) {
+ if (!orgs.length) return;
+
+ const rows = orgs.map((o) => ({
+ companyCode: o.companyCode,
+ departmentCode: o.departmentCode,
+ companyName: o.companyName,
+ departmentLevel: o.departmentLevel,
+ departmentName: o.departmentName,
+ departmentOrder: o.departmentOrder,
+ enCompanyName: o.enCompanyName,
+ enDepartmentName: o.enDepartmentName,
+ enManagerTitle: o.enManagerTitle,
+ enSubOrgCode: o.enSubOrgCode,
+ inDepartmentCode: o.inDepartmentCode,
+ lowDepartmentYn: o.lowDepartmentYn,
+ managerId: o.managerId,
+ managerName: o.managerName,
+ managerTitle: o.managerTitle,
+ preferredLanguage: o.preferredLanguage,
+ subOrgCode: o.subOrgCode,
+ subOrgName: o.subOrgName,
+ uprDepartmentCode: o.uprDepartmentCode,
+ enUprDepartmentName: o.enUprDepartmentName,
+ uprDepartmentName: o.uprDepartmentName,
+ hiddenDepartmentYn: o.hiddenDepartmentYn,
+ corpCode: o.corpCode,
+ corpName: o.corpName,
+ enCorpName: o.enCorpName,
+ raw: o as unknown as Record<string, unknown>,
+ }));
+
+ await db
+ .insert(organizationTable)
+ .values(rows)
+ .onConflictDoUpdate({
+ target: [organizationTable.companyCode, organizationTable.departmentCode],
+ set: {
+ companyName: sql.raw('excluded.company_name'),
+ departmentLevel: sql.raw('excluded.department_level'),
+ departmentName: sql.raw('excluded.department_name'),
+ departmentOrder: sql.raw('excluded.department_order'),
+ enCompanyName: sql.raw('excluded.en_company_name'),
+ enDepartmentName: sql.raw('excluded.en_department_name'),
+ enManagerTitle: sql.raw('excluded.en_manager_title'),
+ enSubOrgCode: sql.raw('excluded.en_sub_org_code'),
+ inDepartmentCode: sql.raw('excluded.in_department_code'),
+ lowDepartmentYn: sql.raw('excluded.low_department_yn'),
+ managerId: sql.raw('excluded.manager_id'),
+ managerName: sql.raw('excluded.manager_name'),
+ managerTitle: sql.raw('excluded.manager_title'),
+ preferredLanguage: sql.raw('excluded.preferred_language'),
+ subOrgCode: sql.raw('excluded.sub_org_code'),
+ subOrgName: sql.raw('excluded.sub_org_name'),
+ uprDepartmentCode: sql.raw('excluded.upr_department_code'),
+ enUprDepartmentName: sql.raw('excluded.en_upr_department_name'),
+ uprDepartmentName: sql.raw('excluded.upr_department_name'),
+ hiddenDepartmentYn: sql.raw('excluded.hidden_department_yn'),
+ corpCode: sql.raw('excluded.corp_code'),
+ corpName: sql.raw('excluded.corp_name'),
+ enCorpName: sql.raw('excluded.en_corp_name'),
+ raw: sql.raw('excluded.raw'),
+ updatedAt: sql.raw('CURRENT_TIMESTAMP'),
+ },
+ });
+}
+
+export async function syncKnoxOrganizations(): Promise<void> {
+ console.log('[KNOX-SYNC] 조직 동기화 시작');
+
+ for (const companyCode of COMPANIES) {
+ try {
+ let page = 1;
+ let totalPage = 1;
+ do {
+ const resp = await searchOrganizations({ companyCode, page: String(page) });
+
+ if (resp.result === 'success') {
+ await upsertOrganizations(resp.organizations);
+ totalPage = resp.totalPage;
+ console.log(
+ `[KNOX-SYNC] 조직 동기화 ${companyCode} ${page}/${totalPage} 페이지 처리`
+ );
+ } else {
+ console.warn(`[KNOX-SYNC] 조직 동기화 실패: ${companyCode} page ${page}`);
+ break;
+ }
+
+ page += 1;
+ } while (page <= totalPage);
+ } catch (err) {
+ console.error(`[KNOX-SYNC] 조직 동기화 오류 (company ${companyCode})`, err);
+ }
+ }
+
+ console.log('[KNOX-SYNC] 조직 동기화 완료');
+}
+
+export async function startKnoxOrganizationSyncScheduler() {
+ if (DO_FIRST_RUN) {
+ syncKnoxOrganizations().catch(console.error);
+ }
+
+ cron.schedule(CRON_STRING, () => {
+ syncKnoxOrganizations().catch(console.error);
+ });
+
+ console.log(`[KNOX-SYNC] 조직 동기화 스케줄러 등록 (${CRON_STRING})`);
+} \ No newline at end of file