"use server" // src/lib/cron/syncItemsFromCodeLists.ts import db from "@/db/db"; import { projects, items } from '@/db/schema'; import { eq } from 'drizzle-orm'; import { getSEDPToken } from "./sedp-token"; const SEDP_API_BASE_URL = process.env.SEDP_API_BASE_URL || 'http://sedpwebapi.ship.samsung.co.kr/api'; async function getCodeLists(projectCode: string): Promise> { try { // 토큰(API 키) 가져오기 const apiKey = await getSEDPToken(); const response = await fetch( `${SEDP_API_BASE_URL}/CodeList/Get`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'accept': '*/*', 'ApiKey': apiKey, 'ProjectNo': projectCode }, body: JSON.stringify({ ProjectNo: projectCode, ContainDeleted: false }) } ); if (!response.ok) { throw new Error(`코드 리스트 요청 실패: ${response.status} ${response.statusText}`); } // 안전하게 JSON 파싱 try { const data = await response.json(); // 데이터가 배열인지 확인 const codeLists: CodeList[] = Array.isArray(data) ? data : [data]; // CL_ID로 효율적인 조회를 위한 맵 생성 const codeListMap = new Map(); for (const codeList of codeLists) { if (!codeList.DELETED) { codeListMap.set(codeList.CL_ID, codeList); } } console.log(`프로젝트 ${projectCode}에서 ${codeListMap.size}개의 코드 리스트를 가져왔습니다`); return codeListMap; } catch (parseError) { console.error(`프로젝트 ${projectCode}의 코드 리스트 응답 파싱 실패:`, parseError); // 응답 내용 로깅 try { const text = await response.clone().text(); console.log(`응답 내용: ${text.substring(0, 200)}${text.length > 200 ? '...' : ''}`); } catch (textError) { console.error('응답 내용 로깅 실패:', textError); } return new Map(); } } catch (error) { console.error(`프로젝트 ${projectCode}의 코드 리스트 가져오기 실패:`, error); return new Map(); } } interface CodeValue { VALUE: string; DESC: string; ATTRIBUTES: Array<{ ATT_ID: string; VALUE: string; }>; } interface CodeList { PROJ_NO: string; CL_ID: string; DESC: string; REMARK: string | null; PRNT_CD_ID: string | null; REG_TYPE_ID: string | null; VAL_ATT_ID: string | null; VALUES: CodeValue[]; LNK_ATT: any[]; DELETED: boolean; CRTER_NO: string; CRTE_DTM: string; CHGER_NO: string | null; CHGE_DTM: string | null; _id: string; } export async function syncItemsFromCodeLists(): Promise { try { console.log('아이템 동기화 시작...'); // 모든 프로젝트 가져오기 const allProjects = await db.select().from(projects); console.log(`총 ${allProjects.length}개의 프로젝트를 처리합니다.`); let totalItemsProcessed = 0; let totalItemsInserted = 0; let totalItemsUpdated = 0; for (const project of allProjects) { try { console.log(`프로젝트 ${project.code} (${project.name}) 처리 중...`); // 프로젝트의 코드리스트 가져오기 const codeListMap = await getCodeLists(project.code); // PKG_NO 코드리스트 찾기 const pkgNoCodeList = codeListMap.get('PKG_NO'); if (!pkgNoCodeList) { console.log(`프로젝트 ${project.code}에서 PKG_NO 코드리스트를 찾을 수 없습니다.`); continue; } console.log(`프로젝트 ${project.code}에서 ${pkgNoCodeList.VALUES.length}개의 아이템을 처리합니다.`); // VALUES 배열 순회하며 items 테이블에 삽입/업데이트 for (const codeValue of pkgNoCodeList.VALUES) { try { // ATTRIBUTES에서 필요한 값들 추출 const packageCodeAttr = codeValue.ATTRIBUTES?.find(attr => attr.ATT_ID === 'PROJ_PACK_NO'); const packageNameAttr = codeValue.ATTRIBUTES?.find(attr => attr.ATT_ID === 'PROJ_PACK_DESC'); const smCodeAttr = codeValue.ATTRIBUTES?.find(attr => attr.ATT_ID === 'SM_code'); const itemData = { ProjectNo: project.code, itemCode: codeValue.VALUE, itemName: packageNameAttr?.VALUE || '' , packageCode: packageCodeAttr?.VALUE || '', smCode: smCodeAttr?.VALUE || null, description: codeValue.DESC || "", // 필요시 추가 매핑 parentItemCode: null, // 필요시 추가 매핑 itemLevel: null, // 필요시 추가 매핑 deleteFlag: 'N', // 기본값 unitOfMeasure: null, // 필요시 추가 매핑 steelType: null, // 필요시 추가 매핑 gradeMaterial: null, // 필요시 추가 매핑 changeDate: null, // 필요시 추가 매핑 baseUnitOfMeasure: null, // 필요시 추가 매핑 updatedAt: new Date() }; // 기존 아이템 확인 (itemCode로 조회) const existingItem = await db.select() .from(items) .where(eq(items.itemCode, codeValue.VALUE)) .limit(1); if (existingItem.length > 0) { // 기존 아이템 업데이트 await db.update(items) .set(itemData) .where(eq(items.itemCode, codeValue.VALUE)); totalItemsUpdated++; } else { // 새 아이템 삽입 await db.insert(items).values(itemData); totalItemsInserted++; } totalItemsProcessed++; } catch (itemError) { console.error(`아이템 ${codeValue.VALUE} 처리 중 오류:`, itemError); } } console.log(`프로젝트 ${project.code} 완료`); } catch (projectError) { console.error(`프로젝트 ${project.code} 처리 중 오류:`, projectError); } } console.log(`아이템 동기화 완료:`); console.log(`- 총 처리된 아이템: ${totalItemsProcessed}개`); console.log(`- 새로 삽입된 아이템: ${totalItemsInserted}개`); console.log(`- 업데이트된 아이템: ${totalItemsUpdated}개`); } catch (error) { console.error('아이템 동기화 중 전체 오류:', error); throw error; } } // 특정 프로젝트만 동기화하는 함수 export async function syncItemsForProject(projectCode: string): Promise { try { console.log(`프로젝트 ${projectCode}의 아이템 동기화 시작...`); // 프로젝트 존재 확인 const project = await db.select() .from(projects) .where(eq(projects.code, projectCode)) .limit(1); if (project.length === 0) { throw new Error(`프로젝트 ${projectCode}를 찾을 수 없습니다.`); } // 프로젝트의 코드리스트 가져오기 const codeListMap = await getCodeLists(projectCode); // PKG_NO 코드리스트 찾기 const pkgNoCodeList = codeListMap.get('PKG_NO'); if (!pkgNoCodeList) { console.log(`프로젝트 ${projectCode}에서 PKG_NO 코드리스트를 찾을 수 없습니다.`); return; } console.log(`${pkgNoCodeList.VALUES.length}개의 아이템을 처리합니다.`); let itemsProcessed = 0; let itemsInserted = 0; let itemsUpdated = 0; // VALUES 배열 순회하며 items 테이블에 삽입/업데이트 for (const codeValue of pkgNoCodeList.VALUES) { try { // ATTRIBUTES에서 필요한 값들 추출 const packageCodeAttr = codeValue.ATTRIBUTES?.find(attr => attr.ATT_ID === 'SHI_PACK_NO'); const smCodeAttr = codeValue.ATTRIBUTES?.find(attr => attr.ATT_ID === 'SM_code'); const packageNameAttr = codeValue.ATTRIBUTES?.find(attr => attr.ATT_ID === 'PROJ_PACK_DESC'); const itemData = { ProjectNo: projectCode, itemCode: codeValue.VALUE, itemName: packageNameAttr?.VALUE || '' , packageCode: packageCodeAttr?.VALUE || '', smCode: smCodeAttr?.VALUE || null, description: codeValue.DESC || "", // 필요시 추가 매핑 parentItemCode: null, // 필요시 추가 매핑 itemLevel: null, // 필요시 추가 매핑 deleteFlag: 'N', // 기본값 unitOfMeasure: null, // 필요시 추가 매핑 steelType: null, // 필요시 추가 매핑 gradeMaterial: null, // 필요시 추가 매핑 changeDate: null, // 필요시 추가 매핑 baseUnitOfMeasure: null, // 필요시 추가 매핑 updatedAt: new Date() }; // 기존 아이템 확인 const existingItem = await db.select() .from(items) .where(eq(items.itemCode, codeValue.VALUE)) .limit(1); if (existingItem.length > 0) { await db.update(items) .set(itemData) .where(eq(items.itemCode, codeValue.VALUE)); itemsUpdated++; } else { await db.insert(items).values(itemData); itemsInserted++; } itemsProcessed++; } catch (itemError) { console.error(`아이템 ${codeValue.VALUE} 처리 중 오류:`, itemError); } } console.log(`프로젝트 ${projectCode} 아이템 동기화 완료:`); console.log(`- 처리된 아이템: ${itemsProcessed}개`); console.log(`- 새로 삽입된 아이템: ${itemsInserted}개`); console.log(`- 업데이트된 아이템: ${itemsUpdated}개`); } catch (error) { console.error(`프로젝트 ${projectCode} 아이템 동기화 중 오류:`, error); throw error; } }