434 lines
11 KiB
TypeScript
434 lines
11 KiB
TypeScript
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<Response> => {
|
|
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<Response> => {
|
|
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<Response> => {
|
|
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<Response> => {
|
|
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<Response> => {
|
|
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<Response> => {
|
|
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<Response> => {
|
|
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,
|
|
});
|
|
}
|
|
};
|