// src/lib/items-ship/service.ts "use server"; // Next.js 서버 액션에서 직접 import하려면 (선택) import { revalidateTag, unstable_noStore } from "next/cache"; import db from "@/db/db"; import { filterColumns } from "@/lib/filter-columns"; import { unstable_cache } from "@/lib/unstable-cache"; import { getErrorMessage } from "@/lib/handle-error"; import { asc, desc, ilike, and, or, eq, count, inArray, sql } from "drizzle-orm"; import { GetItemsSchema, UpdateItemSchema, ShipbuildingItemCreateData, TypedItemCreateData, OffshoreTopItemCreateData, OffshoreHullItemCreateData } from "./validations"; import { Item, items, itemShipbuilding, itemOffshoreTop, itemOffshoreHull } from "@/db/schema/items"; import { findAllItems, insertItem, updateItem } from "./repository"; /* ----------------------------------------------------- 1) 조회 관련 ----------------------------------------------------- */ /** * 복잡한 조건으로 Item 목록을 조회 (+ pagination) 하고, * 총 개수에 따라 pageCount를 계산해서 리턴. * Next.js의 unstable_cache를 사용해 일정 시간 캐시. */ export async function getShipbuildingItems(input: GetItemsSchema) { return unstable_cache( async () => { try { const offset = (input.page - 1) * input.perPage; // advancedTable 모드면 filterColumns()로 where 절 구성 const advancedWhere = filterColumns({ table: items, filters: input.filters, joinOperator: input.joinOperator, }); let globalWhere; if (input.search) { const s = `%${input.search}%`; globalWhere = or( ilike(items.itemCode, s), ilike(items.itemName, s), ilike(items.description, s) ); } const finalWhere = and( advancedWhere, globalWhere ); const where = finalWhere; const orderBy = input.sort.length > 0 ? input.sort.map((item) => item.desc ? desc(items[item.id]) : asc(items[item.id]) ) : [asc(items.createdAt)]; // 조선 아이템 테이블과 기본 아이템 테이블 조인하여 조회 const result = await db.select({ id: itemShipbuilding.id, itemId: itemShipbuilding.itemId, workType: itemShipbuilding.workType, shipTypes: itemShipbuilding.shipTypes, itemCode: items.itemCode, itemName: items.itemName, description: items.description, createdAt: itemShipbuilding.createdAt, updatedAt: itemShipbuilding.updatedAt, }) .from(itemShipbuilding) .innerJoin(items, eq(itemShipbuilding.itemId, items.id)) .where(where) .orderBy(...orderBy) .offset(offset) .limit(input.perPage); // 전체 데이터 개수 조회 const [{ count: total }] = await db.select({ count: count() }) .from(itemShipbuilding) .innerJoin(items, eq(itemShipbuilding.itemId, items.id)) .where(where); const pageCount = Math.ceil(Number(total) / input.perPage); return { data: result, pageCount }; } catch (err) { console.error("Error fetching shipbuilding items:", err); return { data: [], pageCount: 0 }; } }, [JSON.stringify(input)], { revalidate: 3600, tags: ["items"], } )(); } export async function getOffshoreTopItems(input: GetItemsSchema) { return unstable_cache( async () => { try { const offset = (input.page - 1) * input.perPage; // advancedTable 모드면 filterColumns()로 where 절 구성 const advancedWhere = filterColumns({ table: items, filters: input.filters, joinOperator: input.joinOperator, }); let globalWhere; if (input.search) { const s = `%${input.search}%`; globalWhere = or( ilike(items.itemCode, s), ilike(items.itemName, s), ilike(items.description, s) ); } const finalWhere = and( advancedWhere, globalWhere ); const where = finalWhere; const orderBy = input.sort.length > 0 ? input.sort.map((item) => item.desc ? desc(items[item.id]) : asc(items[item.id]) ) : [asc(items.createdAt)]; // 해양 TOP 아이템 테이블과 기본 아이템 테이블 조인하여 조회 const result = await db.select({ id: itemOffshoreTop.id, itemId: itemOffshoreTop.itemId, workType: itemOffshoreTop.workType, itemList1: itemOffshoreTop.itemList1, itemList2: itemOffshoreTop.itemList2, itemList3: itemOffshoreTop.itemList3, itemList4: itemOffshoreTop.itemList4, itemCode: items.itemCode, itemName: items.itemName, description: items.description, createdAt: itemOffshoreTop.createdAt, updatedAt: itemOffshoreTop.updatedAt, }) .from(itemOffshoreTop) .innerJoin(items, eq(itemOffshoreTop.itemId, items.id)) .where(where) .orderBy(...orderBy) .offset(offset) .limit(input.perPage); // 전체 데이터 개수 조회 const [{ count: total }] = await db.select({ count: count() }) .from(itemOffshoreTop) .innerJoin(items, eq(itemOffshoreTop.itemId, items.id)) .where(where); const pageCount = Math.ceil(Number(total) / input.perPage); return { data: result, pageCount }; } catch (err) { console.error("Error fetching offshore top items:", err); return { data: [], pageCount: 0 }; } }, [JSON.stringify(input)], { revalidate: 3600, tags: ["items"], } )(); } export async function getOffshoreHullItems(input: GetItemsSchema) { return unstable_cache( async () => { try { const offset = (input.page - 1) * input.perPage; // advancedTable 모드면 filterColumns()로 where 절 구성 const advancedWhere = filterColumns({ table: items, filters: input.filters, joinOperator: input.joinOperator, }); let globalWhere; if (input.search) { const s = `%${input.search}%`; globalWhere = or( ilike(items.itemCode, s), ilike(items.itemName, s), ilike(items.description, s) ); } const finalWhere = and( advancedWhere, globalWhere ); const where = finalWhere; const orderBy = input.sort.length > 0 ? input.sort.map((item) => item.desc ? desc(items[item.id]) : asc(items[item.id]) ) : [asc(items.createdAt)]; // 해양 HULL 아이템 테이블과 기본 아이템 테이블 조인하여 조회 const result = await db.select({ id: itemOffshoreHull.id, itemId: itemOffshoreHull.itemId, workType: itemOffshoreHull.workType, itemList1: itemOffshoreHull.itemList1, itemList2: itemOffshoreHull.itemList2, itemList3: itemOffshoreHull.itemList3, itemList4: itemOffshoreHull.itemList4, itemCode: items.itemCode, itemName: items.itemName, description: items.description, createdAt: itemOffshoreHull.createdAt, updatedAt: itemOffshoreHull.updatedAt, }) .from(itemOffshoreHull) .innerJoin(items, eq(itemOffshoreHull.itemId, items.id)) .where(where) .orderBy(...orderBy) .offset(offset) .limit(input.perPage); // 전체 데이터 개수 조회 const [{ count: total }] = await db.select({ count: count() }) .from(itemOffshoreHull) .innerJoin(items, eq(itemOffshoreHull.itemId, items.id)) .where(where); const pageCount = Math.ceil(Number(total) / input.perPage); return { data: result, pageCount }; } catch (err) { console.error("Error fetching offshore hull items:", err); return { data: [], pageCount: 0 }; } }, [JSON.stringify(input)], { revalidate: 3600, tags: ["items"], } )(); } /* ----------------------------------------------------- 2) 생성(Create) ----------------------------------------------------- */ /** * Item 생성 - 아이템 타입에 따라 해당 테이블에 데이터 삽입 */ export async function createShipbuildingItem(input: TypedItemCreateData) { unstable_noStore() try { if (!input.itemCode || !input.itemName) { return { success: false, message: "아이템 코드와 아이템 명은 필수입니다", data: null, error: "필수 필드 누락" } } const result = await db.transaction(async (tx) => { // 1. itemCode 정규화해서 직접 쿼리 const existRows = await tx.select().from(items) .where(eq(items.itemCode, input.itemCode)); const existingItem = existRows[0]; let itemId: number; let itemResult; if (existingItem) { // 이미 있으면 업데이트 itemResult = await updateItem(tx, existingItem.id, { itemName: input.itemName, description: input.description, }); itemId = existingItem.id; } else { // 없으면 새로 생성 // 현재 가장 큰 ID 값 가져오기 const maxIdResult = await tx.select({ maxId: sql`MAX(id)` }).from(items); const maxId = maxIdResult[0]?.maxId || 0; const newId = Number(maxId) + 1; // 새 ID로 아이템 생성 itemResult = await tx.insert(items).values({ id: newId, itemCode: input.itemCode, itemName: input.itemName, description: input.description, }).returning(); itemId = itemResult[0].id; } const shipData = input as ShipbuildingItemCreateData; const typeResult = await tx.insert(itemShipbuilding).values({ itemId: itemId, workType: shipData.workType ? (shipData.workType as '기장' | '전장' | '선실' | '배관' | '철의') : '기장', shipTypes: shipData.shipTypes || '', createdAt: new Date(), updatedAt: new Date() }).returning(); return { itemData: itemResult[0], shipbuildingData: typeResult[0] }; }) revalidateTag("items") return { success: true, data: result || null, error: null } } catch (err) { console.error("아이템 생성/업데이트 오류:", err) if (err instanceof Error && err.message.includes("unique constraint")) { return { success: false, message: "이미 존재하는 아이템 코드입니다", data: null, error: "중복 키 오류" } } return { success: false, message: getErrorMessage(err), data: null, error: getErrorMessage(err) } } } /** * Excel import를 위한 조선 아이템 생성 함수 * 하나의 아이템 코드에 대해 여러 선종을 처리 (1:N 관계) */ export async function createShipbuildingImportItem(input: { itemCode: string; itemName: string; workType: '기장' | '전장' | '선실' | '배관' | '철의'; description?: string | null; shipTypes: Record; }) { unstable_noStore(); try { if (!input.itemCode || !input.itemName) { return { success: false, message: "아이템 코드와 아이템 명은 필수입니다", data: null, error: "필수 필드 누락" } } const results = await db.transaction(async (tx) => { // 1. itemCode 정규화해서 직접 쿼리 const existRows = await tx.select().from(items) .where(eq(items.itemCode, input.itemCode)); const existingItem = existRows[0]; console.log('DB에서 직접 조회한 기존 아이템:', existingItem); let itemId: number; if (existingItem) { // 이미 있으면 업데이트 await updateItem(tx, existingItem.id, { itemName: input.itemName, description: input.description, }); itemId = existingItem.id; console.log('기존 아이템 업데이트, id:', itemId); } else { // 없으면 새로 생성 // 현재 가장 큰 ID 값 가져오기 const maxIdResult = await tx.select({ maxId: sql`MAX(id)` }).from(items); const maxId = maxIdResult[0]?.maxId || 0; const newId = Number(maxId) + 1; console.log('새 아이템 생성을 위한 ID 계산:', { maxId, newId }); // 새 ID로 아이템 생성 const insertResult = await tx.insert(items).values({ id: newId, itemCode: input.itemCode, itemName: input.itemName, description: input.description, }).returning(); itemId = insertResult[0].id; console.log('새 아이템 생성 완료, id:', itemId); } const createdItems = []; for (const shipType of Object.keys(input.shipTypes)) { // 그대로 선종명 string으로 저장 const existShip = await tx.select().from(itemShipbuilding) .where( and( eq(itemShipbuilding.itemId, itemId), eq(itemShipbuilding.shipTypes, shipType) ) ); if (!existShip[0]) { const shipbuildingResult = await tx.insert(itemShipbuilding).values({ itemId: itemId, workType: input.workType, shipTypes: shipType, createdAt: new Date(), updatedAt: new Date() }).returning(); createdItems.push({ ...shipbuildingResult[0] }); console.log('조선아이템 생성:', shipType, shipbuildingResult[0]); } else { console.log('이미 존재하는 조선아이템:', shipType); } } return createdItems; }); revalidateTag("items"); return { success: true, data: results, error: null } } catch (err) { return { success: false, message: getErrorMessage(err), data: null, error: getErrorMessage(err) } } } export async function createOffshoreTopItem(data: OffshoreTopItemCreateData) { unstable_noStore() try { if (!data.itemCode || !data.itemName) { return { success: false, message: "아이템 코드와 아이템 명은 필수입니다", data: null, error: "필수 필드 누락" } } // 트랜잭션 내에서 처리 const result = await db.transaction(async (tx) => { // 1. itemCode 정규화해서 직접 쿼리 const existRows = await tx.select().from(items) .where(eq(items.itemCode, data.itemCode)); const existingItem = existRows[0]; let itemId: number; let itemResult; if (existingItem) { // 이미 있으면 업데이트 itemResult = await updateItem(tx, existingItem.id, { itemName: data.itemName, description: data.description, }); itemId = existingItem.id; } else { // 없으면 새로 생성 // 현재 가장 큰 ID 값 가져오기 const maxIdResult = await tx.select({ maxId: sql`MAX(id)` }).from(items); const maxId = maxIdResult[0]?.maxId || 0; const newId = Number(maxId) + 1; // 새 ID로 아이템 생성 itemResult = await tx.insert(items).values({ id: newId, itemCode: data.itemCode, itemName: data.itemName, description: data.description, }).returning(); itemId = itemResult[0].id; } const [offshoreTop] = await tx .insert(itemOffshoreTop) .values({ itemId: itemId, workType: data.workType, itemList1: data.itemList1 || null, itemList2: data.itemList2 || null, itemList3: data.itemList3 || null, itemList4: data.itemList4 || null, createdAt: new Date(), updatedAt: new Date() }) .returning(); return { itemData: itemResult[0], offshoreTopData: offshoreTop }; }) revalidateTag("items") return { success: true, data: result, error: null } } catch (err) { console.error("아이템 생성/업데이트 오류:", err) if (err instanceof Error && err.message.includes("unique constraint")) { return { success: false, message: "이미 존재하는 아이템 코드입니다", data: null, error: "중복 키 오류" } } return { success: false, message: getErrorMessage(err), data: null, error: getErrorMessage(err) } } } export async function createOffshoreHullItem(data: OffshoreHullItemCreateData) { unstable_noStore() try { if (!data.itemCode || !data.itemName) { return { success: false, message: "아이템 코드와 아이템 명은 필수입니다", data: null, error: "필수 필드 누락" } } // 트랜잭션 내에서 처리 const result = await db.transaction(async (tx) => { // 1. itemCode 정규화해서 직접 쿼리 const existRows = await tx.select().from(items) .where(eq(items.itemCode, data.itemCode)); const existingItem = existRows[0]; let itemId: number; let itemResult; if (existingItem) { // 이미 있으면 업데이트 itemResult = await updateItem(tx, existingItem.id, { itemName: data.itemName, description: data.description, }); itemId = existingItem.id; } else { // 없으면 새로 생성 // 현재 가장 큰 ID 값 가져오기 const maxIdResult = await tx.select({ maxId: sql`MAX(id)` }).from(items); const maxId = maxIdResult[0]?.maxId || 0; const newId = Number(maxId) + 1; // 새 ID로 아이템 생성 itemResult = await tx.insert(items).values({ id: newId, itemCode: data.itemCode, itemName: data.itemName, description: data.description, }).returning(); itemId = itemResult[0].id; } const [offshoreHull] = await tx .insert(itemOffshoreHull) .values({ itemId: itemId, workType: data.workType, itemList1: data.itemList1 || null, itemList2: data.itemList2 || null, itemList3: data.itemList3 || null, itemList4: data.itemList4 || null, createdAt: new Date(), updatedAt: new Date() }) .returning(); return { itemData: itemResult[0], offshoreHullData: offshoreHull }; }) revalidateTag("items") return { success: true, data: result, error: null } } catch (err) { console.error("아이템 생성/업데이트 오류:", err) if (err instanceof Error && err.message.includes("unique constraint")) { return { success: false, message: "이미 존재하는 아이템 코드입니다", data: null, error: "중복 키 오류" } } return { success: false, message: getErrorMessage(err), data: null, error: getErrorMessage(err) } } } /* ----------------------------------------------------- 3) 업데이트 ----------------------------------------------------- */ // 업데이트 타입 정의 인터페이스 interface UpdateShipbuildingItemInput extends UpdateItemSchema { id: number; workType?: string; shipTypes?: string; itemCode?: string; itemName?: string; description?: string; } /** 단건 업데이트 */ export async function modifyShipbuildingItem(input: UpdateShipbuildingItemInput) { unstable_noStore(); try { const result = await db.transaction(async (tx) => { // 기존 아이템 조회 const existingShipbuilding = await tx.query.itemShipbuilding.findFirst({ where: eq(itemShipbuilding.id, input.id), with: { item: true } }); if (!existingShipbuilding) { throw new Error("아이템을 찾을 수 없습니다."); } const existingItem = existingShipbuilding.item; // 아이템 테이블 정보가 변경되었는지 확인 const isItemChanged = (input.itemCode && input.itemCode !== existingItem.itemCode) || (input.itemName && input.itemName !== existingItem.itemName) || (input.description !== undefined && input.description !== existingItem.description); // 세부 아이템 정보만 변경된 경우 if (!isItemChanged) { // 조선 아이템 테이블 업데이트 if (input.workType || input.shipTypes) { await tx.update(itemShipbuilding) .set({ workType: input.workType as '기장' | '전장' | '선실' | '배관' | '철의', shipTypes: input.shipTypes }) .where(eq(itemShipbuilding.id, input.id)); } return { data: { id: input.id }, error: null, success: true, message: "아이템이 성공적으로 업데이트되었습니다." }; } // 아이템 테이블 정보가 변경된 경우 - 새 아이템 생성하고 세부 아이템 연결 else { // 새 아이템 생성 const [newItem] = await insertItem(tx, { itemCode: input.itemCode || existingItem.itemCode, itemName: input.itemName || existingItem.itemName, description: input.description !== undefined ? input.description : existingItem.description, }); // 세부 아이템 테이블 정보를 새 아이템에 연결 await tx.update(itemShipbuilding) .set({ itemId: newItem.id, workType: input.workType ? (input.workType as '기장' | '전장' | '선실' | '배관' | '철의') : existingShipbuilding.workType, shipTypes: input.shipTypes || existingShipbuilding.shipTypes }) .where(eq(itemShipbuilding.id, input.id)); return { data: { id: input.id }, error: null, success: true, message: "새 아이템이 생성되고 세부 정보가 업데이트되었습니다." }; } }); // 캐시 무효화 revalidateTag("items"); return result; } catch (err) { return { data: null, error: getErrorMessage(err), success: false, message: "아이템 업데이트 중 오류가 발생했습니다." }; } } // Offshore TOP 업데이트 타입 정의 인터페이스 interface UpdateOffshoreTopItemInput extends UpdateItemSchema { id: number; workType?: string; itemList1?: string; itemList2?: string; itemList3?: string; itemList4?: string; itemCode?: string; itemName?: string; description?: string; } /** Offshore TOP 단건 업데이트 */ export async function modifyOffshoreTopItem(input: UpdateOffshoreTopItemInput) { unstable_noStore(); try { const result = await db.transaction(async (tx) => { // 기존 아이템 조회 const existingOffshoreTop = await tx.query.itemOffshoreTop.findFirst({ where: eq(itemOffshoreTop.id, input.id), with: { item: true } }); if (!existingOffshoreTop) { throw new Error("아이템을 찾을 수 없습니다."); } const existingItem = existingOffshoreTop.item; // 아이템 테이블 정보가 변경되었는지 확인 const isItemChanged = (input.itemCode && input.itemCode !== existingItem.itemCode) || (input.itemName && input.itemName !== existingItem.itemName) || (input.description !== undefined && input.description !== existingItem.description); // 세부 아이템 정보만 변경된 경우 if (!isItemChanged) { // Offshore TOP 아이템 테이블 업데이트 const updateData: Record = {}; if (input.workType) updateData.workType = input.workType as 'TM' | 'TS' | 'TE' | 'TP'; if (input.itemList1 !== undefined) updateData.itemList1 = input.itemList1; if (input.itemList2 !== undefined) updateData.itemList2 = input.itemList2; if (input.itemList3 !== undefined) updateData.itemList3 = input.itemList3; if (input.itemList4 !== undefined) updateData.itemList4 = input.itemList4; if (Object.keys(updateData).length > 0) { await tx.update(itemOffshoreTop) .set(updateData) .where(eq(itemOffshoreTop.id, input.id)); } return { data: { id: input.id }, error: null, success: true, message: "아이템이 성공적으로 업데이트되었습니다." }; } // 아이템 테이블 정보가 변경된 경우 - 새 아이템 생성하고 세부 아이템 연결 else { // 새 아이템 생성 const [newItem] = await insertItem(tx, { itemCode: input.itemCode || existingItem.itemCode, itemName: input.itemName || existingItem.itemName, description: input.description !== undefined ? input.description : existingItem.description, }); // 세부 아이템 테이블 정보를 새 아이템에 연결 const updateData: Record = { itemId: newItem.id }; if (input.workType) updateData.workType = input.workType as 'TM' | 'TS' | 'TE' | 'TP'; if (input.itemList1 !== undefined) updateData.itemList1 = input.itemList1; if (input.itemList2 !== undefined) updateData.itemList2 = input.itemList2; if (input.itemList3 !== undefined) updateData.itemList3 = input.itemList3; if (input.itemList4 !== undefined) updateData.itemList4 = input.itemList4; await tx.update(itemOffshoreTop) .set(updateData) .where(eq(itemOffshoreTop.id, input.id)); return { data: { id: input.id }, error: null, success: true, message: "새 아이템이 생성되고 세부 정보가 업데이트되었습니다." }; } }); // 캐시 무효화 revalidateTag("items"); return result; } catch (err) { return { data: null, error: getErrorMessage(err), success: false, message: "아이템 업데이트 중 오류가 발생했습니다." }; } } // Offshore HULL 업데이트 타입 정의 인터페이스 interface UpdateOffshoreHullItemInput extends UpdateItemSchema { id: number; workType?: string; itemList1?: string; itemList2?: string; itemList3?: string; itemList4?: string; itemCode?: string; itemName?: string; description?: string; } /** Offshore HULL 단건 업데이트 */ export async function modifyOffshoreHullItem(input: UpdateOffshoreHullItemInput) { unstable_noStore(); try { const result = await db.transaction(async (tx) => { // 기존 아이템 조회 const existingOffshoreHull = await tx.query.itemOffshoreHull.findFirst({ where: eq(itemOffshoreHull.id, input.id), with: { item: true } }); if (!existingOffshoreHull) { throw new Error("아이템을 찾을 수 없습니다."); } const existingItem = existingOffshoreHull.item; // 아이템 테이블 정보가 변경되었는지 확인 const isItemChanged = (input.itemCode && input.itemCode !== existingItem.itemCode) || (input.itemName && input.itemName !== existingItem.itemName) || (input.description !== undefined && input.description !== existingItem.description); // 세부 아이템 정보만 변경된 경우 if (!isItemChanged) { // Offshore HULL 아이템 테이블 업데이트 const updateData: Record = {}; if (input.workType) updateData.workType = input.workType as 'HA' | 'HE' | 'HH' | 'HM' | 'NC'; if (input.itemList1 !== undefined) updateData.itemList1 = input.itemList1; if (input.itemList2 !== undefined) updateData.itemList2 = input.itemList2; if (input.itemList3 !== undefined) updateData.itemList3 = input.itemList3; if (input.itemList4 !== undefined) updateData.itemList4 = input.itemList4; if (Object.keys(updateData).length > 0) { await tx.update(itemOffshoreHull) .set(updateData) .where(eq(itemOffshoreHull.id, input.id)); } return { data: { id: input.id }, error: null, success: true, message: "아이템이 성공적으로 업데이트되었습니다." }; } // 아이템 테이블 정보가 변경된 경우 - 새 아이템 생성하고 세부 아이템 연결 else { // 새 아이템 생성 const [newItem] = await insertItem(tx, { itemCode: input.itemCode || existingItem.itemCode, itemName: input.itemName || existingItem.itemName, description: input.description !== undefined ? input.description : existingItem.description, }); // 세부 아이템 테이블 정보를 새 아이템에 연결 const updateData: Record = { itemId: newItem.id }; if (input.workType) updateData.workType = input.workType as 'HA' | 'HE' | 'HH' | 'HM' | 'NC'; if (input.itemList1 !== undefined) updateData.itemList1 = input.itemList1; if (input.itemList2 !== undefined) updateData.itemList2 = input.itemList2; if (input.itemList3 !== undefined) updateData.itemList3 = input.itemList3; if (input.itemList4 !== undefined) updateData.itemList4 = input.itemList4; await tx.update(itemOffshoreHull) .set(updateData) .where(eq(itemOffshoreHull.id, input.id)); return { data: { id: input.id }, error: null, success: true, message: "새 아이템이 생성되고 세부 정보가 업데이트되었습니다." }; } }); // 캐시 무효화 revalidateTag("items"); return result; } catch (err) { return { data: null, error: getErrorMessage(err), success: false, message: "아이템 업데이트 중 오류가 발생했습니다." }; } } /* ----------------------------------------------------- 4) 삭제 ----------------------------------------------------- */ // 삭제 타입 정의 인터페이스 interface DeleteItemInput { id: number; } interface DeleteItemsInput { ids: number[]; } /** 단건 삭제 */ export async function removeShipbuildingItem(input: DeleteItemInput) { unstable_noStore(); try { await db.transaction(async (tx) => { // 세부 아이템만 삭제 (아이템 테이블은 유지) await tx.delete(itemShipbuilding) .where(eq(itemShipbuilding.id, input.id)); }); revalidateTag("items"); return { data: null, error: null, success: true, message: "아이템이 성공적으로 삭제되었습니다." }; } catch (err) { return { data: null, error: getErrorMessage(err), success: false, message: "아이템 삭제 중 오류가 발생했습니다." }; } } /** 복수 삭제 */ export async function removeShipbuildingItems(input: DeleteItemsInput) { unstable_noStore(); try { await db.transaction(async (tx) => { if (input.ids.length > 0) { // 세부 아이템만 삭제 (아이템 테이블은 유지) await tx.delete(itemShipbuilding) .where(inArray(itemShipbuilding.id, input.ids)); } }); revalidateTag("items"); return { data: null, error: null, success: true, message: "아이템이 성공적으로 삭제되었습니다." }; } catch (err) { return { data: null, error: getErrorMessage(err), success: false, message: "아이템 삭제 중 오류가 발생했습니다." }; } } /** Offshore TOP 단건 삭제 */ export async function removeOffshoreTopItem(input: DeleteItemInput) { unstable_noStore(); try { await db.transaction(async (tx) => { // 세부 아이템만 삭제 (아이템 테이블은 유지) await tx.delete(itemOffshoreTop) .where(eq(itemOffshoreTop.id, input.id)); }); revalidateTag("items"); return { data: null, error: null, success: true, message: "아이템이 성공적으로 삭제되었습니다." }; } catch (err) { return { data: null, error: getErrorMessage(err), success: false, message: "아이템 삭제 중 오류가 발생했습니다." }; } } /** Offshore TOP 복수 삭제 */ export async function removeOffshoreTopItems(input: DeleteItemsInput) { unstable_noStore(); try { await db.transaction(async (tx) => { if (input.ids.length > 0) { // 세부 아이템만 삭제 (아이템 테이블은 유지) await tx.delete(itemOffshoreTop) .where(inArray(itemOffshoreTop.id, input.ids)); } }); revalidateTag("items"); return { data: null, error: null, success: true, message: "아이템이 성공적으로 삭제되었습니다." }; } catch (err) { return { data: null, error: getErrorMessage(err), success: false, message: "아이템 삭제 중 오류가 발생했습니다." }; } } /** Offshore HULL 단건 삭제 */ export async function removeOffshoreHullItem(input: DeleteItemInput) { unstable_noStore(); try { await db.transaction(async (tx) => { // 세부 아이템만 삭제 (아이템 테이블은 유지) await tx.delete(itemOffshoreHull) .where(eq(itemOffshoreHull.id, input.id)); }); revalidateTag("items"); return { data: null, error: null, success: true, message: "아이템이 성공적으로 삭제되었습니다." }; } catch (err) { return { data: null, error: getErrorMessage(err), success: false, message: "아이템 삭제 중 오류가 발생했습니다." }; } } /** Offshore HULL 복수 삭제 */ export async function removeOffshoreHullItems(input: DeleteItemsInput) { unstable_noStore(); try { await db.transaction(async (tx) => { if (input.ids.length > 0) { // 세부 아이템만 삭제 (아이템 테이블은 유지) await tx.delete(itemOffshoreHull) .where(inArray(itemOffshoreHull.id, input.ids)); } }); revalidateTag("items"); return { data: null, error: null, success: true, message: "아이템이 성공적으로 삭제되었습니다." }; } catch (err) { return { data: null, error: getErrorMessage(err), success: false, message: "아이템 삭제 중 오류가 발생했습니다." }; } } export async function getAllShipbuildingItems(): Promise { try { return await findAllItems(); } catch (error) { console.error("Failed to get items:", error); throw new Error("Failed to get items"); } }