diff options
| author | joonhoekim <26rote@gmail.com> | 2025-11-24 22:41:52 +0900 |
|---|---|---|
| committer | joonhoekim <26rote@gmail.com> | 2025-11-24 22:41:52 +0900 |
| commit | 25b2561bf17128b96f023c977efb5cb51da0b4aa (patch) | |
| tree | a0154c20a12d2dee8d5acddec5a66e56b7f07e0b /lib/dolce/actions.ts | |
| parent | 7010b6a8c4d05cfb670aec6048f225db21c8c092 (diff) | |
(김준회) dolce: 기존 레이아웃과 유사한 v2 추가, bulk-upload를 MatchBatchDwgFile 사용하지 않도록 변경 (해당 API 동작 이상함)
Diffstat (limited to 'lib/dolce/actions.ts')
| -rw-r--r-- | lib/dolce/actions.ts | 384 |
1 files changed, 384 insertions, 0 deletions
diff --git a/lib/dolce/actions.ts b/lib/dolce/actions.ts index 77de430f..552a9a6a 100644 --- a/lib/dolce/actions.ts +++ b/lib/dolce/actions.ts @@ -684,6 +684,390 @@ export async function uploadFilesToDetailDrawing( } /** + * B4 매핑 정보 일괄 저장 (MatchBatchFileDwgEdit) + */ +export interface B4MappingSaveItem { + CGbn: string | null; + Category: string | null; + CheckBox: string; + DGbn: string | null; + DegreeGbn: string | null; + DeptGbn: string | null; + Discipline: string | null; + DrawingKind: string; + DrawingMoveGbn: string | null; + DrawingName: string | null; + DrawingNo: string; + DrawingUsage: string | null; + FileNm: string; + JGbn: string | null; + Manager: string | null; + MappingYN: "Y" | "N"; + NewOrNot: string | null; + ProjectNo: string; + RegisterGroup: number; + RegisterGroupId: number; + RegisterKindCode: string | null; + RegisterSerialNo: number; + RevNo: string | null; + SGbn: string | null; + UploadId: string; +} + +export async function saveB4MappingBatch( + mappingSaveLists: B4MappingSaveItem[], + userId: string +): Promise<number> { + try { + const response = await dolceApiCall<{ + MatchBatchFileDwgEditResult: number; + }>("MatchBatchFileDwgEdit", { + mappingSaveLists, + UserID: userId, + }); + + return response.MatchBatchFileDwgEditResult; + } catch (error) { + console.error("B4 매핑 정보 저장 실패:", error); + throw error; + } +} + +/** + * B4 파일명 파싱 (validateB4FileName과 동일한 로직) + * 형식: [버림] [문서번호토큰1] [문서번호토큰2] ... [리비전번호].[확장자] + * 예시: "testfile GTT DE 007 R01.pdf" → DrawingNo: "GTT-DE-007", RevNo: "R01" + */ +async function parseB4FileName(fileName: string): Promise<{ + valid: boolean; + drawingNo?: string; + revNo?: string; + error?: string; +}> { + try { + const lastDotIndex = fileName.lastIndexOf("."); + if (lastDotIndex === -1) { + return { valid: false, error: "파일 확장자가 없습니다" }; + } + + const nameWithoutExt = fileName.substring(0, lastDotIndex); + const parts = nameWithoutExt.split(" ").filter((p) => p.trim() !== ""); + + if (parts.length < 3) { + return { + valid: false, + error: `공백이 최소 2개 있어야 합니다 (현재: ${parts.length - 1}개)`, + }; + } + + const revNo = parts[parts.length - 1]; + const drawingTokens = parts.slice(1, parts.length - 1); + const drawingNo = drawingTokens.join("-"); + + if (!drawingNo || !revNo) { + return { valid: false, error: "도면번호 또는 리비전번호가 비어있습니다" }; + } + + return { valid: true, drawingNo: drawingNo.trim(), revNo: revNo.trim() }; + } catch (error) { + return { + valid: false, + error: error instanceof Error ? error.message : "알 수 없는 오류", + }; + } +} + +/** + * B4 파일 일괄 업로드 V2 + * + * MatchBatchFileDwg/MatchBatchFileDwgEdit API 대신 + * DetailDwgReceiptMgmtEdit API와 업로드 서비스만 사용 + * + * 프로세스: + * 1. 파일명 파싱하여 DrawingNo, RevNo 추출 + * 2. 기존 도면 정보 조회 (fetchDwgReceiptList) + * 3. 기존 상세도면 조회 (fetchDetailDwgReceiptList) + * 4. 없으면 ADD, 있으면 기존 UploadId 사용 + * 5. 파일 업로드 (/api/dolce/upload-files) + */ +export async function bulkUploadB4FilesV2( + formData: FormData +): Promise<B4BulkUploadResult> { + try { + console.log("[V2] B4 일괄 업로드 시작"); + + // FormData에서 메타데이터 추출 + const projectNo = formData.get("projectNo") as string; + const userId = formData.get("userId") as string; + const userNm = formData.get("userNm") as string; + const email = formData.get("email") as string; + const vendorCode = formData.get("vendorCode") as string; + const registerKind = formData.get("registerKind") as string; + const fileCount = parseInt(formData.get("fileCount") as string); + + if (!projectNo || !userId || !userNm || !email || !vendorCode || !registerKind || !fileCount) { + throw new Error("필수 파라미터가 누락되었습니다"); + } + + console.log(`[V2] 프로젝트: ${projectNo}, 사용자: ${userId}, 파일 수: ${fileCount}`); + + const results: Array<{ + drawingNo: string; + revNo: string; + fileName: string; + success: boolean; + error?: string; + }> = []; + + let successCount = 0; + let failCount = 0; + + // 1단계: 파일 수집 및 파싱 + interface ParsedFile { + file: File; + drawingNo: string; + revNo: string; + fileName: string; + } + + const parsedFiles: ParsedFile[] = []; + + for (let i = 0; i < fileCount; i++) { + const file = formData.get(`file_${i}`) as File; + if (!file) continue; + + const parseResult = await parseB4FileName(file.name); + if (!parseResult.valid || !parseResult.drawingNo || !parseResult.revNo) { + results.push({ + drawingNo: "", + revNo: "", + fileName: file.name, + success: false, + error: parseResult.error || "파일명 파싱 실패", + }); + failCount++; + continue; + } + + parsedFiles.push({ + file, + drawingNo: parseResult.drawingNo, + revNo: parseResult.revNo, + fileName: file.name, + }); + } + + console.log(`[V2] 파싱 완료: ${parsedFiles.length}개 성공, ${failCount}개 실패`); + + // 2단계: DrawingNo별로 기본 도면 정보 조회 + const drawingNoSet = new Set(parsedFiles.map((f) => f.drawingNo)); + const drawingInfoMap = new Map<string, GttDwgReceiptItem>(); + + for (const drawingNo of drawingNoSet) { + try { + const dwgList = await fetchDwgReceiptList({ + project: projectNo, + drawingKind: "B4", + drawingMoveGbn: "도면입수", + drawingNo: drawingNo, + }); + + const dwgInfo = dwgList.find( + (d) => (d as GttDwgReceiptItem).DrawingNo === drawingNo + ) as GttDwgReceiptItem | undefined; + + if (dwgInfo) { + drawingInfoMap.set(drawingNo, dwgInfo); + console.log(`[V2] 도면 정보 조회 완료: ${drawingNo}`); + } else { + console.warn(`[V2] 도면 정보 없음: ${drawingNo}`); + } + } catch (error) { + console.error(`[V2] 도면 정보 조회 실패: ${drawingNo}`, error); + } + } + + // 3단계: DrawingNo + RevNo로 그룹화 + const uploadGroups = new Map< + string, + { + drawingNo: string; + revNo: string; + files: File[]; + drawingInfo?: GttDwgReceiptItem; + } + >(); + + for (const parsed of parsedFiles) { + const groupKey = `${parsed.drawingNo}_${parsed.revNo}`; + if (!uploadGroups.has(groupKey)) { + uploadGroups.set(groupKey, { + drawingNo: parsed.drawingNo, + revNo: parsed.revNo, + files: [], + drawingInfo: drawingInfoMap.get(parsed.drawingNo), + }); + } + uploadGroups.get(groupKey)!.files.push(parsed.file); + } + + console.log(`[V2] ${uploadGroups.size}개 그룹으로 묶임`); + + // 4단계: 각 그룹별로 처리 + for (const [groupKey, group] of uploadGroups.entries()) { + const { drawingNo, revNo, files, drawingInfo } = group; + + try { + console.log(`[V2] 그룹 처리 시작: ${groupKey} (${files.length}개 파일)`); + + // 도면 정보가 없으면 실패 + if (!drawingInfo) { + throw new Error(`도면 정보를 찾을 수 없습니다: ${drawingNo}`); + } + + // 4-1. 기존 상세도면 조회 + const detailDwgList = await fetchDetailDwgReceiptList({ + project: projectNo, + drawingNo: drawingNo, + discipline: drawingInfo.Discipline, + drawingKind: "B4", + userId: userId, + }); + + console.log(`[V2] 기존 상세도면: ${detailDwgList.length}개`); + + // 4-2. 해당 RevNo의 상세도면 찾기 + const existingDetail = detailDwgList.find( + (d) => d.DrawingRevNo === revNo + ); + + let uploadId: string; + + if (existingDetail) { + // 기존 상세도면이 있으면 해당 UploadId 사용 + uploadId = existingDetail.UploadId; + console.log(`[V2] 기존 상세도면 사용: ${revNo}, UploadId: ${uploadId}`); + } else { + // 4-3. 없으면 새로 생성 (ADD) + uploadId = crypto.randomUUID(); + + // 기존 상세도면이 있으면 거기서 Category 가져오기, 없으면 기본값 + const category = detailDwgList.length > 0 ? detailDwgList[0].Category : "NORM"; + const registerDesc = ""; + + console.log(`[V2] 새 상세도면 생성: ${revNo}, UploadId: ${uploadId}`); + + const addRequest: DetailDwgEditRequest = { + Mode: "ADD", + Status: "01", + RegisterId: 0, + ProjectNo: projectNo, + Discipline: drawingInfo.Discipline, + DrawingKind: "B4", + DrawingNo: drawingNo, + DrawingName: drawingInfo.DrawingName, + RegisterGroupId: drawingInfo.RegisterGroupId, + RegisterSerialNo: drawingInfo.RegisterGroup, + RegisterKind: registerKind, + DrawingRevNo: revNo, + Category: category, + Receiver: null, + Manager: drawingInfo.Manager || "970043", + RegisterDesc: registerDesc, + UploadId: uploadId, + RegCompanyCode: vendorCode, + }; + + await editDetailDwgReceipt({ + dwgList: [addRequest], + userId: userId, + userNm: userNm, + vendorCode: vendorCode, + email: email, + }); + + console.log(`[V2] 상세도면 ADD 완료: ${groupKey}`); + } + + // 4-4. 파일 업로드 + console.log(`[V2] 파일 업로드 시작: ${files.length}개 파일`); + + const uploadFormData = new FormData(); + uploadFormData.append("uploadId", uploadId); + uploadFormData.append("userId", userId); + uploadFormData.append("fileCount", String(files.length)); + + files.forEach((file, index) => { + uploadFormData.append(`file_${index}`, file); + }); + + // Next.js API Route 호출 (프록시) + const uploadResponse = await fetch("/api/dolce/upload-files", { + method: "POST", + body: uploadFormData, + }); + + if (!uploadResponse.ok) { + const errorData = await uploadResponse.json(); + throw new Error(errorData.error || `파일 업로드 실패: ${uploadResponse.status}`); + } + + const uploadResult = await uploadResponse.json(); + + if (!uploadResult.success) { + throw new Error(uploadResult.error || "파일 업로드 실패"); + } + + console.log(`[V2] 파일 업로드 완료: ${groupKey}`); + + // 성공 처리 + for (const file of files) { + results.push({ + drawingNo, + revNo, + fileName: file.name, + success: true, + }); + successCount++; + } + } catch (error) { + // 실패 처리 + const errorMessage = + error instanceof Error ? error.message : "알 수 없는 오류"; + + console.error(`[V2] 그룹 처리 실패: ${groupKey}`, error); + + for (const file of group.files) { + results.push({ + drawingNo: group.drawingNo, + revNo: group.revNo, + fileName: file.name, + success: false, + error: errorMessage, + }); + failCount++; + } + } + } + + console.log(`[V2] 일괄 업로드 완료: 성공 ${successCount}, 실패 ${failCount}`); + + return { + success: successCount > 0, + successCount, + failCount, + results, + }; + } catch (error) { + console.error("[V2] 일괄 업로드 실패:", error); + return { + success: false, + error: error instanceof Error ? error.message : "알 수 없는 오류", + }; + } +} + +/** * B4 파일 일괄 업로드 * 주의: formData를 사용하여 대용량 파일 처리 */ |
