diff options
Diffstat (limited to 'lib/general-check-list/service.ts')
| -rw-r--r-- | lib/general-check-list/service.ts | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/lib/general-check-list/service.ts b/lib/general-check-list/service.ts new file mode 100644 index 00000000..fde756ea --- /dev/null +++ b/lib/general-check-list/service.ts @@ -0,0 +1,245 @@ +"use server"; // Next.js 서버 액션에서 직접 import하려면 (선택) + +import db from "@/db/db"; + +import { filterColumns } from "@/lib/filter-columns"; + +import { asc, desc, ilike, inArray, and, gte, lte, not, or, sql, eq } from "drizzle-orm"; +import { generalEvaluations} from "@/db/schema"; +import { GetGeneralEvaluationsSchema } from "./validation"; +import { selectGeneralCheckLists , countGeneralCheckList} from "./repository"; + +export async function getGeneralCheckList(input: GetGeneralEvaluationsSchema) { + try { + const offset = (input.page - 1) * input.perPage; + + + // 고급 필터 처리 - 테이블의 DataTableFilterList에서 오는 필터 + const advancedFilters = input.filters || []; + const advancedJoinOperator = input.joinOperator || "and"; + + + // 고급 필터 조건 생성 + let advancedWhere; + if (advancedFilters.length > 0) { + advancedWhere = filterColumns({ + table: generalEvaluations, + filters: advancedFilters, + joinOperator: advancedJoinOperator, + }); + } + + // 전역 검색 조건 + let globalWhere; + if (input.search) { + const s = `%${input.search}%`; + globalWhere = or( + ilike(generalEvaluations.category, s), + ilike(generalEvaluations.inspectionItem, s), + ilike(generalEvaluations.remarks, s), + ); + } + + // 모든 조건 결합 + let whereConditions = []; + if (advancedWhere) whereConditions.push(advancedWhere); + if (globalWhere) whereConditions.push(globalWhere); + + // 조건이 있을 때만 and() 사용 + const finalWhere = whereConditions.length > 0 + ? and(...whereConditions) + : undefined; + + + // 정렬 조건 - 안전하게 처리 + const orderBy = + input.sort && input.sort.length > 0 + ? input.sort.map((item) => + item.desc + ? desc(generalEvaluations[item.id]) + : asc(generalEvaluations[item.id]) + ) + : [desc(generalEvaluations.updatedAt)] + + + // 트랜잭션 내부에서 Repository 호출 + const { data, total } = await db.transaction(async (tx) => { + const data = await selectGeneralCheckLists(tx, { + where: finalWhere, + orderBy, + offset, + limit: input.perPage, + }); + + const total = await countGeneralCheckList(tx, finalWhere); + return { data, total }; + }); + + const pageCount = Math.ceil(total / input.perPage); + + return { data, pageCount }; + } catch (err) { + console.error("getRfqs 에러:", err); + + // 에러 세부 정보 더 자세히 로깅 + if (err instanceof Error) { + console.error("에러 메시지:", err.message); + console.error("에러 스택:", err.stack); + + if ('code' in err) { + console.error("SQL 에러 코드:", (err as any).code); + } + } + + // 에러 발생 시 디폴트 + return { data: [], pageCount: 0 }; + } +} + +// ───────────────────────────────────────────────────────────────────────────── +// Types +// ───────────────────────────────────────────────────────────────────────────── +export type GeneralEvaluationInput = { + category: string; + inspectionItem: string; + remarks?: string | null; + isActive?: boolean; + }; + + // ───────────────────────────────────────────────────────────────────────────── + // Helpers + // ───────────────────────────────────────────────────────────────────────────── + async function generateSerialNumber(tx: typeof db, category: string) { + const prefix = `GE-${category}-`; + + // 카테고리 내에서 가장 최근 시리얼 찾아서 +1 + const latest = await tx + .select({ serialNumber: generalEvaluations.serialNumber }) + .from(generalEvaluations) + .where(eq(generalEvaluations.category, category)) + .orderBy(desc(generalEvaluations.serialNumber)) + .limit(1); + + let nextSeq = 1; + if (latest.length) { + const parts = latest[0].serialNumber.split("-"); + const last = parts[parts.length - 1]; + const num = parseInt(last, 10); + if (!isNaN(num)) nextSeq = num + 1; + } + + const seqStr = nextSeq.toString().padStart(3, "0"); + return `${prefix}${seqStr}`; + } + // ───────────────────────────────────────────────────────────────────────────── + // CRUD Actions + // ───────────────────────────────────────────────────────────────────────────── + export async function createGeneralEvaluation(input: GeneralEvaluationInput) { + return db.transaction(async (tx) => { + try { + const serialNumber = await generateSerialNumber(tx, input.category); + + const [created] = await tx + .insert(generalEvaluations) + .values({ + serialNumber, + category: input.category, + inspectionItem: input.inspectionItem, + remarks: input.remarks ?? null, + isActive: input.isActive ?? true, + }) + .returning(); + + return { success: true, data: created, message: "체크리스트가 추가되었습니다." }; + } catch (err) { + console.error("createGeneralEvaluation error", err); + return { success: false, message: "추가 중 오류가 발생했습니다." }; + } + }); + } + + export async function updateGeneralEvaluation(id: number, fields: Partial<GeneralEvaluationInput>) { + try { + const [updated] = await db + .update(generalEvaluations) + .set({ ...fields, updatedAt: sql`now()` }) + .where(eq(generalEvaluations.id, id)) + .returning(); + + return { success: true, data: updated, message: "수정되었습니다." }; + } catch (err) { + console.error("updateGeneralEvaluation error", err); + return { success: false, message: "수정 중 오류가 발생했습니다." }; + } + } + + export async function deleteGeneralEvaluations(ids: number[]) { + if (ids.length === 0) return { success: false, message: "삭제할 항목이 없습니다." }; + try { + await db.delete(generalEvaluations).where(inArray(generalEvaluations.id, ids)); + return { success: true, message: `${ids.length}개의 체크리스트가 삭제되었습니다.` }; + } catch (err) { + console.error("deleteGeneralEvaluations error", err); + return { success: false, message: "삭제 중 오류가 발생했습니다." }; + } + } + + // ───────────────────────────────────────────────────────────────────────────── + // Pagination Search (기존 getGeneralCheckList → getGeneralEvaluations) + // ───────────────────────────────────────────────────────────────────────────── + export async function getGeneralEvaluations(input: GetGeneralEvaluationsSchema) { + const offset = (input.page - 1) * input.perPage; + + // 고급 필터 처리 + const advFilters = input.filters ?? []; + const advOperator = input.joinOperator ?? "and"; + const advWhere = advFilters.length + ? filterColumns({ table: generalEvaluations, filters: advFilters, joinOperator: advOperator }) + : undefined; + + // 전역 검색 + const globalWhere = input.search + ? or( + ilike(generalEvaluations.serialNumber, `%${input.search}%`), + ilike(generalEvaluations.category, `%${input.search}%`), + ilike(generalEvaluations.inspectionItem, `%${input.search}%`), + ilike(generalEvaluations.remarks, `%${input.search}%`) + ) + : undefined; + + const whereAll = [advWhere, globalWhere].filter(Boolean); + const finalWhere = whereAll.length ? and(...whereAll) : undefined; + + // 정렬 + const orderBy = + input.sort && input.sort.length > 0 + ? input.sort.map((item) => + item.desc + ? desc(generalEvaluations[item.id]) + : asc(generalEvaluations[item.id]) + ) + : [desc(generalEvaluations.updatedAt)] + + + // 트랜잭션 + const { data, total } = await db.transaction(async (tx) => { + const data = await tx + .select() + .from(generalEvaluations) + .where(finalWhere ?? undefined) + .orderBy(...orderBy) + .limit(input.perPage) + .offset(offset); + + const [{ count }] = await tx + .select({ count: sql<number>`count(*)` }) + .from(generalEvaluations) + .where(finalWhere ?? undefined); + + return { data, total: count }; + }); + + + return { data, pageCount: Math.ceil(total / input.perPage) }; + } +
\ No newline at end of file |
