import { Prisma } from "@prisma/client"; import prisma from "../config/database"; import { logger } from "../utils/logger"; // 타입 정의 interface CreateDataflowDiagramData { diagram_name: string; relationships: Record; // JSON 데이터 node_positions?: Record | null; // JSON 데이터 (노드 위치 정보) // 🔥 수정: 배열 구조로 변경된 조건부 연결 관련 필드 control?: Array> | null; // JSON 배열 (각 관계별 조건 설정) category?: Array> | null; // JSON 배열 (각 관계별 연결 종류) plan?: Array> | null; // JSON 배열 (각 관계별 실행 계획) company_code: string; created_by: string; updated_by: string; } interface UpdateDataflowDiagramData { diagram_name?: string; relationships?: Record; // JSON 데이터 node_positions?: Record | null; // JSON 데이터 (노드 위치 정보) // 🔥 수정: 배열 구조로 변경된 조건부 연결 관련 필드 control?: Array> | null; // JSON 배열 (각 관계별 조건 설정) category?: Array> | null; // JSON 배열 (각 관계별 연결 종류) plan?: Array> | null; // JSON 배열 (각 관계별 실행 계획) updated_by: string; } /** * 관계도 목록 조회 (페이지네이션) */ export const getDataflowDiagrams = async ( companyCode: string, page: number = 1, size: number = 20, searchTerm?: string ) => { try { const offset = (page - 1) * size; // 검색 조건 구성 const whereClause: { company_code?: string; diagram_name?: { contains: string; mode: "insensitive"; }; } = {}; // company_code가 '*'가 아닌 경우에만 필터링 if (companyCode !== "*") { whereClause.company_code = companyCode; } if (searchTerm) { whereClause.diagram_name = { contains: searchTerm, mode: "insensitive", }; } // 총 개수 조회 const total = await prisma.dataflow_diagrams.count({ where: whereClause, }); // 데이터 조회 const diagrams = await prisma.dataflow_diagrams.findMany({ where: whereClause, orderBy: { updated_at: "desc", }, skip: offset, take: size, }); const totalPages = Math.ceil(total / size); return { diagrams, pagination: { page, size, total, totalPages, }, }; } catch (error) { logger.error("관계도 목록 조회 서비스 오류:", error); throw error; } }; /** * 특정 관계도 조회 */ export const getDataflowDiagramById = async ( diagramId: number, companyCode: string ) => { try { const whereClause: { diagram_id: number; company_code?: string; } = { diagram_id: diagramId, }; // company_code가 '*'가 아닌 경우에만 필터링 if (companyCode !== "*") { whereClause.company_code = companyCode; } const diagram = await prisma.dataflow_diagrams.findFirst({ where: whereClause, }); return diagram; } catch (error) { logger.error("관계도 조회 서비스 오류:", error); throw error; } }; /** * 새로운 관계도 생성 */ export const createDataflowDiagram = async ( data: CreateDataflowDiagramData ) => { try { const newDiagram = await prisma.dataflow_diagrams.create({ data: { diagram_name: data.diagram_name, relationships: data.relationships as Prisma.InputJsonValue, node_positions: data.node_positions as | Prisma.InputJsonValue | undefined, category: data.category ? (data.category as Prisma.InputJsonValue) : undefined, control: data.control as Prisma.InputJsonValue | undefined, plan: data.plan as Prisma.InputJsonValue | undefined, company_code: data.company_code, created_by: data.created_by, updated_by: data.updated_by, }, }); return newDiagram; } catch (error) { logger.error("관계도 생성 서비스 오류:", error); throw error; } }; /** * 관계도 수정 */ export const updateDataflowDiagram = async ( diagramId: number, data: UpdateDataflowDiagramData, companyCode: string ) => { try { logger.info( `관계도 수정 서비스 시작 - ID: ${diagramId}, Company: ${companyCode}` ); // 먼저 해당 관계도가 존재하는지 확인 const whereClause: { diagram_id: number; company_code?: string; } = { diagram_id: diagramId, }; // company_code가 '*'가 아닌 경우에만 필터링 if (companyCode !== "*") { whereClause.company_code = companyCode; } const existingDiagram = await prisma.dataflow_diagrams.findFirst({ where: whereClause, }); logger.info( `기존 관계도 조회 결과:`, existingDiagram ? `ID ${existingDiagram.diagram_id} 발견` : "관계도 없음" ); if (!existingDiagram) { logger.warn( `관계도 ID ${diagramId}를 찾을 수 없음 - Company: ${companyCode}` ); return null; } // 업데이트 실행 const updatedDiagram = await prisma.dataflow_diagrams.update({ where: { diagram_id: diagramId, }, data: { ...(data.diagram_name && { diagram_name: data.diagram_name }), ...(data.relationships && { relationships: data.relationships as Prisma.InputJsonValue, }), ...(data.node_positions !== undefined && { node_positions: data.node_positions ? (data.node_positions as Prisma.InputJsonValue) : Prisma.JsonNull, }), ...(data.category !== undefined && { category: data.category ? (data.category as Prisma.InputJsonValue) : undefined, }), ...(data.control !== undefined && { control: data.control as Prisma.InputJsonValue | undefined, }), ...(data.plan !== undefined && { plan: data.plan as Prisma.InputJsonValue | undefined, }), updated_by: data.updated_by, updated_at: new Date(), }, }); return updatedDiagram; } catch (error) { logger.error("관계도 수정 서비스 오류:", error); throw error; } }; /** * 관계도 삭제 */ export const deleteDataflowDiagram = async ( diagramId: number, companyCode: string ) => { try { // 먼저 해당 관계도가 존재하는지 확인 const whereClause: { diagram_id: number; company_code?: string; } = { diagram_id: diagramId, }; // company_code가 '*'가 아닌 경우에만 필터링 if (companyCode !== "*") { whereClause.company_code = companyCode; } const existingDiagram = await prisma.dataflow_diagrams.findFirst({ where: whereClause, }); if (!existingDiagram) { return false; } // 삭제 실행 await prisma.dataflow_diagrams.delete({ where: { diagram_id: diagramId, }, }); return true; } catch (error) { logger.error("관계도 삭제 서비스 오류:", error); throw error; } }; /** * 관계도 복제 */ export const copyDataflowDiagram = async ( diagramId: number, companyCode: string, newName?: string, userId: string = "SYSTEM" ) => { try { // 원본 관계도 조회 const whereClause: { diagram_id: number; company_code?: string; } = { diagram_id: diagramId, }; // company_code가 '*'가 아닌 경우에만 필터링 if (companyCode !== "*") { whereClause.company_code = companyCode; } const originalDiagram = await prisma.dataflow_diagrams.findFirst({ where: whereClause, }); if (!originalDiagram) { return null; } // 새로운 이름 생성 (제공되지 않은 경우) let copyName = newName; if (!copyName) { // 기존 이름에서 (n) 패턴을 찾아서 증가 const baseNameMatch = originalDiagram.diagram_name.match( /^(.+?)(\s*\((\d+)\))?$/ ); const baseName = baseNameMatch ? baseNameMatch[1] : originalDiagram.diagram_name; // 같은 패턴의 이름들을 찾아서 가장 큰 번호 찾기 const copyWhereClause: { diagram_name: { startsWith: string; }; company_code?: string; } = { diagram_name: { startsWith: baseName, }, }; // company_code가 '*'가 아닌 경우에만 필터링 if (companyCode !== "*") { copyWhereClause.company_code = companyCode; } const existingCopies = await prisma.dataflow_diagrams.findMany({ where: copyWhereClause, select: { diagram_name: true, }, }); let maxNumber = 0; existingCopies.forEach((copy) => { const match = copy.diagram_name.match(/\((\d+)\)$/); if (match) { const num = parseInt(match[1]); if (num > maxNumber) { maxNumber = num; } } }); copyName = `${baseName} (${maxNumber + 1})`; } // 새로운 관계도 생성 const copiedDiagram = await prisma.dataflow_diagrams.create({ data: { diagram_name: copyName, relationships: originalDiagram.relationships as Prisma.InputJsonValue, node_positions: originalDiagram.node_positions ? (originalDiagram.node_positions as Prisma.InputJsonValue) : Prisma.JsonNull, category: originalDiagram.category || undefined, company_code: companyCode, created_by: userId, updated_by: userId, }, }); return copiedDiagram; } catch (error) { logger.error("관계도 복제 서비스 오류:", error); throw error; } }; /** * 🔥 전체 관계 목록 조회 (버튼 제어용) * dataflow_diagrams 테이블에서 관계도 데이터를 조회 (데이터 흐름 관계 화면과 동일) */ export const getAllRelationshipsForButtonControl = async ( companyCode: string ): Promise> => { try { logger.info(`전체 관계 목록 조회 시작 - companyCode: ${companyCode}`); // dataflow_diagrams 테이블에서 관계도들을 조회 const diagrams = await prisma.dataflow_diagrams.findMany({ where: { company_code: companyCode, }, select: { diagram_id: true, diagram_name: true, relationships: true, }, orderBy: { updated_at: "desc", }, }); const allRelationships = diagrams.map((diagram) => { // relationships 구조에서 테이블 정보 추출 const relationships = diagram.relationships as any || {}; // 테이블 정보 추출 let sourceTable = ""; let targetTable = ""; if (relationships.fromTable?.tableName) { sourceTable = relationships.fromTable.tableName; } if (relationships.toTable?.tableName) { targetTable = relationships.toTable.tableName; } return { id: diagram.diagram_id.toString(), name: diagram.diagram_name || `관계 ${diagram.diagram_id}`, sourceTable: sourceTable, targetTable: targetTable, category: "데이터 흐름", }; }); logger.info(`전체 관계 ${allRelationships.length}개 조회 완료`); return allRelationships; } catch (error) { logger.error("전체 관계 목록 조회 서비스 오류:", error); throw error; } };