diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-05-15 01:19:49 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-05-15 01:19:49 +0000 |
| commit | 9eb8e80f4f736c4edffa650c685d1f170ca51aa1 (patch) | |
| tree | cae02173015c806cd5ea92be86938fe3bf14decd /app/api | |
| parent | 71f4dd76b57e77676d8886ac0a8b0bd0a7f24e62 (diff) | |
(대표님) 구매 요청사항 반영한 통합 rfq / 필터 개인화 / po-rfq
Diffstat (limited to 'app/api')
| -rw-r--r-- | app/api/procurement-rfqs/[rfqId]/vendors/[vendorId]/comments/route.ts | 145 | ||||
| -rw-r--r-- | app/api/table-presets/[id]/route.ts | 57 | ||||
| -rw-r--r-- | app/api/table-presets/route.ts | 98 |
3 files changed, 300 insertions, 0 deletions
diff --git a/app/api/procurement-rfqs/[rfqId]/vendors/[vendorId]/comments/route.ts b/app/api/procurement-rfqs/[rfqId]/vendors/[vendorId]/comments/route.ts new file mode 100644 index 00000000..51430118 --- /dev/null +++ b/app/api/procurement-rfqs/[rfqId]/vendors/[vendorId]/comments/route.ts @@ -0,0 +1,145 @@ +// app/api/procurement-rfqs/[rfqId]/vendors/[vendorId]/comments/route.ts +import { NextRequest, NextResponse } from "next/server" + +import db from '@/db/db'; +import { getServerSession } from "next-auth/next" +import { authOptions } from "@/app/api/auth/[...nextauth]/route" + +import { procurementRfqComments, procurementRfqAttachments } from "@/db/schema" +import { revalidateTag } from "next/cache" + +// 파일 저장을 위한 유틸리티 +import { writeFile, mkdir } from 'fs/promises' +import { join } from 'path' +import crypto from 'crypto' + +/** + * 코멘트 생성 API 엔드포인트 + */ +export async function POST( + request: NextRequest, + { params }: { params: { rfqId: string; vendorId: string } } +) { + try { + // 인증 확인 + const session = await getServerSession(authOptions); + if (!session?.user) { + return NextResponse.json( + { success: false, message: "인증이 필요합니다" }, + { status: 401 } + ) + } + + const rfqId = parseInt(params.rfqId) + const vendorId = parseInt(params.vendorId) + + // 유효성 검사 + if (isNaN(rfqId) || isNaN(vendorId)) { + return NextResponse.json( + { success: false, message: "유효하지 않은 매개변수입니다" }, + { status: 400 } + ) + } + + // FormData 파싱 + const formData = await request.formData() + const content = formData.get("content") as string + const isVendorComment = formData.get("isVendorComment") === "true" + const files = formData.getAll("attachments") as File[] + + if (!content && files.length === 0) { + return NextResponse.json( + { success: false, message: "내용이나 첨부파일이 필요합니다" }, + { status: 400 } + ) + } + + // 코멘트 생성 + const [comment] = await db + .insert(procurementRfqComments) + .values({ + rfqId, + vendorId, + userId: parseInt(session.user.id), + content, + isVendorComment, + isRead: !isVendorComment, // 본인 메시지는 읽음 처리 + createdAt: new Date(), + updatedAt: new Date(), + }) + .returning() + + // 첨부파일 처리 + const attachments = [] + if (files.length > 0) { + // 디렉토리 생성 + const uploadDir = join(process.cwd(), "public", `rfq-${rfqId}`, `vendor-${vendorId}`, `comment-${comment.id}`) + await mkdir(uploadDir, { recursive: true }) + + // 각 파일 저장 + for (const file of files) { + const buffer = Buffer.from(await file.arrayBuffer()) + const filename = `${Date.now()}-${crypto.randomBytes(8).toString("hex")}-${file.name.replace(/[^a-zA-Z0-9.-]/g, "_")}` + const filePath = join(uploadDir, filename) + + // 파일 쓰기 + await writeFile(filePath, buffer) + + // DB에 첨부파일 정보 저장 + const [attachment] = await db + .insert(procurementRfqAttachments) + .values({ + rfqId, + commentId: comment.id, + fileName: file.name, + fileSize: file.size, + fileType: file.type, + filePath: `/rfq-${rfqId}/vendor-${vendorId}/comment-${comment.id}/${filename}`, + isVendorUpload: isVendorComment, + uploadedBy: parseInt(session.user.id), + vendorId, + uploadedAt: new Date(), + }) + .returning() + + attachments.push({ + id: attachment.id, + fileName: attachment.fileName, + fileSize: attachment.fileSize, + fileType: attachment.fileType, + filePath: attachment.filePath, + uploadedAt: attachment.uploadedAt + }) + } + } + + // 캐시 무효화 + revalidateTag(`rfq-${rfqId}-comments`) + + // 응답 데이터 구성 + const responseData = { + id: comment.id, + rfqId: comment.rfqId, + vendorId: comment.vendorId, + userId: comment.userId, + content: comment.content, + isVendorComment: comment.isVendorComment, + createdAt: comment.createdAt, + updatedAt: comment.updatedAt, + userName: session.user.name, + attachments, + isRead: comment.isRead + } + + return NextResponse.json({ + success: true, + data: { comment: responseData } + }) + } catch (error) { + console.error("코멘트 생성 오류:", error) + return NextResponse.json( + { success: false, message: "코멘트 생성 중 오류가 발생했습니다" }, + { status: 500 } + ) + } +}
\ No newline at end of file diff --git a/app/api/table-presets/[id]/route.ts b/app/api/table-presets/[id]/route.ts new file mode 100644 index 00000000..8146889d --- /dev/null +++ b/app/api/table-presets/[id]/route.ts @@ -0,0 +1,57 @@ +// app/api/table-presets/[id]/route.ts +import { NextRequest, NextResponse } from "next/server" +import { getServerSession } from "next-auth" +import { authOptions } from "@/app/api/auth/[...nextauth]/route" +import db from "@/db/db" +import { tablePresets } from "@/db/schema/setting" +import { eq } from "drizzle-orm" + +export async function PUT( + request: NextRequest, + { params }: { params: { id: string } } +) { + try { + const session = await getServerSession(authOptions) + if (!session?.user?.id) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }) + } + + const presetId = params.id + const body = await request.json() + + const updatedPreset = await db + .update(tablePresets) + .set({ + ...body, + updatedAt: new Date(), + }) + .where(eq(tablePresets.id, presetId)) + .returning() + + return NextResponse.json(updatedPreset[0]) + } catch (error) { + console.error("Error updating preset:", error) + return NextResponse.json({ error: "Internal Server Error" }, { status: 500 }) + } +} + +export async function DELETE( + request: NextRequest, + { params }: { params: { id: string } } +) { + try { + const session = await getServerSession(authOptions) + if (!session?.user?.id) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }) + } + + const presetId = params.id + + await db.delete(tablePresets).where(eq(tablePresets.id, presetId)) + + return NextResponse.json({ success: true }) + } catch (error) { + console.error("Error deleting preset:", error) + return NextResponse.json({ error: "Internal Server Error" }, { status: 500 }) + } +}
\ No newline at end of file diff --git a/app/api/table-presets/route.ts b/app/api/table-presets/route.ts new file mode 100644 index 00000000..f276b469 --- /dev/null +++ b/app/api/table-presets/route.ts @@ -0,0 +1,98 @@ +// app/api/table-presets/route.ts +import { NextRequest, NextResponse } from "next/server" +import { getServerSession } from "next-auth" +import { authOptions } from "@/app/api/auth/[...nextauth]/route" +import db from "@/db/db" +import { eq, and, or } from "drizzle-orm" +import { tablePresets } from "@/db/schema/setting" + +export async function GET(request: NextRequest) { + try { + const session = await getServerSession(authOptions) + if (!session?.user?.id) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }) + } + + const { searchParams } = new URL(request.url) + const tableId = searchParams.get("tableId") + + if (!tableId) { + return NextResponse.json({ error: "tableId is required" }, { status: 400 }) + } + + // 사용자의 프리셋 + 공유된 프리셋 조회 + const presets = await db + .select() + .from(tablePresets) + .where( + and( + eq(tablePresets.tableId, tableId), + or( + eq(tablePresets.userId, session.user.id), + eq(tablePresets.isShared, true) + ) + ) + ) + .orderBy(tablePresets.createdAt) + + return NextResponse.json(presets) + } catch (error) { + console.error("Error fetching presets:", error) + return NextResponse.json({ error: "Internal Server Error" }, { status: 500 }) + } +} + +export async function POST(request: NextRequest) { + try { + const session = await getServerSession(authOptions) + if (!session?.user?.id) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }) + } + + const body = await request.json() + const { tableId, name, settings, isDefault } = body + + // 기본 프리셋으로 설정하는 경우 기존 기본 프리셋 해제 + if (isDefault) { + await db + .update(tablePresets) + .set({ isDefault: false }) + .where( + and( + eq(tablePresets.userId, session.user.id), + eq(tablePresets.tableId, tableId) + ) + ) + } + + const newPreset = await db + .insert(tablePresets) + .values({ + userId: session.user.id, + tableId, + name, + settings, + isDefault: Boolean(isDefault), + isActive: true, + createdBy: session.user.id, + }) + .returning() + + // 다른 프리셋들의 active 상태 해제 + await db + .update(tablePresets) + .set({ isActive: false }) + .where( + and( + eq(tablePresets.userId, session.user.id), + eq(tablePresets.tableId, tableId), + eq(tablePresets.id, newPreset[0].id) + ) + ) + + return NextResponse.json(newPreset[0]) + } catch (error) { + console.error("Error creating preset:", error) + return NextResponse.json({ error: "Internal Server Error" }, { status: 500 }) + } +}
\ No newline at end of file |
