import { PrismaClient } from "@prisma/client"; import { logger } from "../utils/logger"; const prisma = new PrismaClient(); export interface CodeCategory { category_code: string; category_name: string; category_name_eng?: string | null; description?: string | null; sort_order: number; is_active: string; created_date?: Date | null; created_by?: string | null; updated_date?: Date | null; updated_by?: string | null; } export interface CodeInfo { code_category: string; code_value: string; code_name: string; code_name_eng?: string | null; description?: string | null; sort_order: number; is_active: string; created_date?: Date | null; created_by?: string | null; updated_date?: Date | null; updated_by?: string | null; } export interface GetCategoriesParams { search?: string; isActive?: boolean; page?: number; size?: number; } export interface GetCodesParams { search?: string; isActive?: boolean; } export interface CreateCategoryData { categoryCode: string; categoryName: string; categoryNameEng?: string; description?: string; sortOrder?: number; isActive?: string; } export interface CreateCodeData { codeValue: string; codeName: string; codeNameEng?: string; description?: string; sortOrder?: number; isActive?: string; } export class CommonCodeService { /** * 카테고리 목록 조회 */ async getCategories(params: GetCategoriesParams) { try { const { search, isActive, page = 1, size = 20 } = params; let whereClause: any = {}; if (search) { whereClause.OR = [ { category_name: { contains: search, mode: "insensitive" } }, { category_code: { contains: search, mode: "insensitive" } }, ]; } if (isActive !== undefined) { whereClause.is_active = isActive ? "Y" : "N"; } const offset = (page - 1) * size; const [categories, total] = await Promise.all([ prisma.code_category.findMany({ where: whereClause, orderBy: [{ sort_order: "asc" }, { category_code: "asc" }], skip: offset, take: size, }), prisma.code_category.count({ where: whereClause }), ]); logger.info( `카테고리 조회 완료: ${categories.length}개, 전체: ${total}개` ); return { data: categories, total, }; } catch (error) { logger.error("카테고리 조회 중 오류:", error); throw error; } } /** * 카테고리별 코드 목록 조회 */ async getCodes(categoryCode: string, params: GetCodesParams) { try { const { search, isActive } = params; let whereClause: any = { code_category: categoryCode, }; if (search) { whereClause.OR = [ { code_name: { contains: search, mode: "insensitive" } }, { code_value: { contains: search, mode: "insensitive" } }, ]; } if (isActive !== undefined) { whereClause.is_active = isActive ? "Y" : "N"; } const codes = await prisma.code_info.findMany({ where: whereClause, orderBy: [{ sort_order: "asc" }, { code_value: "asc" }], }); logger.info(`코드 조회 완료: ${categoryCode} - ${codes.length}개`); return codes; } catch (error) { logger.error(`코드 조회 중 오류 (${categoryCode}):`, error); throw error; } } /** * 카테고리 생성 */ async createCategory(data: CreateCategoryData, createdBy: string) { try { const category = await prisma.code_category.create({ data: { category_code: data.categoryCode, category_name: data.categoryName, category_name_eng: data.categoryNameEng, description: data.description, sort_order: data.sortOrder || 0, is_active: "Y", created_by: createdBy, updated_by: createdBy, }, }); logger.info(`카테고리 생성 완료: ${data.categoryCode}`); return category; } catch (error) { logger.error("카테고리 생성 중 오류:", error); throw error; } } /** * 카테고리 수정 */ async updateCategory( categoryCode: string, data: Partial, updatedBy: string ) { try { // 디버깅: 받은 데이터 로그 logger.info(`카테고리 수정 데이터:`, { categoryCode, data }); const category = await prisma.code_category.update({ where: { category_code: categoryCode }, data: { category_name: data.categoryName, category_name_eng: data.categoryNameEng, description: data.description, sort_order: data.sortOrder, is_active: typeof data.isActive === "boolean" ? data.isActive ? "Y" : "N" : data.isActive, // boolean이면 "Y"/"N"으로 변환 updated_by: updatedBy, updated_date: new Date(), }, }); logger.info(`카테고리 수정 완료: ${categoryCode}`); return category; } catch (error) { logger.error(`카테고리 수정 중 오류 (${categoryCode}):`, error); throw error; } } /** * 카테고리 삭제 */ async deleteCategory(categoryCode: string) { try { await prisma.code_category.delete({ where: { category_code: categoryCode }, }); logger.info(`카테고리 삭제 완료: ${categoryCode}`); } catch (error) { logger.error(`카테고리 삭제 중 오류 (${categoryCode}):`, error); throw error; } } /** * 코드 생성 */ async createCode( categoryCode: string, data: CreateCodeData, createdBy: string ) { try { const code = await prisma.code_info.create({ data: { code_category: categoryCode, code_value: data.codeValue, code_name: data.codeName, code_name_eng: data.codeNameEng, description: data.description, sort_order: data.sortOrder || 0, is_active: "Y", created_by: createdBy, updated_by: createdBy, }, }); logger.info(`코드 생성 완료: ${categoryCode}.${data.codeValue}`); return code; } catch (error) { logger.error( `코드 생성 중 오류 (${categoryCode}.${data.codeValue}):`, error ); throw error; } } /** * 코드 수정 */ async updateCode( categoryCode: string, codeValue: string, data: Partial, updatedBy: string ) { try { // 디버깅: 받은 데이터 로그 logger.info(`코드 수정 데이터:`, { categoryCode, codeValue, data }); const code = await prisma.code_info.update({ where: { code_category_code_value: { code_category: categoryCode, code_value: codeValue, }, }, data: { code_name: data.codeName, code_name_eng: data.codeNameEng, description: data.description, sort_order: data.sortOrder, is_active: typeof data.isActive === "boolean" ? data.isActive ? "Y" : "N" : data.isActive, // boolean이면 "Y"/"N"으로 변환 updated_by: updatedBy, updated_date: new Date(), }, }); logger.info(`코드 수정 완료: ${categoryCode}.${codeValue}`); return code; } catch (error) { logger.error(`코드 수정 중 오류 (${categoryCode}.${codeValue}):`, error); throw error; } } /** * 코드 삭제 */ async deleteCode(categoryCode: string, codeValue: string) { try { await prisma.code_info.delete({ where: { code_category_code_value: { code_category: categoryCode, code_value: codeValue, }, }, }); logger.info(`코드 삭제 완료: ${categoryCode}.${codeValue}`); } catch (error) { logger.error(`코드 삭제 중 오류 (${categoryCode}.${codeValue}):`, error); throw error; } } /** * 카테고리별 옵션 조회 (화면관리용) */ async getCodeOptions(categoryCode: string) { try { const codes = await prisma.code_info.findMany({ where: { code_category: categoryCode, is_active: "Y", }, select: { code_value: true, code_name: true, code_name_eng: true, sort_order: true, }, orderBy: [{ sort_order: "asc" }, { code_value: "asc" }], }); const options = codes.map((code) => ({ value: code.code_value, label: code.code_name, labelEng: code.code_name_eng, })); logger.info(`코드 옵션 조회 완료: ${categoryCode} - ${options.length}개`); return options; } catch (error) { logger.error(`코드 옵션 조회 중 오류 (${categoryCode}):`, error); throw error; } } /** * 코드 순서 변경 */ async reorderCodes( categoryCode: string, codes: Array<{ codeValue: string; sortOrder: number }>, updatedBy: string ) { try { // 먼저 존재하는 코드들을 확인 const existingCodes = await prisma.code_info.findMany({ where: { code_category: categoryCode, code_value: { in: codes.map((c) => c.codeValue) }, }, select: { code_value: true }, }); const existingCodeValues = existingCodes.map((c) => c.code_value); const validCodes = codes.filter((c) => existingCodeValues.includes(c.codeValue) ); if (validCodes.length === 0) { throw new Error( `카테고리 ${categoryCode}에 순서를 변경할 유효한 코드가 없습니다.` ); } const updatePromises = validCodes.map(({ codeValue, sortOrder }) => prisma.code_info.update({ where: { code_category_code_value: { code_category: categoryCode, code_value: codeValue, }, }, data: { sort_order: sortOrder, updated_by: updatedBy, updated_date: new Date(), }, }) ); await Promise.all(updatePromises); const skippedCodes = codes.filter( (c) => !existingCodeValues.includes(c.codeValue) ); if (skippedCodes.length > 0) { logger.warn( `코드 순서 변경 시 존재하지 않는 코드들을 건너뜀: ${skippedCodes.map((c) => c.codeValue).join(", ")}` ); } logger.info( `코드 순서 변경 완료: ${categoryCode} - ${validCodes.length}개 (전체 ${codes.length}개 중)` ); } catch (error) { logger.error(`코드 순서 변경 중 오류 (${categoryCode}):`, error); throw error; } } }