summaryrefslogtreecommitdiff
path: root/lib/vendor-type/service.ts
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-04-28 02:13:30 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-04-28 02:13:30 +0000
commitef4c533ebacc2cdc97e518f30e9a9350004fcdfb (patch)
tree345251a3ed0f4429716fa5edaa31024d8f4cb560 /lib/vendor-type/service.ts
parent9ceed79cf32c896f8a998399bf1b296506b2cd4a (diff)
~20250428 작업사항
Diffstat (limited to 'lib/vendor-type/service.ts')
-rw-r--r--lib/vendor-type/service.ts239
1 files changed, 239 insertions, 0 deletions
diff --git a/lib/vendor-type/service.ts b/lib/vendor-type/service.ts
new file mode 100644
index 00000000..8624bb0e
--- /dev/null
+++ b/lib/vendor-type/service.ts
@@ -0,0 +1,239 @@
+"use server"; // Next.js 서버 액션에서 직접 import하려면 (선택)
+
+import { revalidateTag, unstable_noStore } from "next/cache";
+import db from "@/db/db";
+import { customAlphabet } from "nanoid";
+
+import { filterColumns } from "@/lib/filter-columns";
+import { unstable_cache } from "@/lib/unstable-cache";
+import { getErrorMessage } from "@/lib/handle-error";
+
+import { asc, desc, ilike, inArray, and, gte, lte, not, or, eq } from "drizzle-orm";
+import { CreateVendorTypeSchema, GetVendorTypesSchema, UpdateVendorTypeSchema } from "./validations";
+import {
+ countVendorTypes,
+ deleteVendorTypeById,
+ deleteVendorTypesByIds,
+ findAllVendorTypes,
+ insertVendorType,
+ selectVendorTypes,
+ updateVendorType
+} from "./repository";
+import { vendorTypes } from "@/db/schema";
+
+/* -----------------------------------------------------
+ 1) 조회 관련
+----------------------------------------------------- */
+
+/**
+ * 복잡한 조건으로 VendorType 목록을 조회 (+ pagination) 하고,
+ * 총 개수에 따라 pageCount를 계산해서 리턴.
+ * Next.js의 unstable_cache를 사용해 일정 시간 캐시.
+ */
+export async function getVendorTypes(input: GetVendorTypesSchema) {
+ return unstable_cache(
+ async () => {
+ try {
+ const offset = (input.page - 1) * input.perPage;
+
+ // advancedTable 모드면 filterColumns()로 where 절 구성
+ const advancedWhere = filterColumns({
+ table: vendorTypes,
+ filters: input.filters,
+ joinOperator: input.joinOperator,
+ });
+
+ let globalWhere;
+ if (input.search) {
+ const s = `%${input.search}%`;
+ globalWhere = or(
+ ilike(vendorTypes.nameKo, s),
+ ilike(vendorTypes.nameEn, s),
+ ilike(vendorTypes.code, s)
+ );
+ }
+
+ const finalWhere = and(
+ advancedWhere,
+ globalWhere
+ );
+
+ // 아니면 ilike, inArray, gte 등으로 where 절 구성
+ const where = finalWhere;
+
+ const orderBy =
+ input.sort.length > 0
+ ? input.sort.map((item) =>
+ item.desc ? desc(vendorTypes[item.id]) : asc(vendorTypes[item.id])
+ )
+ : [asc(vendorTypes.createdAt)];
+
+ // 트랜잭션 내부에서 Repository 호출
+ const { data, total } = await db.transaction(async (tx) => {
+ const data = await selectVendorTypes(tx, {
+ where,
+ orderBy,
+ offset,
+ limit: input.perPage,
+ });
+
+ const total = await countVendorTypes(tx, where);
+ return { data, total };
+ });
+
+ const pageCount = Math.ceil(total / input.perPage);
+
+ return { data, pageCount };
+ } catch (err) {
+ console.log(err, "err")
+ // 에러 발생 시 디폴트
+ return { data: [], pageCount: 0 };
+ }
+ },
+ [JSON.stringify(input)], // 캐싱 키
+ {
+ revalidate: 3600,
+ tags: ["vendorTypes"], // revalidateTag("vendorTypes") 호출 시 무효화
+ }
+ )();
+}
+
+/* -----------------------------------------------------
+ 2) 생성(Create)
+----------------------------------------------------- */
+export interface VendorTypeCreateData {
+ code?: string;
+ nameKo: string;
+ nameEn: string;
+}
+
+/**
+ * VendorType 생성
+ */
+export async function createVendorType(input: VendorTypeCreateData) {
+ unstable_noStore(); // Next.js 서버 액션 캐싱 방지
+
+ try {
+ if (!input.nameKo || !input.nameEn) {
+ return {
+ success: false,
+ message: "한국어 이름과 영어 이름은 필수입니다",
+ data: null,
+ error: "필수 필드 누락"
+ };
+ }
+
+ // 코드가 없으면 자동 생성 (예: nameEn의 소문자화 + nanoid)
+ const code = input.code || `${input.nameEn.toLowerCase().replace(/\s+/g, '-')}-${customAlphabet('1234567890abcdef', 6)()}`;
+
+ // result 변수에 명시적으로 타입과 초기값 할당
+ let result: any[] = [];
+
+ // 트랜잭션 결과를 result에 할당
+ result = await db.transaction(async (tx) => {
+ // 기존 코드 확인 (code는 unique)
+ const existingVendorType = input.code ? await tx.query.vendorTypes.findFirst({
+ where: eq(vendorTypes.code, input.code),
+ }) : null;
+
+ let txResult;
+ if (existingVendorType) {
+ // 기존 vendorType 업데이트
+ txResult = await updateVendorType(tx, existingVendorType.id, {
+ nameKo: input.nameKo,
+ nameEn: input.nameEn,
+ });
+ } else {
+ // 새 vendorType 생성
+ txResult = await insertVendorType(tx, {
+ code,
+ nameKo: input.nameKo,
+ nameEn: input.nameEn,
+ });
+ }
+
+ return txResult;
+ });
+
+ // 캐시 무효화
+ revalidateTag("vendorTypes");
+
+ return {
+ success: true,
+ data: result[0] || 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)
+ };
+ }
+}
+
+/* -----------------------------------------------------
+ 3) 업데이트
+----------------------------------------------------- */
+
+/** 단건 업데이트 */
+export async function modifyVendorType(input: UpdateVendorTypeSchema & { id: number }) {
+ unstable_noStore();
+ try {
+ const data = await db.transaction(async (tx) => {
+ const [res] = await updateVendorType(tx, input.id, {
+ nameKo: input.nameKo,
+ nameEn: input.nameEn,
+ });
+ return res;
+ });
+
+ revalidateTag("vendorTypes");
+ return { data, error: null };
+ } catch (err) {
+ return { data: null, error: getErrorMessage(err) };
+ }
+}
+
+/** 단건 삭제 */
+export async function removeVendorType(input: { id: number }) {
+ unstable_noStore();
+ try {
+ await db.transaction(async (tx) => {
+ await deleteVendorTypeById(tx, input.id);
+ });
+
+ revalidateTag("vendorTypes");
+ return { data: null, error: null };
+ } catch (err) {
+ return { data: null, error: getErrorMessage(err) };
+ }
+}
+
+/** 복수 삭제 */
+export async function removeVendorTypes(input: { ids: number[] }) {
+ unstable_noStore();
+ try {
+ await db.transaction(async (tx) => {
+ await deleteVendorTypesByIds(tx, input.ids);
+ });
+
+ revalidateTag("vendorTypes");
+ return { data: null, error: null };
+ } catch (err) {
+ return { data: null, error: getErrorMessage(err) };
+ }
+} \ No newline at end of file