diff options
| author | joonhoekim <26rote@gmail.com> | 2025-10-03 15:39:55 +0900 |
|---|---|---|
| committer | joonhoekim <26rote@gmail.com> | 2025-10-03 15:39:55 +0900 |
| commit | 9eec700c9627d91aaf52a89d1bfb0ae0e21eb49a (patch) | |
| tree | 2f997ba959fffda0b7bb43e4ec51885c7c4cb24b | |
| parent | 6cd69b11dc3cedc3ec1e481f1404bd2ce9a64d11 (diff) | |
(김준회) 유저 정보 동기화 관련 nonsapUserId 기준 통일
| -rw-r--r-- | lib/knox-sync/employee-sync-service.ts | 6 | ||||
| -rw-r--r-- | lib/shi-api/shi-api-utils.ts | 47 |
2 files changed, 40 insertions, 13 deletions
diff --git a/lib/knox-sync/employee-sync-service.ts b/lib/knox-sync/employee-sync-service.ts index b7f2a323..3055e1ef 100644 --- a/lib/knox-sync/employee-sync-service.ts +++ b/lib/knox-sync/employee-sync-service.ts @@ -265,13 +265,17 @@ async function syncEmployeesToUsers(): Promise<void> { if (existingUsers.length > 0) { // 기존 사용자 업데이트 + // ⚠️ 주의: 기존 사용자의 domain은 유지 (덮어쓰지 않음) + const existingDomain = existingUsers[0].domain; + await db .update(users) .set({ name: employee.fullName, deptCode: employee.departmentCode, deptName: employee.departmentName, - domain: assignedDomain as UserDomainType, + // domain은 기존 값 유지 (assignedDomain으로 덮어쓰지 않음) + domain: existingDomain, epId: employee.epId, updatedAt: new Date(), }) diff --git a/lib/shi-api/shi-api-utils.ts b/lib/shi-api/shi-api-utils.ts index e3ab1102..2203f6b2 100644 --- a/lib/shi-api/shi-api-utils.ts +++ b/lib/shi-api/shi-api-utils.ts @@ -94,31 +94,33 @@ export const getAllNonsapUser = async () => { debugLog(`[CHUNK ${Math.floor(i/CHUNK_SIZE) + 1}] Processing ${chunk.length} users (${i + 1}-${Math.min(i + chunk.length, sourceData.length)}/${sourceData.length})`); try { - // 청크 내 이메일별 기존 domain 정보 조회를 위한 Map 생성 + // 청크 내 nonsapUserId별 기존 domain 정보 조회를 위한 Map 생성 const existingDomainMap = new Map<string, string>(); // 각 사용자에 대해 개별적으로 기존 domain 조회 (메모리 효율성 위해) for (const u of chunk) { - if (u.EMAIL_ADR) { + if (u.USR_ID) { try { const existingUser = await db .select({ domain: users.domain }) .from(users) - .where(eq(users.email, u.EMAIL_ADR)) + .where(eq(users.nonsapUserId, u.USR_ID)) .limit(1); if (existingUser.length > 0) { - existingDomainMap.set(u.EMAIL_ADR.toLowerCase(), existingUser[0].domain); + existingDomainMap.set(u.USR_ID, existingUser[0].domain); } } catch (error) { // 조회 실패 시 무시하고 계속 진행 - console.warn(`Failed to lookup existing user for email ${u.EMAIL_ADR}:`, error); + console.warn(`Failed to lookup existing user for nonsapUserId ${u.USR_ID}:`, error); } } } // 청크 단위로 매핑 수행 const mappedChunk: InsertUser[] = []; + const seenNonsapUserIds = new Set<string>(); // 청크 내 중복 체크용 + const seenEmails = new Set<string>(); // 청크 내 중복 체크용 for (const u of chunk) { const isDeleted = ynToBool(u.DEL_YN); // nonsap user 테이블에서 삭제여부 @@ -128,9 +130,10 @@ export const getAllNonsapUser = async () => { // S = 정직원 const isRegularEmployee = (u.REGL_ORORD_GB || '').toUpperCase() === 'S'; - // 기존 사용자인지 확인하여 domain 결정 + // 기존 사용자인지 확인하여 domain 결정 (nonsapUserId 기준) const email = u.EMAIL_ADR; - const existingDomain = email ? existingDomainMap.get(email.toLowerCase()) as UserDomain | undefined : undefined; + const nonsapUserId = u.USR_ID; + const existingDomain = nonsapUserId ? existingDomainMap.get(nonsapUserId) as UserDomain | undefined : undefined; // 기존 사용자(existingDomain !== undefined)는 기존 domain을 무조건 유지, 새 사용자는 pending const domain = existingDomain !== undefined ? existingDomain : 'pending'; @@ -152,18 +155,38 @@ export const getAllNonsapUser = async () => { isRegularEmployee, }; - // 필수 필드 검증 (기본적인 검증만 수행) - if (mappedUser.nonsapUserId) { - mappedChunk.push(mappedUser as InsertUser); - } else { + // 필수 필드 검증 및 중복 체크 + if (!mappedUser.nonsapUserId || !mappedUser.email) { totalSkipped++; + debugWarn(`[CHUNK ${Math.floor(i/CHUNK_SIZE) + 1}] 필수 필드 누락: nonsapUserId=${mappedUser.nonsapUserId}, email=${mappedUser.email}`); + continue; } + + // 청크 내 중복 체크 + if (seenNonsapUserIds.has(mappedUser.nonsapUserId)) { + totalSkipped++; + debugWarn(`[CHUNK ${Math.floor(i/CHUNK_SIZE) + 1}] 중복 nonsapUserId 발견 (청크 내): ${mappedUser.nonsapUserId}, 이메일: ${mappedUser.email}`); + continue; + } + + if (seenEmails.has(mappedUser.email.toLowerCase())) { + totalSkipped++; + debugWarn(`[CHUNK ${Math.floor(i/CHUNK_SIZE) + 1}] 중복 email 발견 (청크 내): ${mappedUser.email}, nonsapUserId: ${mappedUser.nonsapUserId}`); + continue; + } + + // 중복 체크 통과 시 추가 + seenNonsapUserIds.add(mappedUser.nonsapUserId); + seenEmails.add(mappedUser.email.toLowerCase()); + mappedChunk.push(mappedUser as InsertUser); } // 매핑된 청크가 있을 경우에만 DB 처리 (트랜잭션으로 처리) if (mappedChunk.length > 0) { await db.transaction(async (tx) => { - await bulkUpsert(tx, users, mappedChunk, 'email', 200); // 청크 내에서도 200개씩 세분화 + // nonsapUserId를 기준으로 UPSERT (진정한 사용자 고유 ID) + // email은 변경될 수 있지만 nonsapUserId는 변경되지 않음 + await bulkUpsert(tx, users, mappedChunk, 'nonsapUserId', 200); // 청크 내에서도 200개씩 세분화 }); totalUpserted += mappedChunk.length; |
