diff options
Diffstat (limited to 'app/api')
| -rw-r--r-- | app/api/(S-ERP)/(ECC)/IF_ECC_EVCP_PR_INFORMATION/route.ts | 199 | ||||
| -rw-r--r-- | app/api/mdg/send-vendor-xml/route.ts | 28 |
2 files changed, 199 insertions, 28 deletions
diff --git a/app/api/(S-ERP)/(ECC)/IF_ECC_EVCP_PR_INFORMATION/route.ts b/app/api/(S-ERP)/(ECC)/IF_ECC_EVCP_PR_INFORMATION/route.ts new file mode 100644 index 00000000..3b7636f9 --- /dev/null +++ b/app/api/(S-ERP)/(ECC)/IF_ECC_EVCP_PR_INFORMATION/route.ts @@ -0,0 +1,199 @@ +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<Omit<BidHeaderData, 'id' | 'createdAt' | 'updatedAt'>>; +type BidItemXML = ToXMLFields<Omit<BidItemData, 'id' | 'createdAt' | 'updatedAt'>>; + +// 처리된 데이터 구조 +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<BidHeaderData>( + header as Record<string, string | undefined>, + undefined // Header는 자체 필드만 사용 + ); + + // 해당 Header의 Item들 필터 후 변환 + const relatedItems = items.filter((item) => item.ANFNR === headerKey); + + const bidItemsConverted = processNestedArray( + relatedItems, + (item) => + convertXMLToDBData<BidItemData>(item as Record<string, string | undefined>, 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; + } +}
\ No newline at end of file diff --git a/app/api/mdg/send-vendor-xml/route.ts b/app/api/mdg/send-vendor-xml/route.ts deleted file mode 100644 index 7f8d1daf..00000000 --- a/app/api/mdg/send-vendor-xml/route.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server'; -import { sendVendorEnvelopeToMDG } from '@/lib/soap/mdg/send/vendor-master/action'; - -export async function POST(request: NextRequest) { - try { - const { envelope } = await request.json(); - - if (!envelope || typeof envelope !== 'string') { - return NextResponse.json( - { success: false, message: 'envelope(XML) is required' }, - { status: 400 } - ); - } - - const result = await sendVendorEnvelopeToMDG(envelope); - - return NextResponse.json(result); - } catch (error) { - console.error('[send-vendor-xml] error:', error); - return NextResponse.json( - { - success: false, - message: error instanceof Error ? error.message : 'Unknown error', - }, - { status: 500 } - ); - } -}
\ No newline at end of file |
