import { Request, Response } from "express"; import { AuthenticatedRequest } from "../types/auth"; import { getDataflowDiagrams as getDataflowDiagramsService, getDataflowDiagramById as getDataflowDiagramByIdService, createDataflowDiagram as createDataflowDiagramService, updateDataflowDiagram as updateDataflowDiagramService, deleteDataflowDiagram as deleteDataflowDiagramService, copyDataflowDiagram as copyDataflowDiagramService, } from "../services/dataflowDiagramService"; import { logger } from "../utils/logger"; /** * 관계도 목록 조회 (페이지네이션) */ export const getDataflowDiagrams = async ( req: AuthenticatedRequest, res: Response ) => { try { const page = parseInt(req.query.page as string) || 1; const size = parseInt(req.query.size as string) || 20; const searchTerm = req.query.searchTerm as string; const userCompanyCode = req.user?.companyCode; // 슈퍼 관리자는 쿼리 파라미터로 회사 지정 가능, 일반/회사 관리자는 자신의 회사만 let companyCode: string; if (userCompanyCode === "*") { // 슈퍼 관리자: 쿼리 파라미터 사용 또는 전체 companyCode = (req.query.companyCode as string) || "*"; } else { // 회사 관리자/일반 사용자: 강제로 자신의 회사 코드 적용 companyCode = userCompanyCode || "*"; } logger.info("관계도 목록 조회", { userId: req.user?.userId, userCompanyCode, filterCompanyCode: companyCode, page, size, }); const result = await getDataflowDiagramsService( companyCode, page, size, searchTerm ); res.json({ success: true, data: result, }); } catch (error) { logger.error("관계도 목록 조회 실패:", error); res.status(500).json({ success: false, message: "관계도 목록 조회 중 오류가 발생했습니다.", error: error instanceof Error ? error.message : "Unknown error", }); } }; /** * 특정 관계도 조회 */ export const getDataflowDiagramById = async ( req: AuthenticatedRequest, res: Response ) => { try { const diagramId = parseInt(req.params.diagramId); const userCompanyCode = req.user?.companyCode; // 슈퍼 관리자는 쿼리 파라미터로 회사 지정 가능, 일반/회사 관리자는 자신의 회사만 let companyCode: string; if (userCompanyCode === "*") { companyCode = (req.query.companyCode as string) || "*"; } else { companyCode = userCompanyCode || "*"; } if (isNaN(diagramId)) { return res.status(400).json({ success: false, message: "유효하지 않은 관계도 ID입니다.", }); } const diagram = await getDataflowDiagramByIdService(diagramId, companyCode); if (!diagram) { return res.status(404).json({ success: false, message: "관계도를 찾을 수 없습니다.", }); } return res.json({ success: true, data: diagram, }); } catch (error) { logger.error("관계도 조회 실패:", error); return res.status(500).json({ success: false, message: "관계도 조회 중 오류가 발생했습니다.", error: error instanceof Error ? error.message : "Unknown error", }); } }; /** * 새로운 관계도 생성 */ export const createDataflowDiagram = async ( req: AuthenticatedRequest, res: Response ) => { try { const { diagram_name, relationships, node_positions, category, control, plan, } = req.body; const userCompanyCode = req.user?.companyCode; const userId = req.user?.userId || "SYSTEM"; // 회사 코드는 로그인한 사용자의 회사 코드 사용 (슈퍼 관리자는 요청 body에서 지정 가능) let companyCode: string; if (userCompanyCode === "*" && req.body.company_code) { // 슈퍼 관리자가 특정 회사로 생성하는 경우 companyCode = req.body.company_code; } else { // 일반 사용자/회사 관리자는 자신의 회사로 생성 companyCode = userCompanyCode || "*"; } logger.info(`새 관계도 생성 요청:`, { diagram_name, companyCode, userId, userCompanyCode, }); logger.info(`node_positions:`, node_positions); logger.info(`category:`, category); logger.info(`control:`, control); logger.info(`plan:`, plan); if (!diagram_name || !relationships) { return res.status(400).json({ success: false, message: "관계도 이름과 관계 정보는 필수입니다.", }); } const newDiagram = await createDataflowDiagramService({ diagram_name, relationships, node_positions, category, control, plan, company_code: companyCode, created_by: userId, updated_by: userId, }); return res.status(201).json({ success: true, data: newDiagram, message: "관계도가 성공적으로 생성되었습니다.", }); } catch (error) { // 디버깅을 위한 에러 정보 출력 logger.error("에러 디버깅:", { errorType: typeof error, errorCode: (error as any)?.code, errorMessage: error instanceof Error ? error.message : "Unknown error", errorName: (error as any)?.name, errorMeta: (error as any)?.meta, }); // 중복 이름 에러인지 먼저 확인 (로그 출력 전에) const isDuplicateError = (error && typeof error === "object" && (error as any).code === "23505") || // PostgreSQL unique_violation (error instanceof Error && (error.message.includes("unique constraint") || error.message.includes("Unique constraint") || error.message.includes("duplicate key") || error.message.includes("UNIQUE constraint failed") || error.message.includes("unique_diagram_name_per_company"))); if (isDuplicateError) { // 중복 에러는 콘솔에 로그 출력하지 않음 return res.status(409).json({ success: false, message: "중복된 이름입니다.", }); } // 다른 에러만 로그 출력 logger.error("관계도 생성 실패:", error); return res.status(500).json({ success: false, message: "관계도 생성 중 오류가 발생했습니다.", error: error instanceof Error ? error.message : "Unknown error", }); } }; /** * 관계도 수정 */ export const updateDataflowDiagram = async ( req: AuthenticatedRequest, res: Response ) => { try { const diagramId = parseInt(req.params.diagramId); const userCompanyCode = req.user?.companyCode; const userId = req.user?.userId || "SYSTEM"; // 슈퍼 관리자는 쿼리 파라미터로 회사 지정 가능, 일반/회사 관리자는 자신의 회사만 let companyCode: string; if (userCompanyCode === "*") { companyCode = (req.query.companyCode as string) || "*"; } else { companyCode = userCompanyCode || "*"; } logger.info(`관계도 수정 요청`, { diagramId, companyCode, userId, userCompanyCode, }); logger.info(`요청 Body:`, JSON.stringify(req.body, null, 2)); logger.info(`node_positions:`, req.body.node_positions); if (isNaN(diagramId)) { return res.status(400).json({ success: false, message: "유효하지 않은 관계도 ID입니다.", }); } const updateData = { ...req.body, updated_by: userId, }; const updatedDiagram = await updateDataflowDiagramService( diagramId, updateData, companyCode ); if (!updatedDiagram) { return res.status(404).json({ success: false, message: "관계도를 찾을 수 없습니다.", }); } return res.json({ success: true, data: updatedDiagram, message: "관계도가 성공적으로 수정되었습니다.", }); } catch (error) { // 중복 이름 에러인지 먼저 확인 (로그 출력 전에) const isDuplicateError = (error && typeof error === "object" && (error as any).code === "23505") || // PostgreSQL unique_violation (error instanceof Error && (error.message.includes("unique constraint") || error.message.includes("Unique constraint") || error.message.includes("duplicate key") || error.message.includes("UNIQUE constraint failed") || error.message.includes("unique_diagram_name_per_company"))); if (isDuplicateError) { // 중복 에러는 콘솔에 로그 출력하지 않음 return res.status(409).json({ success: false, message: "중복된 이름입니다.", }); } // 다른 에러만 로그 출력 logger.error("관계도 수정 실패:", error); return res.status(500).json({ success: false, message: "관계도 수정 중 오류가 발생했습니다.", error: error instanceof Error ? error.message : "Unknown error", }); } }; /** * 관계도 삭제 */ export const deleteDataflowDiagram = async ( req: AuthenticatedRequest, res: Response ) => { try { const diagramId = parseInt(req.params.diagramId); const userCompanyCode = req.user?.companyCode; // 슈퍼 관리자는 쿼리 파라미터로 회사 지정 가능, 일반/회사 관리자는 자신의 회사만 let companyCode: string; if (userCompanyCode === "*") { companyCode = (req.query.companyCode as string) || "*"; } else { companyCode = userCompanyCode || "*"; } if (isNaN(diagramId)) { return res.status(400).json({ success: false, message: "유효하지 않은 관계도 ID입니다.", }); } const deleted = await deleteDataflowDiagramService(diagramId, companyCode); if (!deleted) { return res.status(404).json({ success: false, message: "관계도를 찾을 수 없습니다.", }); } return res.json({ success: true, message: "관계도가 성공적으로 삭제되었습니다.", }); } catch (error) { logger.error("관계도 삭제 실패:", error); return res.status(500).json({ success: false, message: "관계도 삭제 중 오류가 발생했습니다.", error: error instanceof Error ? error.message : "Unknown error", }); } }; /** * 관계도 복제 */ export const copyDataflowDiagram = async ( req: AuthenticatedRequest, res: Response ) => { try { const diagramId = parseInt(req.params.diagramId); const { new_name } = req.body; const userCompanyCode = req.user?.companyCode; const userId = req.user?.userId || "SYSTEM"; // 회사 코드는 로그인한 사용자의 회사 코드 사용 let companyCode: string; if (userCompanyCode === "*" && req.body.companyCode) { // 슈퍼 관리자가 특정 회사로 복제하는 경우 companyCode = req.body.companyCode; } else { // 일반 사용자/회사 관리자는 자신의 회사로 복제 companyCode = userCompanyCode || "*"; } if (isNaN(diagramId)) { return res.status(400).json({ success: false, message: "유효하지 않은 관계도 ID입니다.", }); } const copiedDiagram = await copyDataflowDiagramService( diagramId, companyCode, new_name, userId ); if (!copiedDiagram) { return res.status(404).json({ success: false, message: "복제할 관계도를 찾을 수 없습니다.", }); } return res.status(201).json({ success: true, data: copiedDiagram, message: "관계도가 성공적으로 복제되었습니다.", }); } catch (error) { logger.error("관계도 복제 실패:", error); return res.status(500).json({ success: false, message: "관계도 복제 중 오류가 발생했습니다.", error: error instanceof Error ? error.message : "Unknown error", }); } };