import { NextRequest } from 'next/server'; import db from '@/db/db'; import { biddingProjects, projectSeries } from '@/db/schema/projects'; import { ToXMLFields, serveWsdl, createXMLParser, extractRequestData, convertXMLToDBData, createSoapResponse, withSoapLogging, } from '@/lib/soap/utils'; import { bulkUpsert, bulkReplaceSubTableData } from "@/lib/soap/batch-utils"; // 스키마에서 타입 추론 type BiddingProjectData = typeof biddingProjects.$inferInsert; type ProjectSeriesData = typeof projectSeries.$inferInsert; // XML 구조 타입 정의 (XML에서 오는 추가 필드 포함) type BiddingProjectXML = ToXMLFields> & { shipMHull?: string; // XML에서 오는 추가 필드 }; type ProjectSeriesXML = ToXMLFields>; // 처리된 데이터 구조 interface ProcessedBiddingData { biddingProjects: BiddingProjectData[]; projectSeries: ProjectSeriesData[]; } // GET 요청 처리는 ?wsdl 달고 있으면 WSDL 서비스 제공 export async function GET(request: NextRequest) { const url = new URL(request.url); if (url.searchParams.has('wsdl')) { return serveWsdl('IF_ECC_EVCP_BIDDING_PROJECT.wsdl'); } return new Response('Method Not Allowed', { status: 405 }); } // POST 요청이 데이터 적재 요구 (SOAP) export async function POST(request: NextRequest) { const url = new URL(request.url); if (url.searchParams.has('wsdl')) { return serveWsdl('IF_ECC_EVCP_BIDDING_PROJECT.wsdl'); } const body = await request.text(); // SOAP 로깅 래퍼 함수 사용 return withSoapLogging( 'INBOUND', 'ECC', 'IF_ECC_EVCP_BIDDING_PROJECT', body, async () => { console.log('🚀 BIDDING_PROJECT 수신 시작, 데이터 길이:', body.length); // 1) XML 파싱 const parser = createXMLParser(['biddingProjects', 'projectSeries']); const parsedData = parser.parse(body); // 2) SOAP Body 또는 루트에서 요청 데이터 추출 const requestData = extractRequestData(parsedData, 'IF_ECC_EVCP_BIDDING_PROJECTReq'); if (!requestData) { console.error('유효한 요청 데이터를 찾을 수 없습니다'); throw new Error('Missing request data - IF_ECC_EVCP_BIDDING_PROJECTReq not found'); } // 3) XML 데이터를 DB 삽입 가능한 형태로 변환 const processedData = transformBiddingData(requestData as BiddingRequestXML); // 4) 필수 필드 검증 for (const project of processedData.biddingProjects) { if (!project.pspid) { throw new Error('Missing required field: pspid in Bidding Project'); } } for (const series of processedData.projectSeries) { if (!series.pspid || !series.sersNo) { throw new Error('Missing required fields in Project Series: pspid, sersNo'); } } // 5) 데이터베이스 저장 await saveToDatabase(processedData); console.log(`🎉 처리 완료: ${processedData.biddingProjects.length}개 프로젝트, ${processedData.projectSeries.length}개 시리즈`); // 6) 성공 응답 반환 return createSoapResponse('http://60.101.108.100/', { 'tns:IF_ECC_EVCP_BIDDING_PROJECTRes': { EV_TYPE: 'S', }, }); } ).catch((error) => { // withSoapLogging에서 이미 에러 로그를 처리하므로, 여기서는 응답만 생성 return createSoapResponse('http://60.101.108.100/', { 'tns:IF_ECC_EVCP_BIDDING_PROJECTRes': { EV_TYPE: 'E', EV_MESSAGE: error instanceof Error ? error.message.slice(0, 100) : 'Unknown error', }, }); }); } // ----------------------------------------------------------------------------- // 데이터 변환 및 저장 관련 유틸리티 // ----------------------------------------------------------------------------- // Root XML Request 타입 type BiddingRequestXML = { biddingProjects?: BiddingProjectXML[]; projectSeries?: ProjectSeriesXML[]; }; // XML -> DB 데이터 변환 함수 function transformBiddingData(requestData: BiddingRequestXML): ProcessedBiddingData { const projects = requestData.biddingProjects || []; const series = requestData.projectSeries || []; // Bidding Projects 변환 const biddingProjectsConverted = projects.map((project) => { const converted = convertXMLToDBData( project as Record, undefined ); // shipMHull 값을 기준으로 pjtType 결정 if (project.shipMHull) { converted.pjtType = 'HULL'; // shipMHull 값이 존재하면 해양(HULL) } else { converted.pjtType = 'SHIP'; // shipMHull 값이 없으면 조선(SHIP) } return converted; }); // Project Series 변환 const projectSeriesConverted = series.map((seriesItem) => { return convertXMLToDBData( seriesItem as Record, undefined ); }); return { biddingProjects: biddingProjectsConverted, projectSeries: projectSeriesConverted, }; } // 데이터베이스 저장 함수 async function saveToDatabase(processedData: ProcessedBiddingData) { console.log(`데이터베이스(배치) 저장 시작: ${processedData.biddingProjects.length}개 프로젝트, ${processedData.projectSeries.length}개 시리즈`); try { await db.transaction(async (tx) => { // 1) Bidding Projects UPSERT (배치) const biddingProjectRows = processedData.biddingProjects.filter((p): p is BiddingProjectData => !!p.pspid); await bulkUpsert(tx, biddingProjects, biddingProjectRows, 'pspid'); // 2) Project Series 교체 (배치) const projectKeys = biddingProjectRows.map((p) => p.pspid as string); await bulkReplaceSubTableData(tx, projectSeries, processedData.projectSeries, projectSeries.pspid, projectKeys); }); console.log(`데이터베이스(배치) 저장 완료: ${processedData.biddingProjects.length}개 프로젝트, ${processedData.projectSeries.length}개 시리즈`); return true; } catch (error) { console.error('데이터베이스(배치) 저장 중 오류 발생:', error); throw error; } }