1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
'use server';
import { nonsapUser, users } from '@/db/schema';
import db from '@/db/db';
import { sql } from 'drizzle-orm';
import { debugError, debugLog, debugWarn, debugSuccess } from '@/lib/debug-utils';
const shiApiBaseUrl = process.env.SHI_API_BASE_URL;
const shiNonsapUserSegment = process.env.SHI_NONSAP_USER_SEGMENT;
const shiApiJwtToken = process.env.SHI_API_JWT_TOKEN;
type NonsapUser = typeof nonsapUser.$inferSelect;
type NonsapUserInsert = typeof nonsapUser.$inferInsert;
type InsertUser = typeof users.$inferInsert;
export const getAllNonsapUser = async () => {
try{
debugLog('Starting NONSAP user sync via SHI-API');
if (!shiApiBaseUrl || !shiNonsapUserSegment || !shiApiJwtToken) {
throw new Error('SHI API 환경변수가 설정되지 않았습니다. (SHI_API_BASE_URL, SHI_NONSAP_USER_SEGMENT, SHI_API_JWT_TOKEN)');
}
const ynToBool = (value: string | null | undefined) => (value || '').toUpperCase() === 'Y';
// ** 1. 전체 데이터 조회해 응답 받음 (js 배열) **
const response = await fetch(`${shiApiBaseUrl}${shiNonsapUserSegment}`, {
headers: {
Authorization: `Bearer ${shiApiJwtToken}`,
},
cache: 'no-store',
});
if (!response.ok) {
const text = await response.text().catch(() => '');
throw new Error(`SHI-API 요청 실패: ${response.status} ${response.statusText} ${text}`);
}
const data: NonsapUser[] = await response.json();
debugSuccess(`[SHI-API] fetched ${Array.isArray(data) ? data.length : 0} users`);
// ** 2. 받은 데이터를 DELETE & INSERT 방식으로 수신 테이블 (nonsap-user) 에 저장 **
await db.delete(nonsapUser); // 전체 정리
if (Array.isArray(data) && data.length > 0) {
await db.insert(nonsapUser).values(data as unknown as NonsapUserInsert[]); // 데이터 저장 (스키마 컬럼 그대로)
debugSuccess(`[STAGE] nonsap_user refreshed with ${data.length} records`);
}
// ** 3. 데이터 저장 이후, 비즈니스 테이블인 "public"."users" 에 동기화 시킴 (매핑 필요) **
const now = new Date();
const mappedRaw: Partial<InsertUser>[] = (Array.isArray(data) ? data : [])
.map((u: NonsapUser): Partial<InsertUser> => {
const isDeleted = ynToBool(u.DEL_YN); // nonsap user 테이블에서 삭제여부
const isAbsent = ynToBool(u.LOFF_GB); // nonsap user 테이블에서 휴직여부
const notApproved = (u.AGR_YN || '').toUpperCase() === 'N'; // nonsap user 테이블에서 승인여부
const isActive = !(isDeleted || isAbsent || notApproved); // eVCP 내에서 활성화 여부
// S = 정직원
const isRegularEmployee = (u.REGL_ORORD_GB || '').toUpperCase() === 'S';
return {
// upsert key = USR_ID
nonsapUserId: u.USR_ID || undefined,
// mapped fields
employeeNumber: u.EMPNO || undefined,
knoxId: u.MYSNG_ID || undefined,
name: u.USR_NM || undefined,
email: u.EMAIL_ADR || undefined,
epId: u.MYSNG_ID || undefined,
deptCode: u.CH_DEPTCD || undefined,
deptName: u.CH_DEPTNM || undefined,
phone: u.TELNO || undefined,
isAbsent,
isDeletedOnNonSap: isDeleted,
isActive,
isRegularEmployee,
};
});
// users 테이블 제약조건 대응: email, name 은 not null + nonsapUserId 존재
//.filter((u) => typeof u.email === 'string' && !!u.email && typeof u.name === 'string' && !!u.name && typeof u.nonsapUserId === 'string' && u.nonsapUserId.length > 0);
const mappedUsers = mappedRaw as InsertUser[];
if (mappedUsers.length > 0) {
await db.insert(users)
.values(mappedUsers)
.onConflictDoUpdate({
target: users.nonsapUserId,
set: {
name: sql`excluded.name`,
employeeNumber: sql`excluded.employeeNumber`,
knoxId: sql`excluded.knoxId`,
epId: sql`excluded."epId"`,
deptCode: sql`excluded."deptCode"`,
deptName: sql`excluded."deptName"`,
phone: sql`excluded.phone`,
nonsapUserId: sql`excluded."nonsapUserId"`,
isAbsent: sql`excluded."isAbsent"`,
isDeletedOnNonSap: sql`excluded."isDeletedOnNonSap"`,
isActive: sql`excluded."isActive"`,
isRegularEmployee: sql`excluded."isRegularEmployee"`,
updatedAt: sql`now()`,
},
});
debugSuccess(`[UPSERT] users upserted=${mappedUsers.length} using key=nonsapUserId`);
} else {
debugWarn('[UPSERT] No users mapped for upsert (missing name/email or invalid USR_ID)');
}
// 휴직 사용자도 API에서 수신하므로, 기존 사용자와의 비교를 통한 휴직 처리 로직은 더 이상 필요하지 않음
return {
fetched: Array.isArray(data) ? data.length : 0,
staged: Array.isArray(data) ? data.length : 0,
upserted: mappedUsers.length,
skippedDueToMissingRequiredFields: (Array.isArray(data) ? data.length : 0) - mappedUsers.length,
ranAt: now.toISOString(),
};
} catch(error){
debugError('SHI-API 동기화 실패', error);
console.error("SHI-API 를 통한 유저 동기화 프로세스 간 실패 발생: ", error);
throw error;
}
};
|