diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-05-28 17:29:43 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-05-28 17:29:43 +0000 |
| commit | bc7d627f61a4d055b19d0679b3a4c128b7afcfda (patch) | |
| tree | 84c765b0334c39246444c0a67916c5174b6e2cc7 /app/api/revision-upload | |
| parent | 4bad21ef79fdda5f016e2012ba673d6ee6abb5fc (diff) | |
(대표님) admin / api / components
Diffstat (limited to 'app/api/revision-upload')
| -rw-r--r-- | app/api/revision-upload/route.ts | 345 |
1 files changed, 178 insertions, 167 deletions
diff --git a/app/api/revision-upload/route.ts b/app/api/revision-upload/route.ts index 2138d674..35344b4b 100644 --- a/app/api/revision-upload/route.ts +++ b/app/api/revision-upload/route.ts @@ -1,213 +1,224 @@ -// app/api/revision-upload/route.ts -import { NextRequest, NextResponse } from 'next/server' -import { writeFile } from 'fs/promises' -import { join } from 'path' -import { v4 as uuidv4 } from 'uuid' -import path from 'path' -import db from '@/db/db' -import { documents, issueStages, revisions, documentAttachments } from '@/db/schema/vendorDocu' -import { and, eq } from 'drizzle-orm' +import { NextRequest, NextResponse } from "next/server" +import { writeFile } from "fs/promises" +import { join } from "path" +import { v4 as uuidv4 } from "uuid" +import path from "path" +import { revalidateTag } from "next/cache" // ✅ 추가 + +import db from "@/db/db" +import { + documents, + issueStages, + revisions, + documentAttachments, +} from "@/db/schema/vendorDocu" +import { and, eq } from "drizzle-orm" + +/* ① change log 유틸 */ +import { + logRevisionChange, + logAttachmentChange, +} from "@/lib/vendor-document-list/sync-service" export async function POST(request: NextRequest) { try { const formData = await request.formData() - - // FormData에서 데이터 추출 - const stage = formData.get("stage") as string | null - const revision = formData.get("revision") as string | null - const docIdStr = formData.get("documentId") as string - const docId = parseInt(docIdStr, 10) - const uploaderName = formData.get("uploaderName") as string | null - const comment = formData.get("comment") as string | null - const mode = formData.get("mode") as string || "new" // 'new' | 'append' - - // 파일들 추출 - const attachmentFiles = formData.getAll("attachments") as File[] - - // 유효성 검증 - if (!docId || Number.isNaN(docId)) { - return NextResponse.json( - { error: "Invalid or missing documentId" }, - { status: 400 } - ) - } - - if (!stage || !revision) { - return NextResponse.json( - { error: "Missing stage or revision" }, - { status: 400 } - ) - } - - if (attachmentFiles.length === 0) { - return NextResponse.json( - { error: "No files provided" }, - { status: 400 } - ) - } - // 파일 크기 검증 (각 파일 최대 3GB) - const maxFileSize = 3 * 1024 * 1024 * 1024 // 3GB - for (const file of attachmentFiles) { - if (file.size > maxFileSize) { + /* ------- 파라미터 파싱 ------- */ + const stage = formData.get("stage") as string | null + const revision = formData.get("revision") as string | null + const docId = Number(formData.get("documentId")) + const uploaderName = formData.get("uploaderName") as string | null + const comment = formData.get("comment") as string | null + const mode = (formData.get("mode") || "new") as string // 'new'|'append' + const targetSystem = (formData.get("targetSystem") as string | null) ?? "DOLCE" + const attachmentFiles = formData.getAll("attachments") as File[] + + /* ------- 검증 ------- */ + if (!docId || Number.isNaN(docId)) + return NextResponse.json({ error: "Invalid documentId" }, { status: 400 }) + if (!stage || !revision) + return NextResponse.json({ error: "Missing stage or revision" }, { status: 400 }) + if (!attachmentFiles.length) + return NextResponse.json({ error: "No files provided" }, { status: 400 }) + + const MAX = 3 * 1024 * 1024 * 1024 // 3 GB + for (const f of attachmentFiles) + if (f.size > MAX) return NextResponse.json( - { error: `파일 ${file.name}이 너무 큽니다. 최대 3GB까지 허용됩니다.` }, + { error: `${f.name} > 3 GB` }, { status: 400 } ) - } - } - // 트랜잭션 실행 + /* ------- 계약 ID 확보 ------- */ + const [{ contractId }] = await db + .select({ contractId: documents.contractId }) + .from(documents) + .where(eq(documents.id, docId)) + .limit(1) + + /* ------- 트랜잭션 ------- */ const result = await db.transaction(async (tx) => { - // (1) issueStageId 찾기 또는 생성 + /* 1) Stage */ let issueStageId: number - const stageRecord = await tx - .select() + const [stageRow] = await tx + .select({ id: issueStages.id }) .from(issueStages) - .where(and(eq(issueStages.stageName, stage), eq(issueStages.documentId, docId))) + .where(and( + eq(issueStages.stageName, stage), + eq(issueStages.documentId, docId) + )) .limit(1) - - if (!stageRecord.length) { - // Stage가 없으면 새로 생성 - const [newStage] = await tx - .insert(issueStages) - .values({ - documentId: docId, - stageName: stage, - updatedAt: new Date(), - }) - .returning() - - issueStageId = newStage.id - } else { - issueStageId = stageRecord[0].id - } - - // (2) Revision 찾기 또는 생성 + + if (!stageRow) { + const [s] = await tx.insert(issueStages) + .values({ documentId: docId, stageName: stage, updatedAt: new Date() }) + .returning({ id: issueStages.id }) + issueStageId = s.id + } else issueStageId = stageRow.id + + /* 2) Revision */ + const today = new Date().toISOString().slice(0, 10) let revisionId: number - const revisionRecord = await tx + const [revRow] = await tx .select() .from(revisions) - .where(and(eq(revisions.issueStageId, issueStageId), eq(revisions.revision, revision))) + .where(and( + eq(revisions.issueStageId, issueStageId), + eq(revisions.revision, revision) + )) .limit(1) - - const currentDate = new Date().toISOString().split('T')[0] // YYYY-MM-DD 형식 - - if (!revisionRecord.length || mode === 'new') { - // 새 리비전 생성 - const [newRevision] = await tx - .insert(revisions) + + if (!revRow || mode === "new") { + /* --- CREATE --- */ + const [newRev] = await tx.insert(revisions) .values({ issueStageId, revision, - uploaderType: "vendor", // 항상 vendor로 고정 - uploaderName: uploaderName || undefined, - revisionStatus: "SUBMITTED", // 기본 상태 - submittedDate: currentDate, // 제출일 설정 - comment: comment || undefined, + uploaderType: "vendor", + uploaderName: uploaderName ?? undefined, + revisionStatus: "UPLOADED", + uploadedAt: today, + comment: comment ?? undefined, updatedAt: new Date(), }) .returning() - - revisionId = newRevision.id - console.log("✅ 새 리비전 생성:", { revisionId, revision }) + revisionId = newRev.id + + // change_logs: CREATE + await logRevisionChange( + contractId, + revisionId, + "CREATE", + newRev, + undefined, + undefined, + uploaderName ?? undefined, + [targetSystem] + ) } else { - // 기존 리비전에 파일 추가 (append 모드) - revisionId = revisionRecord[0].id - - // 기존 리비전 정보 업데이트 (코멘트나 업로더명 변경 가능) - await tx - .update(revisions) + /* --- UPDATE --- */ + await tx.update(revisions) .set({ - uploaderName: uploaderName || revisionRecord[0].uploaderName, - comment: comment || revisionRecord[0].comment, + uploaderName: uploaderName ?? revRow.uploaderName, + comment: comment ?? revRow.comment, updatedAt: new Date(), }) - .where(eq(revisions.id, revisionId)) - - console.log("✅ 기존 리비전에 파일 추가:", { revisionId, revision }) + .where(eq(revisions.id, revRow.id)) + + const [updated] = await tx + .select() + .from(revisions) + .where(eq(revisions.id, revRow.id)) + + revisionId = revRow.id + + await logRevisionChange( + contractId, + revisionId, + "UPDATE", + updated, + revRow, + undefined, + uploaderName ?? undefined, + [targetSystem] + ) } - - // (3) 파일들 저장 및 DB 기록 - const uploadedFiles = [] + + /* 3) Attachments */ + const uploadedFiles: any[] = [] const baseDir = join(process.cwd(), "public", "documents") - + for (const file of attachmentFiles) { - if (file.size > 0) { - const originalName = file.name - const ext = path.extname(originalName) - const uniqueName = uuidv4() + ext - const savePath = join(baseDir, uniqueName) - - // 파일 저장 - const arrayBuffer = await file.arrayBuffer() - const buffer = Buffer.from(arrayBuffer) - await writeFile(savePath, buffer) - - // DB에 첨부파일 정보 저장 - const [attachmentRecord] = await tx - .insert(documentAttachments) - .values({ - revisionId, - fileName: originalName, - filePath: "/documents/" + uniqueName, - fileSize: file.size, - fileType: ext.replace('.', '').toLowerCase() || undefined, - updatedAt: new Date(), - }) - .returning() - - uploadedFiles.push({ - id: attachmentRecord.id, - fileName: originalName, + const ext = path.extname(file.name) + const fname = uuidv4() + ext + const dest = join(baseDir, fname) + + await writeFile(dest, Buffer.from(await file.arrayBuffer())) + + const [att] = await tx.insert(documentAttachments) + .values({ + revisionId, + fileName: file.name, + filePath: "/documents/" + fname, fileSize: file.size, - filePath: attachmentRecord.filePath, + fileType: ext.slice(1).toLowerCase() || undefined, + updatedAt: new Date(), }) - - console.log("✅ 파일 저장 완료:", originalName) - } + .returning() + + uploadedFiles.push({ + id: att.id, + fileName: file.name, + fileSize: file.size, + filePath: att.filePath, + }) + + // change_logs: attachment CREATE + await logAttachmentChange( + contractId, + att.id, + "CREATE", + att, + undefined, + undefined, + uploaderName ?? undefined, + [targetSystem] + ) } - - // (4) Documents 테이블의 updatedAt 갱신 - await tx - .update(documents) + + /* 4) documents.updatedAt */ + await tx.update(documents) .set({ updatedAt: new Date() }) .where(eq(documents.id, docId)) - - return { - revisionId, - stage, - revision, - uploaderName, - comment, - uploadedFiles, - mode, - } - }) - console.log("✅ 리비전 업로드 완료:", { - documentId: docId, - stage, - revision, - filesCount: result.uploadedFiles.length, - mode + return { revisionId, stage, revision, uploadedFiles, mode, contractId } }) + // ✅ 캐시 무효화 - 트랜잭션 완료 후에 실행 + try { + // enhanced documents 캐시 무효화 + revalidateTag(`enhanced-documents-${result.contractId}`) + + // sync status 관련 캐시도 무효화 (필요시) + revalidateTag(`sync-status-${result.contractId}`) + + console.log(`✅ Cache invalidated for contract ${result.contractId}`) + } catch (cacheError) { + console.warn('⚠️ Cache invalidation failed:', cacheError) + // 캐시 무효화 실패해도 업로드는 성공으로 처리 + } + return NextResponse.json({ success: true, - message: `${result.uploadedFiles.length}개 파일이 성공적으로 업로드되었습니다.`, + message: `${result.uploadedFiles.length}개 파일 업로드 완료`, data: result, }) - - } catch (error) { - console.error('❌ 리비전 업로드 오류:', error) - + } catch (e) { + console.error("revision-upload error:", e) return NextResponse.json( - { - error: 'Failed to upload revision', - details: error instanceof Error ? error.message : String(error), - }, - { status: 500 } + { error: "Failed to upload revision", details: String(e) }, + { status: 500 }, ) } }
\ No newline at end of file |
