import { NextRequest, NextResponse } from 'next/server' import db from '@/db/db' import { revisions, issueStages, documents, projects } from '@/db/schema' import { eq, sql } from 'drizzle-orm' import { alias } from 'drizzle-orm/pg-core' import { debugLog, debugError, debugSuccess } from '@/lib/debug-utils' // DOLCE Detail Document 타입 정의 interface DOLCEDetailDocument { RegisterSerialNoMax?: string | number // 다른 필드들도 필요시 추가 } export async function GET(request: NextRequest) { try { const { searchParams } = new URL(request.url) const documentId = searchParams.get('documentId') debugLog('1. Input documentId:', documentId) if (!documentId) { debugLog('2. documentId is missing, returning 400') return NextResponse.json( { error: 'documentId is required' }, { status: 400 } ) } const parsedDocumentId = parseInt(documentId) debugLog('3. Parsed documentId:', parsedDocumentId) // 1. 내부 DB에서 최대 serialNo 조회 const r = alias(revisions, 'r') const is = alias(issueStages, 'is') debugLog('4. Created aliases - r:', r, 'is:', is) const maxSerialResult = await db .select({ maxSerial: sql` COALESCE(MAX(CAST(${r.serialNo} AS INTEGER)), 0) `, maxRegisterSerial: sql` COALESCE(MAX(CAST(${r.registerSerialNoMax} AS INTEGER)), 0) ` }) .from(r) .innerJoin( is, eq(r.issueStageId, is.id) ) .where(eq(is.documentId, parsedDocumentId)) debugLog('5. Internal DB query result:', maxSerialResult) const internalMaxSerial = maxSerialResult[0]?.maxSerial || 0 const internalMaxRegisterSerial = maxSerialResult[0]?.maxRegisterSerial || 0 const internalMaxSerialNo = Math.max(internalMaxSerial, internalMaxRegisterSerial) debugLog('6. Internal maxSerialNo:', internalMaxSerialNo) // 2. Document 정보 조회 (DOLCE API 호출을 위해) const documentInfo = await db .select({ docNumber: documents.docNumber, drawingKind: documents.drawingKind, discipline: documents.discipline, projectId: documents.projectId, projectCode: projects.code }) .from(documents) .leftJoin(projects, eq(documents.projectId, projects.id)) .where(eq(documents.id, parsedDocumentId)) .limit(1) debugLog('7. Document info:', documentInfo[0]) if (!documentInfo[0]) { debugError('Document not found for id:', parsedDocumentId) return NextResponse.json( { error: 'Document not found' }, { status: 404 } ) } const { docNumber, drawingKind, discipline, projectCode } = documentInfo[0] // 필수 필드 검증 if (!projectCode || !docNumber || !discipline || !drawingKind) { debugLog('8. Missing required fields for DOLCE API') debugLog(' - projectCode:', projectCode) debugLog(' - docNumber:', docNumber) debugLog(' - discipline:', discipline) debugLog(' - drawingKind:', drawingKind) // DOLCE API 호출 불가능한 경우, 내부 DB 값만 사용 return NextResponse.json({ maxSerialNo: internalMaxSerialNo, nextSerialNo: internalMaxSerialNo + 1, documentId: documentId, source: 'internal_only', debug: { parsedDocumentId, internalMaxSerialNo, missingFields: { projectCode: !projectCode, docNumber: !docNumber, discipline: !discipline, drawingKind: !drawingKind } } }) } // 3. DOLCE API 호출 let dolceMaxSerialNo = 0 try { debugLog('9. Calling DOLCE API...') const dolceDocuments = await fetchDetailFromDOLCE( projectCode, docNumber, discipline, drawingKind ) debugLog('10. DOLCE API response:', dolceDocuments) if (dolceDocuments && dolceDocuments.length > 0) { // 첫 번째 문서의 RegisterSerialNoMax 값 사용 const firstDoc = dolceDocuments[0] if (firstDoc.RegisterSerialNoMax) { dolceMaxSerialNo = parseInt(String(firstDoc.RegisterSerialNoMax)) || 0 debugLog('11. DOLCE maxSerialNo:', dolceMaxSerialNo) } } } catch (error) { debugError('DOLCE API call failed:', error) // DOLCE API 실패 시에도 내부 DB 값은 사용 } // 4. 내부 DB와 DOLCE 값 중 최대값 선택 const finalMaxSerialNo = Math.max(internalMaxSerialNo, dolceMaxSerialNo) debugSuccess('12. Final maxSerialNo:', finalMaxSerialNo) debugSuccess('13. Next serialNo:', finalMaxSerialNo + 1) return NextResponse.json({ maxSerialNo: finalMaxSerialNo, nextSerialNo: finalMaxSerialNo + 1, documentId: documentId, source: dolceMaxSerialNo > internalMaxSerialNo ? 'dolce' : 'internal', debug: { parsedDocumentId, internalMaxSerialNo, dolceMaxSerialNo, documentInfo: { projectCode, docNumber, discipline, drawingKind } } }) } catch (error) { debugError('Error fetching max serial no:', error) debugError('Error stack:', error?.stack) return NextResponse.json( { error: 'Failed to fetch max serial number', details: error?.message }, { status: 500 } ) } } // DOLCE Detail API 호출 함수 async function fetchDetailFromDOLCE( projectCode: string, drawingNo: string, discipline: string, drawingKind: string ): Promise { const endpoint = process.env.DOLCE_DOC_DETAIL_API_URL || 'http://60.100.99.217:1111/Services/VDCSWebService.svc/DetailDwgReceiptMgmt' const requestBody = { project: projectCode, drawingNo: drawingNo, discipline: discipline, drawingKind: drawingKind } console.log(`Fetching detail from DOLCE: ${projectCode} - ${drawingNo}`) console.log('Request body:', requestBody) try { const response = await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(requestBody) }) if (!response.ok) { const errorText = await response.text() throw new Error(`DOLCE Detail API failed: HTTP ${response.status} - ${errorText}`) } const data = await response.json() // DOLCE Detail API 응답 구조에 맞게 처리 if (data.DetailDwgReceiptMgmtResult) { const documents = data.DetailDwgReceiptMgmtResult as DOLCEDetailDocument[] console.log(`Found ${documents.length} detail records for ${drawingNo}`) return documents } else { console.warn(`Unexpected DOLCE Detail response structure:`, data) return [] } } catch (error) { console.error(`DOLCE Detail API call failed for ${drawingNo}:`, error) throw error } }