import { NextRequest } from 'next/server'; import db from '@/db/db'; import { ToXMLFields, serveWsdl, createXMLParser, extractRequestData, convertXMLToDBData, processNestedArray, createErrorResponse, createSuccessResponse, createSoapResponse, replaceSubTableData, withSoapLogging, } from '@/lib/soap/mdg/utils'; import { PR_INFORMATION_T_BID_HEADER, PR_INFORMATION_T_BID_ITEM, } from '@/db/schema/ECC/ecc'; // 스키마에서 타입 추론 type BidHeaderData = typeof PR_INFORMATION_T_BID_HEADER.$inferInsert; type BidItemData = typeof PR_INFORMATION_T_BID_ITEM.$inferInsert; // XML 구조 타입 정의 type BidHeaderXML = ToXMLFields>; type BidItemXML = ToXMLFields>; // 처리된 데이터 구조 interface ProcessedPRData { bidHeader: BidHeaderData; bidItems: BidItemData[]; } export async function GET(request: NextRequest) { const url = new URL(request.url); if (url.searchParams.has('wsdl')) { return serveWsdl('IF_ECC_EVCP_PR_INFORMATION.wsdl'); } return new Response('Method Not Allowed', { status: 405 }); } export async function POST(request: NextRequest) { const url = new URL(request.url); if (url.searchParams.has('wsdl')) { return serveWsdl('IF_ECC_EVCP_PR_INFORMATION.wsdl'); } const body = await request.text(); // SOAP 로깅 래퍼 함수 사용 return withSoapLogging( 'INBOUND', 'S-ERP', 'IF_ECC_EVCP_PR_INFORMATION', body, async () => { console.log('🚀 PR_INFORMATION 수신 시작, 데이터 길이:', body.length); // 1) XML 파싱 const parser = createXMLParser(['T_BID_HEADER', 'T_BID_ITEM']); const parsedData = parser.parse(body); // 2) SOAP Body 또는 루트에서 요청 데이터 추출 const requestData = extractRequestData(parsedData, 'IF_ECC_EVCP_PR_INFORMATIONReq'); if (!requestData) { console.error('유효한 요청 데이터를 찾을 수 없습니다'); throw new Error('Missing request data - IF_ECC_EVCP_PR_INFORMATIONReq not found'); } // 3) XML 데이터를 DB 삽입 가능한 형태로 변환 const processedData = transformPRData(requestData as PRRequestXML); // 4) 필수 필드 검증 for (const prData of processedData) { if (!prData.bidHeader.ANFNR) { throw new Error('Missing required field: ANFNR in Bid Header'); } for (const item of prData.bidItems) { if (!item.ANFNR || !item.ANFPS) { throw new Error('Missing required fields in Bid Item: ANFNR, ANFPS'); } } } // 5) 데이터베이스 저장 await saveToDatabase(processedData); console.log(`🎉 처리 완료: ${processedData.length}개 PR 데이터`); // 6) 성공 응답 반환 return createSoapResponse('http://60.101.108.100/', { 'tns:IF_ECC_EVCP_PR_INFORMATIONRes': { EV_TYPE: 'S', }, }); } ).catch((error) => { // withSoapLogging에서 이미 에러 로그를 처리하므로, 여기서는 응답만 생성 return createSoapResponse('http://60.101.108.100/', { 'tns:IF_ECC_EVCP_PR_INFORMATIONRes': { EV_TYPE: 'E', EV_MESSAGE: error instanceof Error ? error.message.slice(0, 100) : 'Unknown error', }, }); }); } // ----------------------------------------------------------------------------- // 데이터 변환 및 저장 관련 유틸리티 // ----------------------------------------------------------------------------- // Root XML Request 타입 type PRRequestXML = { CHG_GB?: string; T_BID_HEADER?: BidHeaderXML[]; T_BID_ITEM?: BidItemXML[]; }; // XML -> DB 데이터 변환 함수 function transformPRData(requestData: PRRequestXML): ProcessedPRData[] { const headers = requestData.T_BID_HEADER || []; const items = requestData.T_BID_ITEM || []; return headers.map((header) => { const headerKey = header.ANFNR || ''; const fkData = { ANFNR: headerKey }; // Header 변환 const bidHeaderConverted = convertXMLToDBData( header as Record, undefined // Header는 자체 필드만 사용 ); // 해당 Header의 Item들 필터 후 변환 const relatedItems = items.filter((item) => item.ANFNR === headerKey); const bidItemsConverted = processNestedArray( relatedItems, (item) => convertXMLToDBData(item as Record, fkData), fkData ); return { bidHeader: bidHeaderConverted, bidItems: bidItemsConverted, }; }); } // 데이터베이스 저장 함수 async function saveToDatabase(processedPRs: ProcessedPRData[]) { console.log(`데이터베이스 저장 시작: ${processedPRs.length}개 PR 데이터`); try { await db.transaction(async (tx) => { for (const prData of processedPRs) { const { bidHeader, bidItems } = prData; if (!bidHeader.ANFNR) { console.warn('ANFNR가 없는 헤더 발견, 건너뜁니다.'); continue; } // 1. 헤더 테이블 Upsert (ANFNR 기준) await tx .insert(PR_INFORMATION_T_BID_HEADER) .values(bidHeader) .onConflictDoUpdate({ target: PR_INFORMATION_T_BID_HEADER.ANFNR, set: { ...bidHeader, updatedAt: new Date(), }, }); // 2. 아이템 테이블 전체 교체 (ANFNR FK 기준) await replaceSubTableData( tx, PR_INFORMATION_T_BID_ITEM, bidItems, 'ANFNR', bidHeader.ANFNR ); } }); console.log(`데이터베이스 저장 완료: ${processedPRs.length}개 PR`); return true; } catch (error) { console.error('데이터베이스 저장 중 오류 발생:', error); throw error; } }