summaryrefslogtreecommitdiff
path: root/lib/shi-api/shi-api-utils.ts
blob: ddbc186f0cad4b7c81e9b5da39e79f9f12d65f73 (plain)
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;
  }
};