summaryrefslogtreecommitdiff
path: root/app/api/revision-upload
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-05-28 17:29:43 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-05-28 17:29:43 +0000
commitbc7d627f61a4d055b19d0679b3a4c128b7afcfda (patch)
tree84c765b0334c39246444c0a67916c5174b6e2cc7 /app/api/revision-upload
parent4bad21ef79fdda5f016e2012ba673d6ee6abb5fc (diff)
(대표님) admin / api / components
Diffstat (limited to 'app/api/revision-upload')
-rw-r--r--app/api/revision-upload/route.ts345
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