summaryrefslogtreecommitdiff
path: root/lib/general-check-list/service.ts
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-06-19 09:44:28 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-06-19 09:44:28 +0000
commit95bbe9c583ff841220da1267630e7b2025fc36dc (patch)
tree5e3d5bb3302530bbaa7f7abbe8c9cf8193ccbd4c /lib/general-check-list/service.ts
parent0eb030580b5cbe5f03d570c3c9d8c519bac3b783 (diff)
(대표님) 20250619 1844 KST 작업사항
Diffstat (limited to 'lib/general-check-list/service.ts')
-rw-r--r--lib/general-check-list/service.ts245
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