summaryrefslogtreecommitdiff
path: root/lib/shi-api
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-08-14 13:15:21 +0000
committerjoonhoekim <26rote@gmail.com>2025-08-14 13:15:21 +0000
commit49d236df3bd2bd976ebc424644f34f5affa1074f (patch)
tree7b0f60c399e724847894061fae74876aa1bf5c7e /lib/shi-api
parent969c25b56f6d29d7ffa4bc2ce04c5fb4e5846b34 (diff)
(김준회) 결재 테스트 모듈 수정, 환경병수 eVCP 운영 대응, SGIPS JWT TOKEN 수정, SHI-API 기반 유저 관리 추가, 유저목록 테이블 변경
Diffstat (limited to 'lib/shi-api')
-rw-r--r--lib/shi-api/shi-api-utils.ts125
-rw-r--r--lib/shi-api/users-sync-scheduler.ts42
2 files changed, 167 insertions, 0 deletions
diff --git a/lib/shi-api/shi-api-utils.ts b/lib/shi-api/shi-api-utils.ts
new file mode 100644
index 00000000..ddbc186f
--- /dev/null
+++ b/lib/shi-api/shi-api-utils.ts
@@ -0,0 +1,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;
+ }
+};
diff --git a/lib/shi-api/users-sync-scheduler.ts b/lib/shi-api/users-sync-scheduler.ts
new file mode 100644
index 00000000..1cca3441
--- /dev/null
+++ b/lib/shi-api/users-sync-scheduler.ts
@@ -0,0 +1,42 @@
+'use server';
+
+import * as cron from 'node-cron';
+import { getAllNonsapUser } from './shi-api-utils';
+
+// 기본: 매일 01:00 KST 실행. 환경변수로 오버라이드 가능
+const CRON_STRING = process.env.SHI_API_USERS_SYNC_CRON || '0 1 * * *';
+
+/**
+ * SHI-API NONSAP 사용자 동기화 - 일일 스케줄러 등록
+ */
+export async function startShiApiUsersDailySyncScheduler(): Promise<void> {
+ try {
+ cron.schedule(
+ CRON_STRING,
+ async () => {
+ try {
+ console.log('[SHI-API] CRON 실행: NONSAP 사용자 동기화 시작');
+ await getAllNonsapUser();
+ console.log('[SHI-API] CRON 완료: NONSAP 사용자 동기화 성공');
+ } catch (error) {
+ console.error('[SHI-API] CRON 실패: NONSAP 사용자 동기화 오류', error);
+ }
+ },
+ { timezone: 'Asia/Seoul' },
+ );
+
+ console.log('[SHI-API] Daily NONSAP user sync cron registered:', CRON_STRING);
+ } catch (error) {
+ console.error('Failed to set up SHI-API users daily cron scheduler.', error);
+ }
+
+ try {
+ if(process.env.NONSAP_USERSYNC_FIRST_RUN === 'true') {
+ await getAllNonsapUser();
+ }
+ } catch (error) {
+ console.error('Failed to sync NONSAP users in first run mode.', error);
+ }
+}
+
+