summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/nonsap-sync/purchase-group-code/purchase-group-code-sync.ts143
-rw-r--r--lib/nonsap-sync/purchase-group-code/user-code-sync-scheduler.ts46
2 files changed, 189 insertions, 0 deletions
diff --git a/lib/nonsap-sync/purchase-group-code/purchase-group-code-sync.ts b/lib/nonsap-sync/purchase-group-code/purchase-group-code-sync.ts
new file mode 100644
index 00000000..903a164c
--- /dev/null
+++ b/lib/nonsap-sync/purchase-group-code/purchase-group-code-sync.ts
@@ -0,0 +1,143 @@
+import { oracleKnex } from '@/lib/oracle-db/db';
+import db from '@/db/db';
+import { users } from '@/db/schema/users';
+import { eq } from 'drizzle-orm';
+
+/**
+ * 오라클 NonSAP 데이터베이스에서 구매그룹 정보 조회
+ * CMCTB_CD 테이블에서 CD_CLF='MMA070' 조건으로 구매그룹 데이터를 가져옴
+ */
+async function fetchPurchasingGroupsFromOracle(): Promise<Array<{ code: string; employeeNumber: string }>> {
+ try {
+ console.log('🔍 오라클에서 구매그룹 데이터 조회 시작...');
+
+ const result = await oracleKnex
+ .select('CD as code', 'USR_DF_CHAR_9 as employeeNumber')
+ .from('CMCTB_CD')
+ .where('CD_CLF', 'MMA070')
+ .whereNotNull('USR_DF_CHAR_9')
+
+ console.log(`✅ 구매그룹 데이터 ${result.length}건 조회 완료`);
+ return result;
+ } catch (error) {
+ console.error('❌ 오라클에서 구매그룹 데이터 조회 중 오류:', error);
+ throw error;
+ }
+}
+
+/**
+ * employeeNumber 기준으로 users 테이블에 사용자 코드 upsert
+ */
+async function updateUsersCode(purchasingGroups: Array<{ code: string; employeeNumber: string }>) {
+ try {
+ console.log('🔄 사용자 테이블 사용자 코드 업데이트 시작...');
+
+ let updateCount = 0;
+ let notFoundCount = 0;
+ const errors: Array<{ employeeNumber: string; error: string }> = [];
+
+ // 개별 트랜잭션으로 안전하게 처리
+ for (const group of purchasingGroups) {
+ try {
+ await db.transaction(async (tx) => {
+ const result = await tx
+ .update(users)
+ .set({
+ userCode: group.code,
+ updatedAt: new Date()
+ })
+ .where(eq(users.employeeNumber, group.employeeNumber))
+ .returning({ id: users.id });
+
+ if (result.length > 0) {
+ updateCount++;
+ } else {
+ notFoundCount++;
+ console.warn(`⚠️ 사번 ${group.employeeNumber}에 해당하는 사용자를 찾을 수 없음 (사용자 코드: ${group.code})`);
+ }
+ });
+ } catch (error) {
+ errors.push({
+ employeeNumber: group.employeeNumber,
+ error: error instanceof Error ? error.message : '알 수 없는 오류'
+ });
+ console.error(`❌ 사번 ${group.employeeNumber} 업데이트 중 오류:`, error);
+ }
+ }
+
+ // 에러가 있었다면 로그 출력
+ if (errors.length > 0) {
+ console.error(`❌ ${errors.length}건의 업데이트 오류 발생:`);
+ errors.forEach(({ employeeNumber, error }) => {
+ console.error(` - 사번 ${employeeNumber}: ${error}`);
+ });
+ }
+
+ console.log(`✅ 사용자 코드 업데이트 완료: 성공 ${updateCount}건, 미발견 ${notFoundCount}건`);
+ return { updateCount, notFoundCount };
+ } catch (error) {
+ console.error('❌ 사용자 테이블 업데이트 중 오류:', error);
+ throw error;
+ }
+}
+
+/**
+ * 사용자 코드 동기화 메인 함수
+ * 오라클에서 구매그룹 데이터를 조회하여 users 테이블을 업데이트
+ */
+export async function syncUserCodes(): Promise<{ success: boolean; updateCount: number; notFoundCount: number; error?: string }> {
+ try {
+ console.log('🚀 사용자 코드 동기화 작업 시작...');
+ const startTime = Date.now();
+
+ // 1. 오라클에서 구매그룹 데이터 조회
+ const purchasingGroups = await fetchPurchasingGroupsFromOracle();
+
+ if (purchasingGroups.length === 0) {
+ console.log('⚠️ 조회된 구매그룹 데이터가 없습니다.');
+ return { success: true, updateCount: 0, notFoundCount: 0 };
+ }
+
+ // 2. users 테이블 업데이트
+ const { updateCount, notFoundCount } = await updateUsersCode(purchasingGroups);
+
+ const endTime = Date.now();
+ const duration = endTime - startTime;
+
+ console.log(`🎉 사용자 코드 동기화 완료! 소요시간: ${duration}ms`);
+ console.log(`📊 처리 결과: 총 ${purchasingGroups.length}건 중 업데이트 ${updateCount}건, 미발견 ${notFoundCount}건`);
+
+ return {
+ success: true,
+ updateCount,
+ notFoundCount
+ };
+ } catch (error) {
+ console.error('💥 사용자 코드 동기화 작업 중 오류:', error);
+ return {
+ success: false,
+ updateCount: 0,
+ notFoundCount: 0,
+ error: error instanceof Error ? error.message : '알 수 없는 오류'
+ };
+ }
+}
+
+/**
+ * 특정 사번의 사용자 코드를 조회하는 함수 (개별 조회용)
+ */
+export async function getUserCodeByEmployeeNumber(employeeNumber: string): Promise<string | null> {
+ try {
+ const result = await oracleKnex
+ .select('CD')
+ .from('CMCTB_CD')
+ .where('CD_CLF', 'MMA070')
+ .andWhere('USR_DF_CHAR_9', employeeNumber)
+ .limit(1);
+
+ return result.length > 0 ? result[0].CD : null;
+ } catch (error) {
+ console.error(`사용자 코드 조회 중 오류 (사번: ${employeeNumber}):`, error);
+ return null;
+ }
+}
diff --git a/lib/nonsap-sync/purchase-group-code/user-code-sync-scheduler.ts b/lib/nonsap-sync/purchase-group-code/user-code-sync-scheduler.ts
new file mode 100644
index 00000000..23d0b568
--- /dev/null
+++ b/lib/nonsap-sync/purchase-group-code/user-code-sync-scheduler.ts
@@ -0,0 +1,46 @@
+'use server';
+
+import * as cron from 'node-cron';
+import { syncUserCodes } from './purchase-group-code-sync';
+
+// CRON 스케줄 (기본: 매일 새벽 3시)
+const CRON_STRING = process.env.USER_CODE_SYNC_CRON || '0 3 * * *';
+
+// 애플리케이션 기동 시 최초 한 번 실행 여부
+const DO_FIRST_RUN = process.env.USER_CODE_SYNC_FIRST_RUN === 'true';
+
+/**
+ * 사용자 코드 동기화 스케줄러 시작
+ */
+export async function startUserCodeSyncScheduler(): Promise<void> {
+ console.log('[USER-CODE-SYNC] 사용자 코드 동기화 스케줄러 초기화 중...');
+
+ try {
+ // 환경 변수에 따라 실행시 즉시 실행 여부 결정
+ if (DO_FIRST_RUN) {
+ console.log('[USER-CODE-SYNC] 첫 실행: 사용자 코드 동기화 즉시 시작');
+ syncUserCodes().catch((error) => {
+ console.error('[USER-CODE-SYNC] 첫 실행 중 오류:', error);
+ });
+ }
+
+ // CRON JOB 등록
+ cron.schedule(CRON_STRING, async () => {
+ try {
+ console.log('[USER-CODE-SYNC] CRON 실행: 사용자 코드 동기화 시작');
+ await syncUserCodes();
+ } catch (error) {
+ console.error('[USER-CODE-SYNC] 예약된 동기화 실패:', error);
+ // 동기화 실패해도 다음 스케줄은 계속 실행
+ }
+ }, {
+ timezone: 'Asia/Seoul'
+ });
+
+ console.log(`[USER-CODE-SYNC] ✅ 사용자 코드 동기화 CRON 작업 등록 완료 (${CRON_STRING})`);
+
+ } catch (error) {
+ console.error('[USER-CODE-SYNC] 스케줄러 초기화 실패:', error);
+ throw error;
+ }
+}