import { Request, Response } from "express"; import logger from "../utils/logger"; import { ExternalDbConnectionPoolService } from "../services/externalDbConnectionPoolService"; // 외부 DB 커넥터를 가져오는 헬퍼 함수 (연결 풀 사용) export async function getExternalDbConnector(connectionId: number) { const poolService = ExternalDbConnectionPoolService.getInstance(); // 연결 풀 래퍼를 반환 (executeQuery 메서드를 가진 객체) return { executeQuery: async (sql: string, params?: any[]) => { const result = await poolService.executeQuery(connectionId, sql, params); return { rows: result }; }, }; } // 동적 계층 구조 데이터 조회 (범용) export const getHierarchyData = async ( req: Request, res: Response ): Promise => { try { const { externalDbConnectionId, hierarchyConfig } = req.body; if (!externalDbConnectionId || !hierarchyConfig) { return res.status(400).json({ success: false, message: "외부 DB 연결 ID와 계층 구조 설정이 필요합니다.", }); } const connector = await getExternalDbConnector( Number(externalDbConnectionId) ); const config = JSON.parse(hierarchyConfig); const result: any = { warehouse: null, levels: [], materials: [], }; // 창고 데이터 조회 if (config.warehouse) { const warehouseQuery = `SELECT * FROM ${config.warehouse.tableName} LIMIT 100`; const warehouseResult = await connector.executeQuery(warehouseQuery); result.warehouse = warehouseResult.rows; } // 각 레벨 데이터 조회 if (config.levels && Array.isArray(config.levels)) { for (const level of config.levels) { const levelQuery = `SELECT * FROM ${level.tableName} LIMIT 1000`; const levelResult = await connector.executeQuery(levelQuery); result.levels.push({ level: level.level, name: level.name, data: levelResult.rows, }); } } // 자재 데이터 조회 (개수만) if (config.material) { const materialQuery = ` SELECT ${config.material.locationKeyColumn} as location_key, COUNT(*) as count FROM ${config.material.tableName} GROUP BY ${config.material.locationKeyColumn} `; const materialResult = await connector.executeQuery(materialQuery); result.materials = materialResult.rows; } logger.info("동적 계층 구조 데이터 조회", { externalDbConnectionId, warehouseCount: result.warehouse?.length || 0, levelCounts: result.levels.map((l: any) => ({ level: l.level, count: l.data.length, })), }); return res.json({ success: true, data: result, }); } catch (error: any) { logger.error("동적 계층 구조 데이터 조회 실패", error); return res.status(500).json({ success: false, message: "데이터 조회 중 오류가 발생했습니다.", error: error.message, }); } }; // 특정 레벨의 하위 데이터 조회 export const getChildrenData = async ( req: Request, res: Response ): Promise => { try { const { externalDbConnectionId, hierarchyConfig, parentLevel, parentKey } = req.body; if ( !externalDbConnectionId || !hierarchyConfig || !parentLevel || !parentKey ) { return res.status(400).json({ success: false, message: "필수 파라미터가 누락되었습니다.", }); } const connector = await getExternalDbConnector( Number(externalDbConnectionId) ); const config = JSON.parse(hierarchyConfig); // 다음 레벨 찾기 const nextLevel = config.levels?.find( (l: any) => l.level === parentLevel + 1 ); if (!nextLevel) { return res.json({ success: true, data: [], message: "하위 레벨이 없습니다.", }); } // 하위 데이터 조회 const query = ` SELECT * FROM ${nextLevel.tableName} WHERE ${nextLevel.parentKeyColumn} = '${parentKey}' LIMIT 1000 `; const result = await connector.executeQuery(query); logger.info("하위 데이터 조회", { externalDbConnectionId, parentLevel, parentKey, count: result.rows.length, }); return res.json({ success: true, data: result.rows, }); } catch (error: any) { logger.error("하위 데이터 조회 실패", error); return res.status(500).json({ success: false, message: "하위 데이터 조회 중 오류가 발생했습니다.", error: error.message, }); } }; // 창고 목록 조회 (사용자 지정 테이블) - 레거시, 호환성 유지 export const getWarehouses = async ( req: Request, res: Response ): Promise => { try { const { externalDbConnectionId, tableName } = req.query; if (!externalDbConnectionId) { return res.status(400).json({ success: false, message: "외부 DB 연결 ID가 필요합니다.", }); } if (!tableName) { return res.status(400).json({ success: false, message: "테이블명이 필요합니다.", }); } const connector = await getExternalDbConnector( Number(externalDbConnectionId) ); // 테이블명을 사용하여 모든 컬럼 조회 const query = `SELECT * FROM ${tableName} LIMIT 100`; const result = await connector.executeQuery(query); logger.info("창고 목록 조회", { externalDbConnectionId, tableName, count: result.rows.length, data: result.rows, // 실제 데이터 확인 }); return res.json({ success: true, data: result.rows, }); } catch (error: any) { logger.error("창고 목록 조회 실패", error); return res.status(500).json({ success: false, message: "창고 목록 조회 중 오류가 발생했습니다.", error: error.message, }); } }; // 구역 목록 조회 (사용자 지정 테이블) - 레거시, 호환성 유지 export const getAreas = async ( req: Request, res: Response ): Promise => { try { const { externalDbConnectionId, warehouseKey, tableName } = req.query; if (!externalDbConnectionId || !warehouseKey || !tableName) { return res.status(400).json({ success: false, message: "필수 파라미터가 누락되었습니다.", }); } const connector = await getExternalDbConnector( Number(externalDbConnectionId) ); const query = ` SELECT * FROM ${tableName} WHERE WAREKEY = '${warehouseKey}' LIMIT 1000 `; const result = await connector.executeQuery(query); logger.info("구역 목록 조회", { externalDbConnectionId, tableName, warehouseKey, count: result.rows.length, }); return res.json({ success: true, data: result.rows, }); } catch (error: any) { logger.error("구역 목록 조회 실패", error); return res.status(500).json({ success: false, message: "구역 목록 조회 중 오류가 발생했습니다.", error: error.message, }); } }; // 위치 목록 조회 (사용자 지정 테이블) - 레거시, 호환성 유지 export const getLocations = async ( req: Request, res: Response ): Promise => { try { const { externalDbConnectionId, areaKey, tableName } = req.query; if (!externalDbConnectionId || !areaKey || !tableName) { return res.status(400).json({ success: false, message: "필수 파라미터가 누락되었습니다.", }); } const connector = await getExternalDbConnector( Number(externalDbConnectionId) ); const query = ` SELECT * FROM ${tableName} WHERE AREAKEY = '${areaKey}' LIMIT 1000 `; const result = await connector.executeQuery(query); logger.info("위치 목록 조회", { externalDbConnectionId, tableName, areaKey, count: result.rows.length, }); return res.json({ success: true, data: result.rows, }); } catch (error: any) { logger.error("위치 목록 조회 실패", error); return res.status(500).json({ success: false, message: "위치 목록 조회 중 오류가 발생했습니다.", error: error.message, }); } }; // 자재 목록 조회 (동적 컬럼 매핑 지원) export const getMaterials = async ( req: Request, res: Response ): Promise => { try { const { externalDbConnectionId, locaKey, tableName, keyColumn, locationKeyColumn, layerColumn, } = req.query; if ( !externalDbConnectionId || !locaKey || !tableName || !locationKeyColumn ) { return res.status(400).json({ success: false, message: "필수 파라미터가 누락되었습니다.", }); } const connector = await getExternalDbConnector( Number(externalDbConnectionId) ); // 동적 쿼리 생성 const orderByClause = layerColumn ? `ORDER BY ${layerColumn}` : ""; const query = ` SELECT * FROM ${tableName} WHERE ${locationKeyColumn} = '${locaKey}' ${orderByClause} LIMIT 1000 `; logger.info(`자재 조회 쿼리: ${query}`); const result = await connector.executeQuery(query); logger.info("자재 목록 조회", { externalDbConnectionId, tableName, locaKey, count: result.rows.length, }); return res.json({ success: true, data: result.rows, }); } catch (error: any) { logger.error("자재 목록 조회 실패", error); return res.status(500).json({ success: false, message: "자재 목록 조회 중 오류가 발생했습니다.", error: error.message, }); } }; // 자재 개수 조회 (여러 Location 일괄) - 레거시, 호환성 유지 export const getMaterialCounts = async ( req: Request, res: Response ): Promise => { try { const { externalDbConnectionId, locationKeys, tableName } = req.body; if (!externalDbConnectionId || !locationKeys || !tableName) { return res.status(400).json({ success: false, message: "필수 파라미터가 누락되었습니다.", }); } const connector = await getExternalDbConnector( Number(externalDbConnectionId) ); const keysString = locationKeys.map((key: string) => `'${key}'`).join(","); const query = ` SELECT LOCAKEY as location_key, COUNT(*) as count FROM ${tableName} WHERE LOCAKEY IN (${keysString}) GROUP BY LOCAKEY `; const result = await connector.executeQuery(query); logger.info("자재 개수 조회", { externalDbConnectionId, tableName, locationCount: locationKeys.length, }); return res.json({ success: true, data: result.rows, }); } catch (error: any) { logger.error("자재 개수 조회 실패", error); return res.status(500).json({ success: false, message: "자재 개수 조회 중 오류가 발생했습니다.", error: error.message, }); } };