summaryrefslogtreecommitdiff
path: root/lib/vendor-document-list
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-07-07 01:44:45 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-07-07 01:44:45 +0000
commit90f79a7a691943a496f67f01c1e493256070e4de (patch)
tree37275fde3ae08c2bca384fbbc8eb378de7e39230 /lib/vendor-document-list
parentfbb3b7f05737f9571b04b0a8f4f15c0928de8545 (diff)
(대표님) 변경사항 20250707 10시 43분 - unstaged 변경사항 추가
Diffstat (limited to 'lib/vendor-document-list')
-rw-r--r--lib/vendor-document-list/dolce-upload-service.ts501
-rw-r--r--lib/vendor-document-list/import-service.ts169
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'
+ }
}
}