diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-07-07 01:44:45 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-07-07 01:44:45 +0000 |
| commit | 90f79a7a691943a496f67f01c1e493256070e4de (patch) | |
| tree | 37275fde3ae08c2bca384fbbc8eb378de7e39230 /lib/vendor-document-list | |
| parent | fbb3b7f05737f9571b04b0a8f4f15c0928de8545 (diff) | |
(대표님) 변경사항 20250707 10시 43분 - unstaged 변경사항 추가
Diffstat (limited to 'lib/vendor-document-list')
| -rw-r--r-- | lib/vendor-document-list/dolce-upload-service.ts | 501 | ||||
| -rw-r--r-- | lib/vendor-document-list/import-service.ts | 169 |
2 files changed, 420 insertions, 250 deletions
diff --git a/lib/vendor-document-list/dolce-upload-service.ts b/lib/vendor-document-list/dolce-upload-service.ts index 8e9d386b..54672f33 100644 --- a/lib/vendor-document-list/dolce-upload-service.ts +++ b/lib/vendor-document-list/dolce-upload-service.ts @@ -16,6 +16,11 @@ export interface DOLCEUploadResult { } } +interface FileReaderConfig { + baseDir: string; + isProduction: boolean; +} + interface DOLCEDocument { Mode: "ADD" | "MOD" Status: string @@ -65,6 +70,24 @@ interface DOLCEFileMapping { UploadId: string } +function getFileReaderConfig(): FileReaderConfig { + const isProduction = process.env.NODE_ENV === "production"; + + if (isProduction) { + return { + baseDir: process.env.NAS_PATH || "/evcp_nas", // NAS 기본 경로 + isProduction: true, + }; + } else { + return { + baseDir: path.join(process.cwd(), "public"), // 개발환경 public 폴더 + isProduction: false, + }; + } +} + + + class DOLCEUploadService { private readonly BASE_URL = process.env.DOLCE_API_URL || 'http://60.100.99.217:1111' private readonly UPLOAD_SERVICE_URL = process.env.DOLCE_UPLOAD_URL || 'http://60.100.99.217:1111/PWPUploadService.ashx' @@ -80,13 +103,13 @@ class DOLCEUploadService { ): Promise<DOLCEUploadResult> { try { console.log(`Starting DOLCE upload for contract ${contractId}, revisions: ${revisionIds.join(', ')}`) - + // 1. 계약 정보 조회 (프로젝트 코드, 벤더 코드 등) const contractInfo = await this.getContractInfo(contractId) if (!contractInfo) { throw new Error(`Contract info not found for ID: ${contractId}`) } - + // 2. 업로드할 리비전 정보 조회 const revisionsToUpload = await this.getRevisionsForUpload(revisionIds) if (revisionsToUpload.length === 0) { @@ -96,9 +119,7 @@ class DOLCEUploadService { uploadedFiles: 0 } } - - // 3. 각 issueStageId별로 첫 번째 revision 정보를 미리 조회 (Mode 결정용) - + let uploadedDocuments = 0 let uploadedFiles = 0 const errors: string[] = [] @@ -107,69 +128,63 @@ class DOLCEUploadService { fileResults: [], mappingResults: [] } - - // 4. 각 리비전별로 처리 - // 4. 각 리비전별로 처리 + + // 3. 각 리비전별로 처리 for (const revision of revisionsToUpload) { try { console.log(`Processing revision ${revision.revision} for document ${revision.documentNo}`) - - // 4-1. 파일이 있는 경우 먼저 업로드 + + // 3-1. UploadId 미리 생성 (파일이 있는 경우에만) let uploadId: string | undefined if (revision.attachments && revision.attachments.length > 0) { - // ✅ userId를 uploadFiles 메서드에 전달 - const fileUploadResults = await this.uploadFiles(revision.attachments, userId) - - if (fileUploadResults.length > 0) { - uploadId = fileUploadResults[0].uploadId // 첫 번째 파일의 UploadId 사용 - uploadedFiles += fileUploadResults.length - results.fileResults.push(...fileUploadResults) - } + uploadId = uuidv4() // 문서 업로드 시 사용할 UploadId 미리 생성 + console.log(`Generated UploadId for document upload: ${uploadId}`) } - - // 4-2. 문서 정보 업로드 + + // 3-2. 문서 정보 업로드 (UploadId 포함) const dolceDoc = this.transformToDoLCEDocument( revision, contractInfo, - uploadId, + uploadId, // 미리 생성된 UploadId 사용 contractInfo.vendorCode, ) - + const docResult = await this.uploadDocument([dolceDoc], userId) - if (docResult.success) { - uploadedDocuments++ - results.documentResults.push(docResult) - - // 4-3. 파일이 있는 경우 매핑 정보 전송 - if (uploadId && revision.attachments && revision.attachments.length > 0) { - const mappingData = this.transformToFileMapping( - revision, - contractInfo, - uploadId, - revision.attachments[0].fileName + if (!docResult.success) { + errors.push(`Document upload failed for ${revision.documentNo}: ${docResult.error}`) + continue // 문서 업로드 실패 시 다음 리비전으로 넘어감 + } + + uploadedDocuments++ + results.documentResults.push(docResult) + console.log(`✅ Document uploaded successfully: ${revision.documentNo}`) + + // 3-3. 파일 업로드 (이미 생성된 UploadId 사용) + if (uploadId && revision.attachments && revision.attachments.length > 0) { + try { + // 파일 업로드 시 이미 생성된 UploadId 사용 + const fileUploadResults = await this.uploadFiles( + revision.attachments, + userId, + uploadId // 이미 생성된 UploadId 전달 ) - - const mappingResult = await this.uploadFileMapping([mappingData], userId) - if (mappingResult.success) { - results.mappingResults.push(mappingResult) - } else { - errors.push(`File mapping failed for ${revision.documentNo}: ${mappingResult.error}`) - } + + } catch (fileError) { + errors.push(`File upload failed for ${revision.documentNo}: ${fileError instanceof Error ? fileError.message : 'Unknown error'}`) + console.error(`❌ File upload failed for ${revision.documentNo}:`, fileError) } - - // 4-4. 성공한 리비전의 상태 업데이트 - await this.updateRevisionStatus(revision.id, 'SUBMITTED', uploadId) - - } else { - errors.push(`Document upload failed for ${revision.documentNo}: ${docResult.error}`) } - + + // 3-5. 성공한 리비전의 상태 업데이트 + await this.updateRevisionStatus(revision.id, 'SUBMITTED', uploadId) + } catch (error) { const errorMessage = `Failed to process revision ${revision.revision}: ${error instanceof Error ? error.message : 'Unknown error'}` errors.push(errorMessage) console.error(errorMessage, error) } } + return { success: errors.length === 0, uploadedDocuments, @@ -177,13 +192,12 @@ class DOLCEUploadService { errors: errors.length > 0 ? errors : undefined, results } - + } catch (error) { console.error('DOLCE upload failed:', error) throw error } } - /** * 계약 정보 조회 */ @@ -312,75 +326,77 @@ class DOLCEUploadService { return revisionsWithAttachments } - /** - * 파일 업로드 (PWPUploadService.ashx) - */ - /** - * 파일 업로드 (PWPUploadService.ashx) - DB 업데이트 포함 - */ - private async uploadFiles( - attachments: any[], - userId: string - ): Promise<Array<{ uploadId: string, fileId: string, filePath: string }>> { - const uploadResults = [] - - for (const attachment of attachments) { - try { - // UploadId와 FileId 생성 (UUID 형태) - const uploadId = uuidv4() - const fileId = uuidv4() - - // 파일 데이터 읽기 (실제 구현에서는 파일 시스템이나 S3에서 읽어옴) - const fileBuffer = await this.getFileBuffer(attachment.filePath) - - const uploadUrl = `${this.UPLOAD_SERVICE_URL}?UploadId=${uploadId}&FileId=${fileId}` - - const response = await fetch(uploadUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/octet-stream', - }, - body: fileBuffer - }) +/** + * 파일 업로드 (PWPUploadService.ashx) - 수정된 버전 + * @param attachments 업로드할 첨부파일 목록 + * @param userId 사용자 ID + * @param uploadId 이미 생성된 UploadId (문서 업로드 시 생성됨) + */ +private async uploadFiles( + attachments: any[], + userId: string, + uploadId: string // 이미 생성된 UploadId를 매개변수로 받음 +): Promise<Array<{ uploadId: string, fileId: string, filePath: string }>> { + const uploadResults = [] - if (!response.ok) { - const errorText = await response.text() - throw new Error(`File upload failed: HTTP ${response.status} - ${errorText}`) - } + for (const attachment of attachments) { + try { + // FileId만 새로 생성 (UploadId는 이미 생성된 것 사용) + const fileId = uuidv4() - const dolceFilePath = await response.text() // DOLCE에서 반환하는 파일 경로 - - // ✅ 업로드 성공 후 documentAttachments 테이블 업데이트 - await db - .update(documentAttachments) - .set({ - uploadId: uploadId, - fileId: fileId, - uploadedBy: userId, - dolceFilePath: dolceFilePath, - uploadedAt: new Date(), - updatedAt: new Date() - }) - .where(eq(documentAttachments.id, attachment.id)) - - uploadResults.push({ - uploadId, - fileId, - filePath: dolceFilePath - }) + console.log(`Uploading file with predefined UploadId: ${uploadId}, FileId: ${fileId}`) + + // 파일 데이터 읽기 + const fileBuffer = await this.getFileBuffer(attachment.filePath) - console.log(`✅ File uploaded successfully: ${attachment.fileName} -> ${dolceFilePath}`) - console.log(`✅ DB updated for attachment ID: ${attachment.id}`) + const uploadUrl = `${this.UPLOAD_SERVICE_URL}?UploadId=${uploadId}&FileId=${fileId}` - } catch (error) { - console.error(`❌ File upload failed for ${attachment.fileName}:`, error) - throw error + const response = await fetch(uploadUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/octet-stream', + }, + body: fileBuffer + }) + + if (!response.ok) { + const errorText = await response.text() + throw new Error(`File upload failed: HTTP ${response.status} - ${errorText}`) } - } - return uploadResults + const dolceFilePath = await response.text() // DOLCE에서 반환하는 파일 경로 + + // 업로드 성공 후 documentAttachments 테이블 업데이트 + await db + .update(documentAttachments) + .set({ + uploadId: uploadId, // 이미 생성된 UploadId 사용 + fileId: fileId, + uploadedBy: userId, + dolceFilePath: dolceFilePath, + uploadedAt: new Date(), + updatedAt: new Date() + }) + .where(eq(documentAttachments.id, attachment.id)) + + uploadResults.push({ + uploadId, + fileId, + filePath: dolceFilePath + }) + + console.log(`✅ File uploaded successfully: ${attachment.fileName} -> ${dolceFilePath}`) + console.log(`✅ DB updated for attachment ID: ${attachment.id}`) + + } catch (error) { + console.error(`❌ File upload failed for ${attachment.fileName}:`, error) + throw error + } } + return uploadResults +} + /** * 문서 정보 업로드 (DetailDwgReceiptMgmtEdit) */ @@ -468,98 +484,125 @@ class DOLCEUploadService { /** * 리비전 데이터를 DOLCE 문서 형태로 변환 (업데이트된 스키마 사용) */ - private transformToDoLCEDocument( - revision: any, - contractInfo: any, - uploadId?: string, - vendorCode?: string, - ): DOLCEDocument { - // Mode 결정: 해당 issueStageId의 첫 번째 revision인지 확인 - let mode: "ADD" | "MOD" = "MOD" // 기본값은 MOD\ - - - if(revision.registerId){ - mode = "MOD" - } else{ - mode = "ADD" - } - - // RegisterKind 결정: stageName에 따라 설정 - let registerKind = "APPC" // 기본값 +/** + * 리비전 데이터를 DOLCE 문서 형태로 변환 (업데이트된 스키마 사용) + */ +private transformToDoLCEDocument( + revision: any, + contractInfo: any, + uploadId?: string, + vendorCode?: string, +): DOLCEDocument { + // Mode 결정: registerId가 있으면 MOD, 없으면 ADD + let mode: "ADD" | "MOD" = "ADD" // 기본값은 ADD + + if (revision.registerId) { + mode = "MOD" + } else { + mode = "ADD" + } - if (revision.stageName) { - const stageNameLower = revision.stageName.toLowerCase() + // RegisterKind 결정: usage와 usageType에 따라 설정 + let registerKind = "APPR" // 기본값 - if (revision.drawingKind === "B4") { - // B4: 기존 로직 - if (stageNameLower.includes("pre")) { - registerKind = "RECP" - } else if (stageNameLower.includes("working")) { - registerKind = "RECW" - } - } else if (revision.drawingKind === "B5") { - // B5: FMEA 관련 - if (stageNameLower.includes("pre")) { - registerKind = "FMEA-R1" - } else if (stageNameLower.includes("working")) { - registerKind = "FMEA-R2" + if (revision.usage && revision.usage !== 'DEFAULT') { + switch (revision.usage) { + case "APPROVAL": + if (revision.usageType === "Full") { + registerKind = "APPR" + } else if (revision.usageType === "Partial") { + registerKind = "APPR-P" + } else { + registerKind = "APPR" // 기본값 } - } else if (revision.drawingKind === "B3") { - // B3: WORK/APPC - if (stageNameLower.includes("work") && revision.usage.includes('Partial')) { - registerKind = "WORK-P" - } else if (stageNameLower.includes("work") && revision.usage.includes('Full')) { + break + + case "WORKING": + if (revision.usageType === "Full") { registerKind = "WORK" - } else if (stageNameLower.includes("approval") && revision.usage.includes('Partial')) { - registerKind = "APPC-P" - } - else if (stageNameLower.includes("approval") && revision.usage.includes('Full')) { - registerKind = "APPC" + } else if (revision.usageType === "Partial") { + registerKind = "WORK-P" + } else { + registerKind = "WORK" // 기본값 } - } - } + break - const getSerialNumber = (revisionValue: string): number => { - // 먼저 숫자인지 확인 - const numericValue = parseInt(revisionValue) - if (!isNaN(numericValue)) { - return numericValue - } + case "The 1st": + registerKind = "FMEA-1" + break - // 문자인 경우 (a=1, b=2, c=3, ...) - if (typeof revisionValue === 'string' && revisionValue.length === 1) { - const charCode = revisionValue.toLowerCase().charCodeAt(0) - if (charCode >= 97 && charCode <= 122) { // a-z - return charCode - 96 // a=1, b=2, c=3, ... - } - } + case "The 2nd": + registerKind = "FMEA-2" + break + + case "Pre": + registerKind = "RECP" + break + + case "Working": + registerKind = "RECW" + break - // 기본값 + case "Mark-Up": + registerKind = "CMTM" + break + + default: + console.warn(`Unknown usage type: ${revision.usage}, using default APPR`) + registerKind = "APPR" // 기본값 + break + } + } else { + console.warn(`No usage specified for revision ${revision.revision}, using default APPR`) + } + + // Serial Number 계산 함수 + const getSerialNumber = (revisionValue: string): number => { + if (!revisionValue) { return 1 } + // 먼저 숫자인지 확인 + const numericValue = parseInt(revisionValue) + if (!isNaN(numericValue)) { + return numericValue + } - return { - Mode: mode, - Status: revision.revisionStatus || "Standby", - RegisterId: revision.externalRegisterId, // 업데이트된 필드 사용 - ProjectNo: contractInfo.projectCode, - Discipline: revision.discipline, - DrawingKind: revision.drawingKind, - DrawingNo: revision.documentNo, - DrawingName: revision.documentName, - RegisterGroupId: revision.registerGroupId || 0, - RegisterSerialNo: getSerialNumber(revision.revision || "1"), - RegisterKind: registerKind, // stageName에 따라 동적 설정 - DrawingRevNo: revision.revision || "-", - Category: revision.category || "TS", - Receiver: null, - Manager: revision.managerNo || "202206", // 담당자 번호 사용 - RegisterDesc: revision.comment || "System upload", - UploadId: uploadId, - RegCompanyCode: vendorCode || "A0005531" // 벤더 코드 + // 문자인 경우 (a=1, b=2, c=3, ...) + if (typeof revisionValue === 'string' && revisionValue.length === 1) { + const charCode = revisionValue.toLowerCase().charCodeAt(0) + if (charCode >= 97 && charCode <= 122) { // a-z + return charCode - 96 // a=1, b=2, c=3, ... + } } + + // 기본값 + return 1 } + + console.log(`Transform to DOLCE: Mode=${mode}, RegisterKind=${registerKind}, Usage=${revision.usage}, UsageType=${revision.usageType}`) + + return { + Mode: mode, + Status: revision.revisionStatus || "Standby", + RegisterId: revision.registerId || 0, // registerId가 없으면 0 (ADD 모드) + ProjectNo: contractInfo.projectCode, + Discipline: revision.discipline || "DL", + DrawingKind: revision.drawingKind || "B3", + DrawingNo: revision.documentNo, + DrawingName: revision.documentName, + RegisterGroupId: revision.registerGroupId || 0, + RegisterSerialNo: getSerialNumber(revision.revision || "1"), + RegisterKind: registerKind, // usage/usageType에 따라 동적 설정 + DrawingRevNo: revision.revision || "-", + Category: revision.category || "TS", + Receiver: null, + Manager: revision.managerNo || "202206", // 담당자 번호 사용 + RegisterDesc: revision.comment || "System upload", + UploadId: uploadId, + RegCompanyCode: vendorCode || "A0005531" // 벤더 코드 + } +} /** * 파일 매핑 데이터 변환 */ @@ -598,54 +641,74 @@ class DOLCEUploadService { } } + + /** * 파일 버퍼 읽기 (실제 파일 시스템 기반) - 타입 에러 수정 */ private async getFileBuffer(filePath: string): Promise<ArrayBuffer> { try { - console.log(`Reading file from path: ${filePath}`) - + console.log(`📂 파일 읽기 요청: ${filePath}`); + if (filePath.startsWith('http')) { - // URL인 경우 직접 다운로드 - const response = await fetch(filePath) + // ✅ URL인 경우 직접 다운로드 (기존과 동일) + console.log(`🌐 HTTP URL에서 파일 다운로드: ${filePath}`); + + const response = await fetch(filePath); if (!response.ok) { - throw new Error(`Failed to download file: ${response.status}`) + throw new Error(`파일 다운로드 실패: ${response.status}`); } - return await response.arrayBuffer() + + const arrayBuffer = await response.arrayBuffer(); + console.log(`✅ HTTP 다운로드 완료: ${arrayBuffer.byteLength} bytes`); + + return arrayBuffer; } else { - // 로컬 파일 경로인 경우 - const fs = await import('fs') - const path = await import('path') - - let actualFilePath: string - + // ✅ 로컬/NAS 파일 경로 처리 (환경별 분기) + const fs = await import('fs'); + const path = await import('path'); + const config = getFileReaderConfig(); + + let actualFilePath: string; + + // 경로 형태별 처리 if (filePath.startsWith('/documents/')) { - // DB에 저장된 경로 형태: "/documents/[uuid].ext" - // 실제 파일 시스템 경로로 변환: "public/documents/[uuid].ext" - actualFilePath = path.join(process.cwd(), 'public', filePath) - } else if (filePath.startsWith('/')) { - // 절대 경로인 경우 public 디렉토리 기준으로 변환 - actualFilePath = path.join(process.cwd(), 'public', filePath) - } else { - // 상대 경로인 경우 그대로 사용 - actualFilePath = filePath + // ✅ DB에 저장된 경로 형태: "/documents/[uuid].ext" + // 개발: public/documents/[uuid].ext + // 프로덕션: /evcp_nas/documents/[uuid].ext + actualFilePath = path.join(config.baseDir, filePath.substring(1)); // 앞의 '/' 제거 + console.log(`📁 documents 경로 처리: ${filePath} → ${actualFilePath}`); + } + + else { + // ✅ 상대 경로는 현재 디렉토리 기준 + actualFilePath = filePath; + console.log(`📂 상대 경로 사용: ${actualFilePath}`); } - + + console.log(`🔍 실제 파일 경로: ${actualFilePath}`); + console.log(`🏠 환경: ${config.isProduction ? 'PRODUCTION (NAS)' : 'DEVELOPMENT (public)'}`); + // 파일 존재 여부 확인 if (!fs.existsSync(actualFilePath)) { - throw new Error(`File not found: ${actualFilePath}`) + console.error(`❌ 파일 없음: ${actualFilePath}`); + throw new Error(`파일을 찾을 수 없습니다: ${actualFilePath}`); } - + // 파일 읽기 - const fileBuffer = fs.readFileSync(actualFilePath) - console.log(`✅ File read successfully: ${actualFilePath} (${fileBuffer.length} bytes)`) - - // Buffer를 ArrayBuffer로 변환 (타입 안전성 보장) - return new ArrayBuffer(fileBuffer.length).slice(0).constructor(fileBuffer) + const fileBuffer = fs.readFileSync(actualFilePath); + console.log(`✅ 파일 읽기 성공: ${actualFilePath} (${fileBuffer.length} bytes)`); + + // ✅ Buffer를 ArrayBuffer로 정확히 변환 + const arrayBuffer = new ArrayBuffer(fileBuffer.length); + const uint8Array = new Uint8Array(arrayBuffer); + uint8Array.set(fileBuffer); + + return arrayBuffer; } } catch (error) { - console.error(`❌ Failed to read file: ${filePath}`, error) - throw error + console.error(`❌ 파일 읽기 실패: ${filePath}`, error); + throw error; } } diff --git a/lib/vendor-document-list/import-service.ts b/lib/vendor-document-list/import-service.ts index 9a4e44db..bc384ea2 100644 --- a/lib/vendor-document-list/import-service.ts +++ b/lib/vendor-document-list/import-service.ts @@ -890,22 +890,67 @@ class ImportService { detailDoc: DOLCEDetailDocument, sourceSystem: string ): Promise<'NEW' | 'UPDATED' | 'SKIPPED'> { - // 기존 revision 조회 (registerId로) - const existingRevision = await db - .select() - .from(revisions) - .where(and( - eq(revisions.issueStageId, issueStageId), - eq(revisions.registerId, detailDoc.RegisterId) - )) - .limit(1) - + + // 🆕 여러 조건으로 기존 revision 조회 + let existingRevision = null + + // 1차: registerId로 조회 (가장 정확한 매칭) + if (detailDoc.RegisterId) { + const results = await db + .select() + .from(revisions) + .where(and( + eq(revisions.issueStageId, issueStageId), + eq(revisions.registerId, detailDoc.RegisterId) + )) + .limit(1) + + if (results.length > 0) { + existingRevision = results[0] + console.log(`Found revision by registerId: ${detailDoc.RegisterId}`) + } + } + + // 2차: externalUploadId로 조회 (업로드했던 revision 매칭) + if (!existingRevision && detailDoc.UploadId) { + const results = await db + .select() + .from(revisions) + .where(and( + eq(revisions.issueStageId, issueStageId), + eq(revisions.externalUploadId, detailDoc.UploadId) + )) + .limit(1) + + if (results.length > 0) { + existingRevision = results[0] + console.log(`Found revision by externalUploadId: ${detailDoc.UploadId}`) + } + } + + // 3차: DrawingRevNo로 조회 (같은 issueStage 내에서 revision 번호 매칭) + if (!existingRevision && detailDoc.DrawingRevNo) { + const results = await db + .select() + .from(revisions) + .where(and( + eq(revisions.issueStageId, issueStageId), + eq(revisions.revision, detailDoc.DrawingRevNo) + )) + .limit(1) + + if (results.length > 0) { + existingRevision = results[0] + console.log(`Found revision by DrawingRevNo: ${detailDoc.DrawingRevNo}`) + } + } + // Category에 따른 uploaderType 매핑 const uploaderType = this.mapCategoryToUploaderType(detailDoc.Category) - // RegisterKind에 따른 usage, usageType 매핑 (기본 로직, 추후 개선) + // RegisterKind에 따른 usage, usageType 매핑 const { usage, usageType } = this.mapRegisterKindToUsage(detailDoc.RegisterKind) - + // DOLCE 상세 데이터를 revisions 스키마에 맞게 변환 const revisionData = { issueStageId, @@ -916,27 +961,27 @@ class ImportService { usageType, revisionStatus: detailDoc.Status, externalUploadId: detailDoc.UploadId, - registerId: detailDoc.RegisterId, + registerId: detailDoc.RegisterId, // 🆕 항상 최신 registerId로 업데이트 comment: detailDoc.RegisterDesc, submittedDate: this.convertDolceDateToDate(detailDoc.CreateDt), updatedAt: new Date() } - - if (existingRevision.length > 0) { + + if (existingRevision) { // 업데이트 필요 여부 확인 - const existing = existingRevision[0] const hasChanges = - existing.revision !== revisionData.revision || - existing.revisionStatus !== revisionData.revisionStatus || - existing.uploaderName !== revisionData.uploaderName - + existingRevision.revision !== revisionData.revision || + existingRevision.revisionStatus !== revisionData.revisionStatus || + existingRevision.uploaderName !== revisionData.uploaderName || + existingRevision.registerId !== revisionData.registerId // 🆕 registerId 변경 확인 + if (hasChanges) { await db .update(revisions) .set(revisionData) - .where(eq(revisions.id, existing.id)) - - console.log(`Updated revision: ${detailDoc.RegisterId}`) + .where(eq(revisions.id, existingRevision.id)) + + console.log(`Updated revision: ${detailDoc.RegisterId} (local ID: ${existingRevision.id})`) return 'UPDATED' } else { return 'SKIPPED' @@ -949,12 +994,11 @@ class ImportService { ...revisionData, createdAt: new Date() }) - + console.log(`Created new revision: ${detailDoc.RegisterId}`) return 'NEW' } } - /** * Category를 uploaderType으로 매핑 */ @@ -972,12 +1016,75 @@ class ImportService { /** * RegisterKind를 usage/usageType으로 매핑 */ - private mapRegisterKindToUsage(registerKind: string): { usage: string; usageType: string } { - // TODO: 추후 비즈니스 로직에 맞게 구현 - // 현재는 기본 매핑만 제공 - return { - usage: registerKind || 'DEFAULT', - usageType: registerKind || 'DEFAULT' + private mapRegisterKindToUsage(registerKind: string): { usage: string; usageType: string | null } { + if (!registerKind) { + return { + usage: 'DEFAULT', + usageType: 'DEFAULT' + } + } + + switch (registerKind.toUpperCase()) { + case 'APPR': + return { + usage: 'APPROVAL', + usageType: 'Full' + } + + case 'APPR-P': + return { + usage: 'APPROVAL', + usageType: 'Partial' + } + + case 'WORK': + return { + usage: 'WORKING', + usageType: 'Full' + } + + case 'WORK-P': + return { + usage: 'WORKING', + usageType: 'Partial' + } + + case 'FMEA-1': + return { + usage: 'The 1st', + usageType: null + } + + case 'FMEA-2': + return { + usage: 'The 2nd', + usageType: null + } + + case 'RECP': + return { + usage: 'Pre', + usageType: null + } + + case 'RECW': + return { + usage: 'Working', + usageType: null + } + + case 'CMTM': + return { + usage: 'Mark-Up', + usageType: null + } + + default: + console.warn(`Unknown RegisterKind: ${registerKind}`) + return { + usage: registerKind, + usageType: 'DEFAULT' + } } } |
