summaryrefslogtreecommitdiff
path: root/lib/shi-api/shi-api-utils.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/shi-api/shi-api-utils.ts')
-rw-r--r--lib/shi-api/shi-api-utils.ts53
1 files changed, 43 insertions, 10 deletions
diff --git a/lib/shi-api/shi-api-utils.ts b/lib/shi-api/shi-api-utils.ts
index 2203f6b2..1e420356 100644
--- a/lib/shi-api/shi-api-utils.ts
+++ b/lib/shi-api/shi-api-utils.ts
@@ -89,6 +89,11 @@ export const getAllNonsapUser = async () => {
let totalSkipped = 0;
// 청크 단위로 매핑과 DB 처리를 동시에 수행
+ // 전체 데이터 기준으로 "첫 번째 이메일만 반영" 정책을 지키기 위해
+ // 전역 Set 으로 이메일/USR_ID 중복을 관리한다.
+ const globalSeenEmails = new Set<string>();
+ const globalSeenNonsapUserIds = new Set<string>();
+
for (let i = 0; i < sourceData.length; i += CHUNK_SIZE) {
const chunk = sourceData.slice(i, i + CHUNK_SIZE);
debugLog(`[CHUNK ${Math.floor(i/CHUNK_SIZE) + 1}] Processing ${chunk.length} users (${i + 1}-${Math.min(i + chunk.length, sourceData.length)}/${sourceData.length})`);
@@ -162,6 +167,20 @@ export const getAllNonsapUser = async () => {
continue;
}
+ const emailKey = mappedUser.email.toLowerCase();
+
+ // 데이터셋 전체 기준 중복 이메일/USR_ID 체크 (첫 번째 건만 반영)
+ if (globalSeenEmails.has(emailKey)) {
+ totalSkipped++;
+ debugWarn(`[GLOBAL] 중복 email 스킵: ${mappedUser.email}, nonsapUserId: ${mappedUser.nonsapUserId}`);
+ continue;
+ }
+ if (mappedUser.nonsapUserId && globalSeenNonsapUserIds.has(mappedUser.nonsapUserId)) {
+ totalSkipped++;
+ debugWarn(`[GLOBAL] 중복 nonsapUserId 스킵: ${mappedUser.nonsapUserId}, email: ${mappedUser.email}`);
+ continue;
+ }
+
// 청크 내 중복 체크
if (seenNonsapUserIds.has(mappedUser.nonsapUserId)) {
totalSkipped++;
@@ -169,7 +188,7 @@ export const getAllNonsapUser = async () => {
continue;
}
- if (seenEmails.has(mappedUser.email.toLowerCase())) {
+ if (seenEmails.has(emailKey)) {
totalSkipped++;
debugWarn(`[CHUNK ${Math.floor(i/CHUNK_SIZE) + 1}] 중복 email 발견 (청크 내): ${mappedUser.email}, nonsapUserId: ${mappedUser.nonsapUserId}`);
continue;
@@ -177,20 +196,34 @@ export const getAllNonsapUser = async () => {
// 중복 체크 통과 시 추가
seenNonsapUserIds.add(mappedUser.nonsapUserId);
- seenEmails.add(mappedUser.email.toLowerCase());
+ seenEmails.add(emailKey);
+ globalSeenEmails.add(emailKey);
+ if (mappedUser.nonsapUserId) {
+ globalSeenNonsapUserIds.add(mappedUser.nonsapUserId);
+ }
mappedChunk.push(mappedUser as InsertUser);
}
// 매핑된 청크가 있을 경우에만 DB 처리 (트랜잭션으로 처리)
if (mappedChunk.length > 0) {
- await db.transaction(async (tx) => {
- // nonsapUserId를 기준으로 UPSERT (진정한 사용자 고유 ID)
- // email은 변경될 수 있지만 nonsapUserId는 변경되지 않음
- await bulkUpsert(tx, users, mappedChunk, 'nonsapUserId', 200); // 청크 내에서도 200개씩 세분화
- });
-
- totalUpserted += mappedChunk.length;
- debugLog(`[CHUNK ${Math.floor(i/CHUNK_SIZE) + 1}] Successfully processed ${mappedChunk.length} users`);
+ try {
+ await db.transaction(async (tx) => {
+ // nonsapUserId를 기준으로 UPSERT (진정한 사용자 고유 ID)
+ // email은 변경될 수 있지만 nonsapUserId는 변경되지 않음
+ await bulkUpsert(tx, users, mappedChunk, 'nonsapUserId', 200); // 청크 내에서도 200개씩 세분화
+ });
+
+ totalUpserted += mappedChunk.length;
+ debugLog(`[CHUNK ${Math.floor(i/CHUNK_SIZE) + 1}] Successfully processed ${mappedChunk.length} users`);
+ } catch (chunkTxError: unknown) {
+ // 이메일 UNIQUE 제약 위반 등은 건너뛰고 다음 청크 진행 (첫 건 반영 정책)
+ const pgCode = (chunkTxError as any)?.code;
+ if (pgCode === '23505') {
+ debugWarn(`[CHUNK ${Math.floor(i/CHUNK_SIZE) + 1}] Unique constraint 충돌로 청크 스킵: ${(chunkTxError as Error).message}`);
+ continue;
+ }
+ throw chunkTxError;
+ }
}
// 청크 간 잠시 대기하여 시스템 부하 방지