import { PrismaClient } from "@prisma/client"; const prisma = new PrismaClient(); export interface ComponentStandardData { component_code: string; component_name: string; component_name_eng?: string; description?: string; category: string; icon_name?: string; default_size?: any; component_config: any; preview_image?: string; sort_order?: number; is_active?: string; is_public?: string; company_code: string; created_by?: string; updated_by?: string; } export interface ComponentQueryParams { category?: string; active?: string; is_public?: string; company_code?: string; search?: string; sort?: string; order?: "asc" | "desc"; limit?: number; offset?: number; } class ComponentStandardService { /** * 컴포넌트 목록 조회 */ async getComponents(params: ComponentQueryParams = {}) { const { category, active = "Y", is_public, company_code, search, sort = "sort_order", order = "asc", limit, offset = 0, } = params; const where: any = {}; // 활성화 상태 필터 if (active) { where.is_active = active; } // 카테고리 필터 if (category && category !== "all") { where.category = category; } // 공개 여부 필터 if (is_public) { where.is_public = is_public; } // 회사별 필터 (공개 컴포넌트 + 해당 회사 컴포넌트) if (company_code) { where.OR = [{ is_public: "Y" }, { company_code }]; } // 검색 조건 if (search) { where.OR = [ ...(where.OR || []), { component_name: { contains: search, mode: "insensitive" } }, { component_name_eng: { contains: search, mode: "insensitive" } }, { description: { contains: search, mode: "insensitive" } }, ]; } const orderBy: any = {}; orderBy[sort] = order; const components = await prisma.component_standards.findMany({ where, orderBy, take: limit, skip: offset, }); const total = await prisma.component_standards.count({ where }); return { components, total, limit, offset, }; } /** * 컴포넌트 상세 조회 */ async getComponent(component_code: string) { const component = await prisma.component_standards.findUnique({ where: { component_code }, }); if (!component) { throw new Error(`컴포넌트를 찾을 수 없습니다: ${component_code}`); } return component; } /** * 컴포넌트 생성 */ async createComponent(data: ComponentStandardData) { // 중복 코드 확인 const existing = await prisma.component_standards.findUnique({ where: { component_code: data.component_code }, }); if (existing) { throw new Error( `이미 존재하는 컴포넌트 코드입니다: ${data.component_code}` ); } const component = await prisma.component_standards.create({ data: { ...data, created_date: new Date(), updated_date: new Date(), }, }); return component; } /** * 컴포넌트 수정 */ async updateComponent( component_code: string, data: Partial ) { const existing = await this.getComponent(component_code); const component = await prisma.component_standards.update({ where: { component_code }, data: { ...data, updated_date: new Date(), }, }); return component; } /** * 컴포넌트 삭제 */ async deleteComponent(component_code: string) { const existing = await this.getComponent(component_code); await prisma.component_standards.delete({ where: { component_code }, }); return { message: `컴포넌트가 삭제되었습니다: ${component_code}` }; } /** * 컴포넌트 정렬 순서 업데이트 */ async updateSortOrder( updates: Array<{ component_code: string; sort_order: number }> ) { const transactions = updates.map(({ component_code, sort_order }) => prisma.component_standards.update({ where: { component_code }, data: { sort_order, updated_date: new Date() }, }) ); await prisma.$transaction(transactions); return { message: "정렬 순서가 업데이트되었습니다." }; } /** * 컴포넌트 복제 */ async duplicateComponent( source_code: string, new_code: string, new_name: string ) { const source = await this.getComponent(source_code); // 새 코드 중복 확인 const existing = await prisma.component_standards.findUnique({ where: { component_code: new_code }, }); if (existing) { throw new Error(`이미 존재하는 컴포넌트 코드입니다: ${new_code}`); } const component = await prisma.component_standards.create({ data: { component_code: new_code, component_name: new_name, component_name_eng: source.component_name_eng, description: source.description, category: source.category, icon_name: source.icon_name, default_size: source.default_size as any, component_config: source.component_config as any, preview_image: source.preview_image, sort_order: source.sort_order, is_active: source.is_active, is_public: source.is_public, company_code: source.company_code, created_date: new Date(), created_by: source.created_by, updated_date: new Date(), updated_by: source.updated_by, }, }); return component; } /** * 카테고리 목록 조회 */ async getCategories(company_code?: string) { const where: any = { is_active: "Y", }; if (company_code) { where.OR = [{ is_public: "Y" }, { company_code }]; } const categories = await prisma.component_standards.findMany({ where, select: { category: true }, distinct: ["category"], }); return categories .map((item) => item.category) .filter((category) => category !== null); } /** * 컴포넌트 통계 */ async getStatistics(company_code?: string) { const where: any = { is_active: "Y", }; if (company_code) { where.OR = [{ is_public: "Y" }, { company_code }]; } const total = await prisma.component_standards.count({ where }); const byCategory = await prisma.component_standards.groupBy({ by: ["category"], where, _count: { category: true }, }); const byStatus = await prisma.component_standards.groupBy({ by: ["is_active"], _count: { is_active: true }, }); return { total, byCategory: byCategory.map((item) => ({ category: item.category, count: item._count.category, })), byStatus: byStatus.map((item) => ({ status: item.is_active, count: item._count.is_active, })), }; } } export default new ComponentStandardService();