diff options
| author | joonhoekim <26rote@gmail.com> | 2025-10-30 10:35:26 +0900 |
|---|---|---|
| committer | joonhoekim <26rote@gmail.com> | 2025-10-30 10:35:26 +0900 |
| commit | 284f9f40d9494168f3e68eedd9af067c38362eea (patch) | |
| tree | ac26b3e9fab46b996c16bfa7a7add8f26ddb5042 /lib/pos/sync-rfq-pos-files.ts | |
| parent | 88f13ab89d2250e52c3375b077328a933a5762ec (diff) | |
(김준회) refactor: POS: 온디맨드로 다운로드받도록 변경, 매핑로직에선 pos 관련 로직 제거
Diffstat (limited to 'lib/pos/sync-rfq-pos-files.ts')
| -rw-r--r-- | lib/pos/sync-rfq-pos-files.ts | 407 |
1 files changed, 0 insertions, 407 deletions
diff --git a/lib/pos/sync-rfq-pos-files.ts b/lib/pos/sync-rfq-pos-files.ts deleted file mode 100644 index acb34f20..00000000 --- a/lib/pos/sync-rfq-pos-files.ts +++ /dev/null @@ -1,407 +0,0 @@ -'use server'; - -import db from '@/db/db'; -import { rfqPrItems, rfqLastAttachments, rfqLastAttachmentRevisions } from '@/db/schema/rfqLast'; -import { eq, and, ne } from 'drizzle-orm'; -import { getDcmtmIdByMaterialCode } from './get-dcmtm-id'; -import { getEncryptDocumentumFile } from './get-pos'; -import { downloadPosFile } from './download-pos-file'; -import type { PosFileSyncResult } from './types'; -import path from 'path'; -import fs from 'fs/promises'; -import { revalidatePath } from 'next/cache'; -import { debugLog, debugError, debugSuccess, debugProcess, debugWarn } from '@/lib/debug-utils'; - - -/** - * RFQ의 모든 PR Items의 MATNR로 POS 파일을 조회하고 서버에 다운로드하여 저장 - */ -export async function syncRfqPosFiles( - rfqId: number, - userId: number -): Promise<PosFileSyncResult> { - debugLog(`🚀 POS 파일 동기화 시작`, { rfqId, userId }); - - const result: PosFileSyncResult = { - success: false, - processedCount: 0, - successCount: 0, - failedCount: 0, - errors: [], - details: [] - }; - - try { - // 1. RFQ의 모든 PR Items 조회 (materialCode 중복 제거) - debugProcess(`📋 RFQ PR Items 조회 시작 (RFQ ID: ${rfqId})`); - - const prItems = await db - .selectDistinct({ - materialCode: rfqPrItems.materialCode, - materialDescription: rfqPrItems.materialDescription, - }) - .from(rfqPrItems) - .where(and( - eq(rfqPrItems.rfqsLastId, rfqId), - // materialCode가 null이 아닌 것만 - )) - .then(items => items.filter(item => item.materialCode && item.materialCode.trim() !== '')); - - debugLog(`📦 조회된 PR Items`, { - totalCount: prItems.length, - materialCodes: prItems.map(item => item.materialCode) - }); - - if (prItems.length === 0) { - debugWarn(`⚠️ 처리할 자재코드가 없습니다 (RFQ ID: ${rfqId})`); - result.errors.push('처리할 자재코드가 없습니다.'); - return result; - } - - result.processedCount = prItems.length; - debugSuccess(`✅ 처리할 자재코드 ${prItems.length}개 발견`); - - // 2. 각 자재코드별로 POS 파일 처리 - debugProcess(`🔄 자재코드별 POS 파일 처리 시작`); - - for (const prItem of prItems) { - const materialCode = prItem.materialCode!; - debugLog(`📋 자재코드 처리 시작: ${materialCode}`); - - try { - // 2-1. 자재코드로 DCMTM_ID 조회 - debugProcess(`🔍 DCMTM_ID 조회 시작 (자재코드: ${materialCode})`); - const dcmtmResult = await getDcmtmIdByMaterialCode({ materialCode }); - - debugLog(`🎯 DCMTM_ID 조회 결과`, { - materialCode, - success: dcmtmResult.success, - fileCount: dcmtmResult.files?.length || 0, - files: dcmtmResult.files - }); - - if (!dcmtmResult.success || !dcmtmResult.files || dcmtmResult.files.length === 0) { - debugWarn(`⚠️ DCMTM_ID 조회 실패 또는 파일 없음 (자재코드: ${materialCode})`, dcmtmResult.error); - result.details.push({ - materialCode, - status: 'no_files', - error: dcmtmResult.error || 'POS 파일을 찾을 수 없음' - }); - continue; - } - - // 여러 파일이 있을 경우 첫 번째 파일만 처리 - const posFile = dcmtmResult.files[0]; - debugLog(`📁 처리할 POS 파일 선택`, { - materialCode, - selectedFile: posFile, - totalFiles: dcmtmResult.files.length - }); - - // 2-2. POS API로 파일 경로 가져오기 - debugProcess(`🌐 POS API 호출 시작 (DCMTM_ID: ${posFile.dcmtmId})`); - const posResult = await getEncryptDocumentumFile({ - objectID: posFile.dcmtmId - }); - - debugLog(`🌐 POS API 호출 결과`, { - materialCode, - dcmtmId: posFile.dcmtmId, - success: posResult.success, - resultPath: posResult.result, - error: posResult.error - }); - - if (!posResult.success || !posResult.result) { - debugError(`❌ POS API 호출 실패 (자재코드: ${materialCode})`, posResult.error); - result.details.push({ - materialCode, - fileName: posFile.fileName, - status: 'failed', - error: posResult.error || 'POS 파일 경로 조회 실패' - }); - result.failedCount++; - continue; - } - - // 2-3. 내부망에서 파일 다운로드 - debugProcess(`⬇️ 파일 다운로드 시작 (경로: ${posResult.result})`); - const downloadResult = await downloadPosFile({ - relativePath: posResult.result - }); - - debugLog(`⬇️ 파일 다운로드 결과`, { - materialCode, - success: downloadResult.success, - fileName: downloadResult.fileName, - fileSize: downloadResult.fileBuffer?.length, - mimeType: downloadResult.mimeType, - error: downloadResult.error - }); - - if (!downloadResult.success || !downloadResult.fileBuffer) { - debugError(`❌ 파일 다운로드 실패 (자재코드: ${materialCode})`, downloadResult.error); - result.details.push({ - materialCode, - fileName: posFile.fileName, - status: 'failed', - error: downloadResult.error || '파일 다운로드 실패' - }); - result.failedCount++; - continue; - } - - // 2-4. 서버 파일 시스템에 저장 - debugProcess(`💾 서버 파일 저장 시작 (파일명: ${downloadResult.fileName || `${materialCode}.pdf`})`); - const saveResult = await saveFileToServer( - downloadResult.fileBuffer, - downloadResult.fileName || `${materialCode}.pdf` - ); - - debugLog(`💾 서버 파일 저장 결과`, { - materialCode, - success: saveResult.success, - filePath: saveResult.filePath, - fileName: saveResult.fileName, - error: saveResult.error - }); - - if (!saveResult.success) { - debugError(`❌ 서버 파일 저장 실패 (자재코드: ${materialCode})`, saveResult.error); - result.details.push({ - materialCode, - fileName: posFile.fileName, - status: 'failed', - error: saveResult.error || '서버 파일 저장 실패' - }); - result.failedCount++; - continue; - } - - // 2-5. 데이터베이스에 첨부파일 정보 저장 - debugProcess(`🗄️ DB 첨부파일 정보 저장 시작 (자재코드: ${materialCode})`); - const dbResult = await saveAttachmentToDatabase( - rfqId, - materialCode, - { dcmtmId: posFile.dcmtmId, fileName: posFile.fileName }, - saveResult.filePath!, - saveResult.fileName!, - downloadResult.fileBuffer.length, - downloadResult.mimeType || 'application/pdf', - userId - ); - - debugLog(`🗄️ DB 저장 결과`, { - materialCode, - success: dbResult.success, - error: dbResult.error - }); - - if (!dbResult.success) { - debugError(`❌ DB 저장 실패 (자재코드: ${materialCode})`, dbResult.error); - result.details.push({ - materialCode, - fileName: posFile.fileName, - status: 'failed', - error: dbResult.error || 'DB 저장 실패' - }); - result.failedCount++; - continue; - } - - debugSuccess(`✅ 자재코드 ${materialCode} 처리 완료`); - result.details.push({ - materialCode, - fileName: posFile.fileName, - status: 'success' - }); - result.successCount++; - - } catch (error) { - const errorMessage = error instanceof Error ? error.message : '알 수 없는 오류'; - debugError(`❌ 자재코드 ${materialCode} 처리 중 예외 발생`, { error: errorMessage, stack: error instanceof Error ? error.stack : undefined }); - result.details.push({ - materialCode, - status: 'failed', - error: errorMessage - }); - result.failedCount++; - result.errors.push(`${materialCode}: ${errorMessage}`); - } - } - - result.success = result.successCount > 0; - - debugLog(`📊 POS 파일 동기화 최종 결과`, { - rfqId, - processedCount: result.processedCount, - successCount: result.successCount, - failedCount: result.failedCount, - success: result.success, - errors: result.errors - }); - - // 캐시 무효화 - debugProcess(`🔄 캐시 무효화 (경로: /evcp/rfq-last/${rfqId})`); - revalidatePath(`/evcp/rfq-last/${rfqId}`); - - debugSuccess(`🎉 POS 파일 동기화 완료 (성공: ${result.successCount}건, 실패: ${result.failedCount}건)`); - return result; - - } catch (error) { - const errorMessage = error instanceof Error ? error.message : '알 수 없는 오류'; - debugError(`❌ POS 파일 동기화 전체 처리 오류`, { error: errorMessage, stack: error instanceof Error ? error.stack : undefined }); - result.errors.push(`전체 처리 오류: ${errorMessage}`); - return result; - } -} - -/** - * 파일을 서버 파일 시스템에 저장 - */ -async function saveFileToServer( - fileBuffer: Buffer, - originalFileName: string -): Promise<{ - success: boolean; - filePath?: string; - fileName?: string; - error?: string; -}> { - try { - // uploads/pos 디렉토리 생성 - const uploadDir = path.join(process.cwd(), 'uploads', 'pos'); - - try { - await fs.access(uploadDir); - } catch { - await fs.mkdir(uploadDir, { recursive: true }); - } - - // 고유한 파일명 생성 (타임스탬프 + 원본명) - const timestamp = Date.now(); - const sanitizedFileName = originalFileName.replace(/[^a-zA-Z0-9.-]/g, '_'); - const fileName = `${timestamp}_${sanitizedFileName}`; - const filePath = path.join(uploadDir, fileName); - - // 파일 저장 - await fs.writeFile(filePath, fileBuffer); - - return { - success: true, - filePath: `uploads/pos/${fileName}`, // 상대 경로로 저장 - fileName: originalFileName - }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : '파일 저장 실패' - }; - } -} - -/** - * 첨부파일 정보를 데이터베이스에 저장 - */ -async function saveAttachmentToDatabase( - rfqId: number, - materialCode: string, - posFileInfo: { dcmtmId: string; fileName: string }, - filePath: string, - originalFileName: string, - fileSize: number, - fileType: string, - userId: number -): Promise<{ - success: boolean; - error?: string; -}> { - try { - await db.transaction(async (tx) => { - // 1. 기존 동일한 자재코드의 설계 첨부파일이 있는지 확인 - const existingAttachment = await tx - .select() - .from(rfqLastAttachments) - .where(and( - eq(rfqLastAttachments.rfqId, rfqId), - eq(rfqLastAttachments.attachmentType, '설계'), - eq(rfqLastAttachments.serialNo, materialCode) - )) - .limit(1); - - let attachmentId: number; - - if (existingAttachment.length > 0) { - // 기존 첨부파일이 있으면 업데이트 - attachmentId = existingAttachment[0].id; - - await tx - .update(rfqLastAttachments) - .set({ - currentRevision: 'Rev.1', // 새 리비전으로 업데이트 - description: `${posFileInfo.fileName} (자재코드: ${materialCode})`, - updatedAt: new Date() - }) - .where(eq(rfqLastAttachments.id, attachmentId)); - } else { - // 새 첨부파일 생성 - const [newAttachment] = await tx - .insert(rfqLastAttachments) - .values({ - attachmentType: '설계', - serialNo: materialCode, - rfqId, - currentRevision: 'Rev.0', - description: `${posFileInfo.fileName} (자재코드: ${materialCode})`, - createdBy: userId, - createdAt: new Date(), - updatedAt: new Date() - }) - .returning({ id: rfqLastAttachments.id }); - - attachmentId = newAttachment.id; - } - - // 2. 새 리비전 생성 - const [newRevision] = await tx - .insert(rfqLastAttachmentRevisions) - .values({ - attachmentId, - revisionNo: 'Rev.0', - fileName: path.basename(filePath), - originalFileName, - filePath, - fileSize, - fileType, - isLatest: true, - revisionComment: `POS 시스템에서 자동 동기화됨 (DCMTM_ID: ${posFileInfo.dcmtmId})`, - createdBy: userId - }) - .returning({ id: rfqLastAttachmentRevisions.id }); - - // 3. 첨부파일의 latestRevisionId 업데이트 - await tx - .update(rfqLastAttachments) - .set({ - latestRevisionId: newRevision.id - }) - .where(eq(rfqLastAttachments.id, attachmentId)); - - // 4. 기존 리비전들의 isLatest를 false로 업데이트 - await tx - .update(rfqLastAttachmentRevisions) - .set({ isLatest: false }) - .where(and( - eq(rfqLastAttachmentRevisions.attachmentId, attachmentId), - ne(rfqLastAttachmentRevisions.id, newRevision.id) - )); - }); - - return { success: true }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'DB 저장 실패' - }; - } -} |
