summaryrefslogtreecommitdiff
path: root/app/api/swp/upload
diff options
context:
space:
mode:
Diffstat (limited to 'app/api/swp/upload')
-rw-r--r--app/api/swp/upload/route.ts138
1 files changed, 72 insertions, 66 deletions
diff --git a/app/api/swp/upload/route.ts b/app/api/swp/upload/route.ts
index b38c4ff4..3e15e0a3 100644
--- a/app/api/swp/upload/route.ts
+++ b/app/api/swp/upload/route.ts
@@ -1,11 +1,7 @@
import { NextRequest, NextResponse } from "next/server";
import * as fs from "fs/promises";
import * as path from "path";
-import { eq, and } from "drizzle-orm";
-import db from "@/db/db";
-import { swpDocuments } from "@/db/schema/SWP/swp-documents";
import { fetchGetVDRDocumentList, fetchGetExternalInboxList } from "@/lib/swp/api-client";
-import { syncSwpProject } from "@/lib/swp/sync-service";
import { debugLog, debugError, debugSuccess } from "@/lib/debug-utils";
// API Route 설정
@@ -26,21 +22,27 @@ interface InBoxFileInfo {
}
/**
- * 파일명 파싱: [DOC_NO]_[REV_NO]_[STAGE]_[자유-파일명].[확장자]
- * 자유 파일명에는 언더스코어가 포함될 수 있음
+ * 파일명 파싱: [DOC_NO]_[REV_NO]_[STAGE].[확장자] 또는 [DOC_NO]_[REV_NO]_[STAGE]_[자유-파일명].[확장자]
+ * 자유 파일명은 선택사항이며, 포함될 경우 언더스코어를 포함할 수 있음
*/
function parseFileName(fileName: string) {
const lastDotIndex = fileName.lastIndexOf(".");
- const extension = lastDotIndex !== -1 ? fileName.substring(lastDotIndex + 1) : "";
- const nameWithoutExt = lastDotIndex !== -1 ? fileName.substring(0, lastDotIndex) : fileName;
+
+ // 확장자 검증
+ if (lastDotIndex === -1) {
+ throw new Error(`파일 확장자가 없습니다: ${fileName}`);
+ }
+
+ const extension = fileName.substring(lastDotIndex + 1);
+ const nameWithoutExt = fileName.substring(0, lastDotIndex);
const parts = nameWithoutExt.split("_");
- // 최소 4개 파트 필요: docNo, revNo, stage, fileName
- if (parts.length < 4) {
+ // 최소 3개 파트 필요: docNo, revNo, stage (fileName은 선택사항)
+ if (parts.length < 3) {
throw new Error(
`잘못된 파일명 형식입니다: ${fileName}. ` +
- `형식: [DOC_NO]_[REV_NO]_[STAGE]_[파일명].확장자 (언더스코어 최소 3개 필요)`
+ `형식: [DOC_NO]_[REV_NO]_[STAGE].[확장자] (언더스코어 최소 2개 필요)`
);
}
@@ -49,10 +51,29 @@ function parseFileName(fileName: string) {
const revNo = parts[1];
const stage = parts[2];
- // 나머지는 자유 파일명 (언더스코어 포함 가능)
- const customFileName = parts.slice(3).join("_");
+ // 나머지는 자유 파일명 (선택사항, 언더스코어 포함 가능)
+ const customFileName = parts.length > 3 ? parts.slice(3).join("_") : "";
+
+ // 필수 항목이 비어있지 않은지 확인
+ if (!ownDocNo || ownDocNo.trim() === "") {
+ throw new Error(`문서번호(DOC_NO)가 비어있습니다: ${fileName}`);
+ }
+
+ if (!revNo || revNo.trim() === "") {
+ throw new Error(`리비전 번호(REV_NO)가 비어있습니다: ${fileName}`);
+ }
+
+ if (!stage || stage.trim() === "") {
+ throw new Error(`스테이지(STAGE)가 비어있습니다: ${fileName}`);
+ }
- return { ownDocNo, revNo, stage, fileName: customFileName, extension };
+ return {
+ ownDocNo: ownDocNo.trim(),
+ revNo: revNo.trim(),
+ stage: stage.trim(),
+ fileName: customFileName.trim(),
+ extension
+ };
}
/**
@@ -71,22 +92,43 @@ function generateTimestamp(): string {
}
/**
- * CPY_CD 조회
+ * CPY_CD 조회 (API 기반)
+ * GetVDRDocumentList API를 호출하여 해당 프로젝트/벤더의 CPY_CD를 조회
*/
async function getCpyCdForVendor(projNo: string, vndrCd: string): Promise<string> {
- const result = await db
- .select({ CPY_CD: swpDocuments.CPY_CD })
- .from(swpDocuments)
- .where(and(eq(swpDocuments.PROJ_NO, projNo), eq(swpDocuments.VNDR_CD, vndrCd)))
- .limit(1);
+ try {
+ console.log(`[getCpyCdForVendor] API 조회 시작: projNo=${projNo}, vndrCd=${vndrCd}`);
+
+ // GetVDRDocumentList API 호출 (벤더 필터 적용)
+ const documents = await fetchGetVDRDocumentList({
+ proj_no: projNo,
+ doc_gb: "V",
+ vndrCd: vndrCd,
+ });
- if (!result || result.length === 0 || !result[0].CPY_CD) {
- throw new Error(
- `프로젝트 ${projNo}에서 벤더 코드 ${vndrCd}에 해당하는 회사 코드(CPY_CD)를 찾을 수 없습니다.`
- );
- }
+ console.log(`[getCpyCdForVendor] API 조회 완료: ${documents.length}개 문서`);
- return result[0].CPY_CD;
+ if (!documents || documents.length === 0) {
+ throw new Error(
+ `프로젝트 ${projNo}에서 벤더 코드 ${vndrCd}에 할당된 문서가 없습니다.`
+ );
+ }
+
+ // 첫 번째 문서에서 CPY_CD 추출
+ const cpyCd = documents[0].CPY_CD;
+
+ if (!cpyCd) {
+ throw new Error(
+ `프로젝트 ${projNo}에서 벤더 코드 ${vndrCd}에 해당하는 회사 코드(CPY_CD)를 찾을 수 없습니다.`
+ );
+ }
+
+ console.log(`[getCpyCdForVendor] CPY_CD 확인: ${cpyCd}`);
+ return cpyCd;
+ } catch (error) {
+ console.error("[getCpyCdForVendor] 오류:", error);
+ throw error;
+ }
}
/**
@@ -308,39 +350,8 @@ export async function POST(request: NextRequest) {
await callSaveInBoxList(inBoxFileInfos);
}
- // 업로드 성공 후 동기화 처리 (현재 벤더의 변경사항만)
- if (result.successCount > 0) {
- try {
- console.log(`[upload] 동기화 시작: projNo=${projNo}, vndrCd=${vndrCd}`);
-
- // GetVDRDocumentList 및 GetExternalInboxList API 호출 (벤더 필터 적용)
- const [documents, files] = await Promise.all([
- fetchGetVDRDocumentList({
- proj_no: projNo,
- doc_gb: "V",
- vndrCd: vndrCd, // 현재 벤더만 필터링
- }),
- fetchGetExternalInboxList({
- projNo: projNo,
- vndrCd: vndrCd, // 현재 벤더만 필터링
- }),
- ]);
-
- console.log(`[upload] API 조회 완료: 문서 ${documents.length}개, 파일 ${files.length}개`);
-
- // 동기화 실행
- const syncResult = await syncSwpProject(projNo, documents, files);
-
- if (syncResult.success) {
- console.log(`[upload] 동기화 완료:`, syncResult.stats);
- } else {
- console.warn(`[upload] 동기화 경고:`, syncResult.errors);
- }
- } catch (syncError) {
- // 동기화 실패는 경고로만 처리 (업로드 자체는 성공)
- console.error("[upload] 동기화 실패 (업로드는 성공):", syncError);
- }
- }
+ // ⚠️ Full API 방식으로 전환했으므로 로컬 DB 동기화는 불필요
+ // 업로드 성공 시 SaveInBoxList API 호출만으로 충분 (이미 위에서 완료)
// 결과 메시지 생성
let message: string;
@@ -348,7 +359,7 @@ export async function POST(request: NextRequest) {
if (result.failedCount === 0) {
success = true;
- message = `${result.successCount}개 파일이 성공적으로 업로드 및 동기화되었습니다.`;
+ message = `${result.successCount}개 파일이 성공적으로 업로드되었습니다.`;
} else if (result.successCount === 0) {
success = false;
message = `모든 파일 업로드에 실패했습니다. (${result.failedCount}개)`;
@@ -359,18 +370,13 @@ export async function POST(request: NextRequest) {
console.log(`[upload] 완료:`, { success, message, result });
- // 동기화 완료 정보 추가
- const syncCompleted = result.successCount > 0;
- const syncTimestamp = new Date().toISOString();
-
return NextResponse.json({
success,
message,
successCount: result.successCount,
failedCount: result.failedCount,
details: result.details,
- syncCompleted,
- syncTimestamp,
+ uploadTimestamp: new Date().toISOString(),
affectedVndrCd: vndrCd,
});
} catch (error) {