Merge branch 'feature/v2-renewal' of http://39.117.244.52:3000/kjs/ERP-node into feature/v2-unified-renewal

This commit is contained in:
kjs
2026-01-05 18:23:14 +09:00
10 changed files with 2012 additions and 158 deletions

View File

@@ -39,11 +39,25 @@ export const getScreenGroups = async (req: Request, res: Response) => {
const countResult = await pool.query(countQuery, params);
const total = parseInt(countResult.rows[0].total);
// 데이터 조회
// 데이터 조회 (screens 배열 포함)
const dataQuery = `
SELECT
sg.*,
(SELECT COUNT(*) FROM screen_group_screens sgs WHERE sgs.group_id = sg.id) as screen_count
(SELECT COUNT(*) FROM screen_group_screens sgs WHERE sgs.group_id = sg.id) as screen_count,
(SELECT json_agg(
json_build_object(
'id', sgs.id,
'screen_id', sgs.screen_id,
'screen_name', sd.screen_name,
'screen_role', sgs.screen_role,
'display_order', sgs.display_order,
'is_default', sgs.is_default,
'table_name', sd.table_name
) ORDER BY sgs.display_order
) FROM screen_group_screens sgs
LEFT JOIN screen_definitions sd ON sgs.screen_id = sd.screen_id
WHERE sgs.group_id = sg.id
) as screens
FROM screen_groups sg
${whereClause}
ORDER BY sg.display_order ASC, sg.created_date DESC
@@ -84,7 +98,8 @@ export const getScreenGroup = async (req: Request, res: Response) => {
'screen_name', sd.screen_name,
'screen_role', sgs.screen_role,
'display_order', sgs.display_order,
'is_default', sgs.is_default
'is_default', sgs.is_default,
'table_name', sd.table_name
) ORDER BY sgs.display_order
) FROM screen_group_screens sgs
LEFT JOIN screen_definitions sd ON sgs.screen_id = sd.screen_id
@@ -981,3 +996,109 @@ export const getMultipleScreenLayoutSummary = async (req: Request, res: Response
}
};
// ============================================================
// 화면 서브 테이블 관계 조회 (조인/참조 테이블)
// ============================================================
// 여러 화면의 서브 테이블 정보 조회 (메인 테이블 → 서브 테이블 관계)
export const getScreenSubTables = async (req: Request, res: Response) => {
try {
const { screenIds } = req.body;
if (!screenIds || !Array.isArray(screenIds) || screenIds.length === 0) {
return res.status(400).json({ success: false, message: "screenIds 배열이 필요합니다." });
}
// 화면별 메인 테이블과 서브 테이블 관계 조회
// componentConfig에서 tableName, sourceTable 추출
const query = `
SELECT DISTINCT
sd.screen_id,
sd.screen_name,
sd.table_name as main_table,
COALESCE(
sl.properties->'componentConfig'->>'tableName',
sl.properties->'componentConfig'->>'sourceTable'
) as sub_table,
sl.properties->>'componentType' as component_type,
sl.properties->'componentConfig'->>'targetTable' as target_table
FROM screen_definitions sd
JOIN screen_layouts sl ON sd.screen_id = sl.screen_id
WHERE sd.screen_id = ANY($1)
AND (
sl.properties->'componentConfig'->>'tableName' IS NOT NULL
OR sl.properties->'componentConfig'->>'sourceTable' IS NOT NULL
)
ORDER BY sd.screen_id
`;
const result = await pool.query(query, [screenIds]);
// 화면별 서브 테이블 그룹화
const screenSubTables: Record<number, {
screenId: number;
screenName: string;
mainTable: string;
subTables: Array<{
tableName: string;
componentType: string;
relationType: string; // 'join' | 'lookup' | 'source'
}>;
}> = {};
result.rows.forEach((row: any) => {
const screenId = row.screen_id;
const mainTable = row.main_table;
const subTable = row.sub_table;
// 메인 테이블과 동일한 경우 제외
if (!subTable || subTable === mainTable) {
return;
}
if (!screenSubTables[screenId]) {
screenSubTables[screenId] = {
screenId,
screenName: row.screen_name,
mainTable: mainTable || '',
subTables: [],
};
}
// 중복 체크
const exists = screenSubTables[screenId].subTables.some(
(st) => st.tableName === subTable
);
if (!exists) {
// 관계 타입 추론
let relationType = 'lookup';
const componentType = row.component_type || '';
if (componentType.includes('autocomplete') || componentType.includes('entity-search')) {
relationType = 'lookup';
} else if (componentType.includes('modal-repeater') || componentType.includes('selected-items')) {
relationType = 'source';
} else if (componentType.includes('table')) {
relationType = 'join';
}
screenSubTables[screenId].subTables.push({
tableName: subTable,
componentType: componentType,
relationType: relationType,
});
}
});
logger.info("화면 서브 테이블 정보 조회", { screenIds, resultCount: Object.keys(screenSubTables).length });
res.json({
success: true,
data: screenSubTables,
});
} catch (error: any) {
logger.error("화면 서브 테이블 정보 조회 실패:", error);
res.status(500).json({ success: false, message: "화면 서브 테이블 정보 조회에 실패했습니다.", error: error.message });
}
};

View File

@@ -29,6 +29,8 @@ import {
// 화면 레이아웃 요약
getScreenLayoutSummary,
getMultipleScreenLayoutSummary,
// 화면 서브 테이블 관계
getScreenSubTables,
} from "../controllers/screenGroupController";
const router = Router();
@@ -82,6 +84,11 @@ router.delete("/table-relations/:id", deleteTableRelation);
router.get("/layout-summary/:screenId", getScreenLayoutSummary);
router.post("/layout-summary/batch", getMultipleScreenLayoutSummary);
// ============================================================
// 화면 서브 테이블 관계 (조인/참조 테이블)
// ============================================================
router.post("/sub-tables/batch", getScreenSubTables);
export default router;