summaryrefslogtreecommitdiff
path: root/lib/forms/services.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/forms/services.ts')
-rw-r--r--lib/forms/services.ts466
1 files changed, 314 insertions, 152 deletions
diff --git a/lib/forms/services.ts b/lib/forms/services.ts
index e5fc8666..ff21626c 100644
--- a/lib/forms/services.ts
+++ b/lib/forms/services.ts
@@ -1,18 +1,29 @@
// lib/forms/services.ts
-"use server"
+"use server";
+import path from "path";
+import fs from "fs/promises";
+import { v4 as uuidv4 } from "uuid";
import db from "@/db/db";
-import { formEntries, formMetas, forms, tags, tagTypeClassFormMappings } from "@/db/schema/vendorData"
-import { eq, and, desc, sql, DrizzleError, or } from "drizzle-orm"
-import { unstable_cache } from "next/cache"
-import { revalidateTag } from "next/cache"
+import {
+ formEntries,
+ formMetas,
+ forms,
+ tags,
+ tagTypeClassFormMappings,
+ vendorDataReportTemps,
+ VendorDataReportTemps,
+} from "@/db/schema/vendorData";
+import { eq, and, desc, sql, DrizzleError, or } from "drizzle-orm";
+import { unstable_cache } from "next/cache";
+import { revalidateTag } from "next/cache";
import { getErrorMessage } from "../handle-error";
import { DataTableColumnJSON } from "@/components/form-data/form-data-table-columns";
export interface FormInfo {
- id: number
- formCode: string
- formName: string
+ id: number;
+ formCode: string;
+ formName: string;
// tagType: string
}
@@ -29,7 +40,9 @@ export async function getFormsByContractItemId(contractItemId: number | null) {
try {
return unstable_cache(
async () => {
- console.log(`[Forms Service] Fetching forms for contractItemId: ${contractItemId}`);
+ console.log(
+ `[Forms Service] Fetching forms for contractItemId: ${contractItemId}`
+ );
try {
// 데이터베이스에서 폼 조회
@@ -38,38 +51,48 @@ export async function getFormsByContractItemId(contractItemId: number | null) {
id: forms.id,
formCode: forms.formCode,
formName: forms.formName,
- // tagType: forms.tagType,
+ // tagType: forms.tagType,
})
.from(forms)
.where(eq(forms.contractItemId, contractItemId));
- console.log(`[Forms Service] Found ${formRecords.length} forms for contractItemId: ${contractItemId}`);
+ console.log(
+ `[Forms Service] Found ${formRecords.length} forms for contractItemId: ${contractItemId}`
+ );
// 결과가 배열인지 확인
if (!Array.isArray(formRecords)) {
- getErrorMessage(`Unexpected result format for contractItemId ${contractItemId} ${formRecords}`);
+ getErrorMessage(
+ `Unexpected result format for contractItemId ${contractItemId} ${formRecords}`
+ );
return { forms: [] };
}
return { forms: formRecords };
} catch (error) {
- getErrorMessage(`Database error for contractItemId ${contractItemId}: ${error}`);
+ getErrorMessage(
+ `Database error for contractItemId ${contractItemId}: ${error}`
+ );
throw error; // 캐시 함수에서 에러를 던져 캐싱이 발생하지 않도록 함
}
},
[cacheKey],
{
// 캐시 시간 단축
- revalidate: 60, // 1분으로 줄임
- tags: [cacheKey]
+ revalidate: 60, // 1분으로 줄임
+ tags: [cacheKey],
}
)();
} catch (error) {
- getErrorMessage(`Cache operation failed for contractItemId ${contractItemId}: ${error}`);
+ getErrorMessage(
+ `Cache operation failed for contractItemId ${contractItemId}: ${error}`
+ );
// 캐시 문제 시 직접 쿼리 시도
try {
- console.log(`[Forms Service] Fallback: Direct query for contractItemId: ${contractItemId}`);
+ console.log(
+ `[Forms Service] Fallback: Direct query for contractItemId: ${contractItemId}`
+ );
const formRecords = await db
.select({
@@ -83,7 +106,9 @@ export async function getFormsByContractItemId(contractItemId: number | null) {
return { forms: formRecords };
} catch (dbError) {
- getErrorMessage(`Fallback query failed for contractItemId ${contractItemId}:${dbError}`);
+ getErrorMessage(
+ `Fallback query failed for contractItemId ${contractItemId}:${dbError}`
+ );
return { forms: [] };
}
}
@@ -113,7 +138,7 @@ export async function revalidateForms(contractItemId: number) {
*/
export async function getFormData(formCode: string, contractItemId: number) {
// 고유 캐시 키 (formCode + contractItemId)
- const cacheKey = `form-data-${formCode}-${contractItemId}`
+ const cacheKey = `form-data-${formCode}-${contractItemId}`;
try {
// 1) unstable_cache로 전체 로직을 감싼다
@@ -126,24 +151,29 @@ export async function getFormData(formCode: string, contractItemId: number) {
.from(formMetas)
.where(eq(formMetas.formCode, formCode))
.orderBy(desc(formMetas.updatedAt))
- .limit(1)
+ .limit(1);
- const meta = metaRows[0] ?? null
+ const meta = metaRows[0] ?? null;
if (!meta) {
- return { columns: null, data: [] }
+ return { columns: null, data: [] };
}
// (2) form_entries에서 (formCode, contractItemId)에 해당하는 "가장 최신" 한 행
const entryRows = await db
.select()
.from(formEntries)
- .where(and(eq(formEntries.formCode, formCode), eq(formEntries.contractItemId, contractItemId)))
+ .where(
+ and(
+ eq(formEntries.formCode, formCode),
+ eq(formEntries.contractItemId, contractItemId)
+ )
+ )
.orderBy(desc(formEntries.updatedAt))
- .limit(1)
+ .limit(1);
- const entry = entryRows[0] ?? null
+ const entry = entryRows[0] ?? null;
// columns: DB에 저장된 JSON (DataTableColumnJSON[])
- const columns = meta.columns as DataTableColumnJSON[]
+ const columns = meta.columns as DataTableColumnJSON[];
columns.forEach((col) => {
// 이미 displayLabel이 있으면 그대로 두고,
@@ -151,40 +181,44 @@ export async function getFormData(formCode: string, contractItemId: number) {
// 둘 다 없으면 label만 쓴다.
if (!col.displayLabel) {
if (col.uom) {
- col.displayLabel = `${col.label} (${col.uom})`
+ col.displayLabel = `${col.label} (${col.uom})`;
} else {
- col.displayLabel = col.label
+ col.displayLabel = col.label;
}
}
- })
-
+ });
+
// data: 만약 entry가 없거나, data가 아닌 형태면 빈 배열
- let data: Array<Record<string, any>> = []
+ let data: Array<Record<string, any>> = [];
if (entry) {
if (Array.isArray(entry.data)) {
- data = entry.data
+ data = entry.data;
} else {
- console.warn("formEntries data was not an array. Using empty array.")
+ console.warn(
+ "formEntries data was not an array. Using empty array."
+ );
}
}
- return { columns, data }
+ return { columns, data };
// --- 기존 로직 끝 ---
},
[cacheKey], // 캐시 키 의존성
{
- revalidate: 60, // 1분 캐시
- tags: [cacheKey], // 캐시 태그
+ revalidate: 60, // 1분 캐시
+ tags: [cacheKey], // 캐시 태그
}
- )()
+ )();
- return result
+ return result;
} catch (cacheError) {
- console.error(`[getFormData] Cache operation failed:`, cacheError)
+ console.error(`[getFormData] Cache operation failed:`, cacheError);
// --- fallback: 캐시 문제 시 직접 쿼리 시도 ---
try {
- console.log(`[getFormData] Fallback DB query for (${formCode}, ${contractItemId})`)
+ console.log(
+ `[getFormData] Fallback DB query for (${formCode}, ${contractItemId})`
+ );
// (1) form_metas
const metaRows = await db
@@ -192,24 +226,29 @@ export async function getFormData(formCode: string, contractItemId: number) {
.from(formMetas)
.where(eq(formMetas.formCode, formCode))
.orderBy(desc(formMetas.updatedAt))
- .limit(1)
+ .limit(1);
- const meta = metaRows[0] ?? null
+ const meta = metaRows[0] ?? null;
if (!meta) {
- return { columns: null, data: [] }
+ return { columns: null, data: [] };
}
// (2) form_entries
const entryRows = await db
.select()
.from(formEntries)
- .where(and(eq(formEntries.formCode, formCode), eq(formEntries.contractItemId, contractItemId)))
+ .where(
+ and(
+ eq(formEntries.formCode, formCode),
+ eq(formEntries.contractItemId, contractItemId)
+ )
+ )
.orderBy(desc(formEntries.updatedAt))
- .limit(1)
+ .limit(1);
- const entry = entryRows[0] ?? null
+ const entry = entryRows[0] ?? null;
- const columns = meta.columns as DataTableColumnJSON[]
+ const columns = meta.columns as DataTableColumnJSON[];
columns.forEach((col) => {
// 이미 displayLabel이 있으면 그대로 두고,
@@ -217,33 +256,34 @@ export async function getFormData(formCode: string, contractItemId: number) {
// 둘 다 없으면 label만 쓴다.
if (!col.displayLabel) {
if (col.uom) {
- col.displayLabel = `${col.label} (${col.uom})`
+ col.displayLabel = `${col.label} (${col.uom})`;
} else {
- col.displayLabel = col.label
+ col.displayLabel = col.label;
}
}
- })
+ });
- let data: Array<Record<string, any>> = []
+ let data: Array<Record<string, any>> = [];
if (entry) {
if (Array.isArray(entry.data)) {
- data = entry.data
+ data = entry.data;
} else {
- console.warn("formEntries data was not an array. Using empty array (fallback).")
+ console.warn(
+ "formEntries data was not an array. Using empty array (fallback)."
+ );
}
}
- return { columns, data }
+ return { columns, data };
} catch (dbError) {
- console.error(`[getFormData] Fallback DB query failed:`, dbError)
- return { columns: null, data: [] }
+ console.error(`[getFormData] Fallback DB query failed:`, dbError);
+ return { columns: null, data: [] };
}
}
}
// export async function syncMissingTags(contractItemId: number, formCode: string) {
-
// // (1) forms 테이블에서 (contractItemId, formCode) 찾기
// const [formRow] = await db
// .select()
@@ -320,50 +360,55 @@ export async function getFormData(formCode: string, contractItemId: number) {
// .where(eq(formEntries.id, entry.id))
// }
-
// revalidateTag(`form-data-${formCode}-${contractItemId}`);
// return { createdCount }
// }
-export async function syncMissingTags(contractItemId: number, formCode: string) {
+export async function syncMissingTags(
+ contractItemId: number,
+ formCode: string
+) {
// (1) Ensure there's a row in `forms` matching (contractItemId, formCode).
const [formRow] = await db
.select()
.from(forms)
.where(
- and(eq(forms.contractItemId, contractItemId), eq(forms.formCode, formCode))
+ and(
+ eq(forms.contractItemId, contractItemId),
+ eq(forms.formCode, formCode)
+ )
)
- .limit(1)
+ .limit(1);
if (!formRow) {
throw new Error(
`Form not found for contractItemId=${contractItemId}, formCode=${formCode}`
- )
+ );
}
// (2) Get all mappings from `tagTypeClassFormMappings` for this formCode.
const formMappings = await db
.select()
.from(tagTypeClassFormMappings)
- .where(eq(tagTypeClassFormMappings.formCode, formCode))
+ .where(eq(tagTypeClassFormMappings.formCode, formCode));
// If no mappings are found, there's nothing to sync.
if (formMappings.length === 0) {
- console.log(`No mappings found for formCode=${formCode}`)
- return { createdCount: 0, updatedCount: 0, deletedCount: 0 }
+ console.log(`No mappings found for formCode=${formCode}`);
+ return { createdCount: 0, updatedCount: 0, deletedCount: 0 };
}
// Build a dynamic OR clause to match (tagType, class) pairs from the mappings.
const orConditions = formMappings.map((m) =>
and(eq(tags.tagType, m.tagTypeLabel), eq(tags.class, m.classLabel))
- )
+ );
// (3) Fetch all matching `tags` for the contractItemId + any of the (tagType, class) pairs.
const tagRows = await db
.select()
.from(tags)
- .where(and(eq(tags.contractItemId, contractItemId), or(...orConditions)))
+ .where(and(eq(tags.contractItemId, contractItemId), or(...orConditions)));
// (4) Fetch (or create) a single `formEntries` row for (contractItemId, formCode).
let [entry] = await db
@@ -375,7 +420,7 @@ export async function syncMissingTags(contractItemId: number, formCode: string)
eq(formEntries.formCode, formCode)
)
)
- .limit(1)
+ .limit(1);
if (!entry) {
const [inserted] = await db
@@ -385,64 +430,64 @@ export async function syncMissingTags(contractItemId: number, formCode: string)
formCode,
data: [], // Initialize with empty array
})
- .returning()
- entry = inserted
+ .returning();
+ entry = inserted;
}
// entry.data는 [{ tagNumber: string, tagDescription?: string }, ...] 형태라고 가정
const existingData = entry.data as Array<{
- tagNumber: string
- tagDescription?: string
- }>
+ tagNumber: string;
+ tagDescription?: string;
+ }>;
// Create a Set of valid tagNumbers from tagRows for efficient lookup
- const validTagNumbers = new Set(tagRows.map(tag => tag.tagNo))
+ const validTagNumbers = new Set(tagRows.map((tag) => tag.tagNo));
// Copy existing data to work with
let updatedData: Array<{
- tagNumber: string
- tagDescription?: string
- }> = []
-
- let createdCount = 0
- let updatedCount = 0
- let deletedCount = 0
+ tagNumber: string;
+ tagDescription?: string;
+ }> = [];
+
+ let createdCount = 0;
+ let updatedCount = 0;
+ let deletedCount = 0;
// First, filter out items that should be deleted (not in validTagNumbers)
for (const item of existingData) {
if (validTagNumbers.has(item.tagNumber)) {
- updatedData.push(item)
+ updatedData.push(item);
} else {
- deletedCount++
+ deletedCount++;
}
}
// (5) For each tagRow, if it's missing in updatedData, push it in.
// 이미 있는 경우에도 description이 달라지면 업데이트할 수 있음.
for (const tagRow of tagRows) {
- const { tagNo, description } = tagRow
-
+ const { tagNo, description } = tagRow;
+
// 5-1. 기존 데이터에서 tagNumber 매칭
const existingIndex = updatedData.findIndex(
(item) => item.tagNumber === tagNo
- )
-
+ );
+
// 5-2. 없다면 새로 추가
if (existingIndex === -1) {
updatedData.push({
tagNumber: tagNo,
tagDescription: description ?? "",
- })
- createdCount++
+ });
+ createdCount++;
} else {
// 5-3. 이미 있으면, description이 다를 때만 업데이트(선택 사항)
- const existingItem = updatedData[existingIndex]
+ const existingItem = updatedData[existingIndex];
if (existingItem.tagDescription !== description) {
updatedData[existingIndex] = {
...existingItem,
tagDescription: description ?? "",
- }
- updatedCount++
+ };
+ updatedCount++;
}
}
}
@@ -452,13 +497,13 @@ export async function syncMissingTags(contractItemId: number, formCode: string)
await db
.update(formEntries)
.set({ data: updatedData })
- .where(eq(formEntries.id, entry.id))
+ .where(eq(formEntries.id, entry.id));
}
// 캐시 무효화 등 후처리
- revalidateTag(`form-data-${formCode}-${contractItemId}`)
+ revalidateTag(`form-data-${formCode}-${contractItemId}`);
- return { createdCount, updatedCount, deletedCount }
+ return { createdCount, updatedCount, deletedCount };
}
/**
@@ -468,10 +513,10 @@ export async function syncMissingTags(contractItemId: number, formCode: string)
* 업데이트 후, revalidateTag()로 캐시 무효화.
*/
type UpdateResponse = {
- success: boolean
- message: string
- data?: any
-}
+ success: boolean;
+ message: string;
+ data?: any;
+};
export async function updateFormDataInDB(
formCode: string,
@@ -480,12 +525,12 @@ export async function updateFormDataInDB(
): Promise<UpdateResponse> {
try {
// 1) tagNumber로 식별
- const tagNumber = newData.tagNumber
+ const tagNumber = newData.tagNumber;
if (!tagNumber) {
return {
success: false,
- message: "tagNumber는 필수 항목입니다."
- }
+ message: "tagNumber는 필수 항목입니다.",
+ };
}
// 2) row 찾기 (단 하나)
@@ -498,52 +543,52 @@ export async function updateFormDataInDB(
eq(formEntries.contractItemId, contractItemId)
)
)
- .limit(1)
+ .limit(1);
if (!entries || entries.length === 0) {
return {
success: false,
- message: `폼 데이터를 찾을 수 없습니다. (formCode=${formCode}, contractItemId=${contractItemId})`
- }
+ message: `폼 데이터를 찾을 수 없습니다. (formCode=${formCode}, contractItemId=${contractItemId})`,
+ };
}
- const entry = entries[0]
+ const entry = entries[0];
// 3) data가 배열인지 확인
if (!entry.data) {
return {
success: false,
- message: "폼 데이터가 없습니다."
- }
+ message: "폼 데이터가 없습니다.",
+ };
}
- const dataArray = entry.data as Array<Record<string, any>>
+ const dataArray = entry.data as Array<Record<string, any>>;
if (!Array.isArray(dataArray)) {
return {
success: false,
- message: "폼 데이터가 올바른 형식이 아닙니다. 배열 형식이어야 합니다."
- }
+ message: "폼 데이터가 올바른 형식이 아닙니다. 배열 형식이어야 합니다.",
+ };
}
// 4) tagNumber = newData.tagNumber 항목 찾기
- const idx = dataArray.findIndex((item) => item.tagNumber === tagNumber)
+ const idx = dataArray.findIndex((item) => item.tagNumber === tagNumber);
if (idx < 0) {
return {
success: false,
- message: `태그 번호 "${tagNumber}"를 가진 항목을 찾을 수 없습니다.`
- }
+ message: `태그 번호 "${tagNumber}"를 가진 항목을 찾을 수 없습니다.`,
+ };
}
// 5) 병합
- const oldItem = dataArray[idx]
+ const oldItem = dataArray[idx];
const updatedItem = {
...oldItem,
...newData,
tagNumber: oldItem.tagNumber, // tagNumber 변경 불가 시 유지
- }
+ };
- const updatedArray = [...dataArray]
- updatedArray[idx] = updatedItem
+ const updatedArray = [...dataArray];
+ updatedArray[idx] = updatedItem;
// 6) DB UPDATE
try {
@@ -551,67 +596,70 @@ export async function updateFormDataInDB(
.update(formEntries)
.set({
data: updatedArray,
- updatedAt: new Date() // 업데이트 시간도 갱신
+ updatedAt: new Date(), // 업데이트 시간도 갱신
})
- .where(eq(formEntries.id, entry.id))
+ .where(eq(formEntries.id, entry.id));
} catch (dbError) {
- console.error("Database update error:", dbError)
+ console.error("Database update error:", dbError);
if (dbError instanceof DrizzleError) {
return {
success: false,
- message: `데이터베이스 업데이트 오류: ${dbError.message}`
- }
+ message: `데이터베이스 업데이트 오류: ${dbError.message}`,
+ };
}
return {
success: false,
- message: "데이터베이스 업데이트 중 오류가 발생했습니다."
- }
+ message: "데이터베이스 업데이트 중 오류가 발생했습니다.",
+ };
}
// 7) Cache 무효화
try {
// 캐시 태그를 form-data-${formCode}-${contractItemId} 형태로 가정
- const cacheTag = `form-data-${formCode}-${contractItemId}`
- revalidateTag(cacheTag)
+ const cacheTag = `form-data-${formCode}-${contractItemId}`;
+ revalidateTag(cacheTag);
} catch (cacheError) {
- console.warn("Cache revalidation warning:", cacheError)
+ console.warn("Cache revalidation warning:", cacheError);
// 캐시 무효화는 실패해도 업데이트 자체는 성공했으므로 경고만 로그로 남김
}
return {
success: true,
- message: '데이터가 성공적으로 업데이트되었습니다.',
+ message: "데이터가 성공적으로 업데이트되었습니다.",
data: {
tagNumber,
- updatedFields: Object.keys(newData).filter(key => key !== 'tagNumber')
- }
- }
+ updatedFields: Object.keys(newData).filter(
+ (key) => key !== "tagNumber"
+ ),
+ },
+ };
} catch (error) {
// 예상치 못한 오류 처리
- console.error("Unexpected error in updateFormDataInDB:", error)
+ console.error("Unexpected error in updateFormDataInDB:", error);
return {
success: false,
- message: error instanceof Error
- ? `예상치 못한 오류가 발생했습니다: ${error.message}`
- : "알 수 없는 오류가 발생했습니다."
- }
+ message:
+ error instanceof Error
+ ? `예상치 못한 오류가 발생했습니다: ${error.message}`
+ : "알 수 없는 오류가 발생했습니다.",
+ };
}
}
// FormColumn Type (동일)
export interface FormColumn {
- key: string
- type: string
- label: string
- options?: string[]
+ key: string;
+ type: string;
+ label: string;
+ options?: string[];
}
interface MetadataResult {
- formName: string
- formCode: string
- columns: FormColumn[]
+ formName: string;
+ formCode: string;
+ columns: FormColumn[];
}
/**
@@ -620,26 +668,140 @@ interface MetadataResult {
* { formName, formCode, columns } 형태로 반환.
* 없으면 null.
*/
-export async function fetchFormMetadata(formCode: string): Promise<MetadataResult | null> {
+export async function fetchFormMetadata(
+ formCode: string
+): Promise<MetadataResult | null> {
try {
// 기존 방식: select().from().where()
const rows = await db
.select()
.from(formMetas)
.where(eq(formMetas.formCode, formCode))
- .limit(1)
+ .limit(1);
// rows는 배열
- const metaData = rows[0]
- if (!metaData) return null
+ const metaData = rows[0];
+ if (!metaData) return null;
return {
formCode: metaData.formCode,
formName: metaData.formName,
- columns: metaData.columns as FormColumn[]
+ columns: metaData.columns as FormColumn[],
+ };
+ } catch (err) {
+ console.error("Error in fetchFormMetadata:", err);
+ return null;
+ }
+}
+
+type GetReportFileList = (
+ packageId: string,
+ formCode: string
+) => Promise<{
+ formId: number;
+}>;
+
+export const getFormId: GetReportFileList = async (packageId, formCode) => {
+ const result: { formId: number } = {
+ formId: 0,
+ };
+ try {
+ const [targetForm] = await db
+ .select()
+ .from(forms)
+ .where(
+ and(
+ eq(forms.formCode, formCode),
+ eq(forms.contractItemId, Number(packageId))
+ )
+ );
+
+ if (!targetForm) {
+ throw new Error("Not Found Target Form");
}
+
+ const { id: formId } = targetForm;
+
+ result.formId = formId;
} catch (err) {
- console.error("Error in fetchFormMetadata:", err)
- return null
+ } finally {
+ return result;
}
-} \ No newline at end of file
+};
+
+type getReportTempList = (
+ packageId: number,
+ formId: number
+) => Promise<VendorDataReportTemps[]>;
+
+export const getReportTempList: getReportTempList = async (
+ packageId,
+ formId
+) => {
+ let result: VendorDataReportTemps[] = [];
+
+ try {
+ result = await db
+ .select()
+ .from(vendorDataReportTemps)
+ .where(
+ and(
+ eq(vendorDataReportTemps.contractItemId, packageId),
+ eq(vendorDataReportTemps.formId, formId)
+ )
+ );
+ } catch (err) {
+ } finally {
+ return result;
+ }
+};
+
+export async function uploadReportTemp(
+ packageId: number,
+ formId: number,
+ formData: FormData
+) {
+ const file = formData.get("file") as File | null;
+ const customFileName = formData.get("customFileName") as string;
+ const uploaderType = (formData.get("uploaderType") as string) || "vendor";
+
+ if (!["vendor", "client", "shi"].includes(uploaderType)) {
+ throw new Error(
+ `Invalid uploaderType: ${uploaderType}. Must be one of: vendor, client, shi`
+ );
+ }
+ if (file && file.size > 0) {
+ const originalName = customFileName;
+ const ext = path.extname(originalName);
+ const uniqueName = uuidv4() + ext;
+ const baseDir = path.join(
+ process.cwd(),
+ "public",
+ "vendorFormData",
+ packageId.toString(),
+ formId.toString()
+ );
+
+ const savePath = path.join(baseDir, uniqueName);
+
+ const arrayBuffer = await file.arrayBuffer();
+ const buffer = Buffer.from(arrayBuffer);
+
+ await fs.mkdir(baseDir, { recursive: true });
+
+ await fs.writeFile(savePath, buffer);
+
+ return db.transaction(async (tx) => {
+ // 파일 정보를 테이블에 저장
+ await tx
+ .insert(vendorDataReportTemps)
+ .values({
+ contractItemId: packageId,
+ formId: formId,
+ fileName: originalName,
+ filePath: `/vendorFormData/${packageId.toString()}/${formId.toString()}/${uniqueName}`,
+ })
+ .returning();
+ });
+ }
+}