diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-04-28 02:13:30 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-04-28 02:13:30 +0000 |
| commit | ef4c533ebacc2cdc97e518f30e9a9350004fcdfb (patch) | |
| tree | 345251a3ed0f4429716fa5edaa31024d8f4cb560 /lib/vendor-type/service.ts | |
| parent | 9ceed79cf32c896f8a998399bf1b296506b2cd4a (diff) | |
~20250428 작업사항
Diffstat (limited to 'lib/vendor-type/service.ts')
| -rw-r--r-- | lib/vendor-type/service.ts | 239 |
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 |
