summaryrefslogtreecommitdiff
path: root/lib/dolce/actions.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dolce/actions.ts')
-rw-r--r--lib/dolce/actions.ts384
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를 사용하여 대용량 파일 처리
*/