feat: 서브 테이블 조인 및 필터 관계 정보 개선
- rightPanel.columns에서 참조하는 외부 테이블 및 조인 컬럼 정보를 수집하는 로직 추가 - 서브 테이블의 조인 컬럼 참조 정보를 포함하여 시각화 개선 - 필터 관계를 선 없이 뱃지로 표시하여 겹침 방지 및 시각적 표현 개선 - TableNodeData 인터페이스에 조인 컬럼 및 참조 정보 필드 추가 - 화면 관계 흐름에서 조인 컬럼 및 필터 관계 정보 표시 기능 개선
This commit is contained in:
@@ -1607,7 +1607,8 @@ export const getScreenSubTables = async (req: Request, res: Response) => {
|
||||
sd.table_name as main_table,
|
||||
sl.properties->>'componentType' as component_type,
|
||||
sl.properties->'componentConfig'->'rightPanel'->'relation' as right_panel_relation,
|
||||
sl.properties->'componentConfig'->'rightPanel'->'tableName' as right_panel_table
|
||||
sl.properties->'componentConfig'->'rightPanel'->'tableName' as right_panel_table,
|
||||
sl.properties->'componentConfig'->'rightPanel'->'columns' as right_panel_columns
|
||||
FROM screen_definitions sd
|
||||
JOIN screen_layouts sl ON sd.screen_id = sl.screen_id
|
||||
WHERE sd.screen_id = ANY($1)
|
||||
@@ -1616,6 +1617,29 @@ export const getScreenSubTables = async (req: Request, res: Response) => {
|
||||
|
||||
const rightPanelResult = await pool.query(rightPanelQuery, [screenIds]);
|
||||
|
||||
// rightPanel.columns에서 참조되는 외부 테이블 수집 (예: customer_mng.customer_name → customer_mng)
|
||||
const rightPanelJoinedTables: Map<string, Set<string>> = new Map(); // screenId_tableName → Set<참조테이블>
|
||||
|
||||
rightPanelResult.rows.forEach((row: any) => {
|
||||
const screenId = row.screen_id;
|
||||
const rightPanelTable = row.right_panel_table;
|
||||
const rightPanelColumns = row.right_panel_columns;
|
||||
|
||||
if (rightPanelColumns && Array.isArray(rightPanelColumns)) {
|
||||
rightPanelColumns.forEach((col: any) => {
|
||||
const colName = col.name || col.columnName || col.field;
|
||||
if (colName && colName.includes('.')) {
|
||||
const refTable = colName.split('.')[0];
|
||||
const key = `${screenId}_${rightPanelTable}`;
|
||||
if (!rightPanelJoinedTables.has(key)) {
|
||||
rightPanelJoinedTables.set(key, new Set());
|
||||
}
|
||||
rightPanelJoinedTables.get(key)!.add(refTable);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
rightPanelResult.rows.forEach((row: any) => {
|
||||
const screenId = row.screen_id;
|
||||
const mainTable = row.main_table;
|
||||
@@ -1626,6 +1650,10 @@ export const getScreenSubTables = async (req: Request, res: Response) => {
|
||||
// relation 객체에서 테이블 및 필드 매핑 추출
|
||||
const subTable = rightPanelTable || relation?.targetTable || relation?.tableName;
|
||||
if (!subTable || subTable === mainTable) return;
|
||||
|
||||
// rightPanel.columns에서 참조하는 외부 테이블 목록
|
||||
const key = `${screenId}_${subTable}`;
|
||||
const joinedTables = rightPanelJoinedTables.get(key) ? Array.from(rightPanelJoinedTables.get(key)!) : [];
|
||||
|
||||
if (!screenSubTables[screenId]) {
|
||||
screenSubTables[screenId] = {
|
||||
@@ -1697,6 +1725,7 @@ export const getScreenSubTables = async (req: Request, res: Response) => {
|
||||
originalRelationType: relation?.type || 'join', // 원본 relation.type ("join" | "detail")
|
||||
foreignKey: relation?.foreignKey, // 디테일 테이블의 FK 컬럼
|
||||
leftColumn: relation?.leftColumn, // 마스터 테이블의 선택 기준 컬럼
|
||||
joinedTables: joinedTables.length > 0 ? joinedTables : undefined, // rightPanel.columns에서 참조하는 외부 테이블들
|
||||
fieldMappings: fieldMappings.length > 0 ? fieldMappings : undefined,
|
||||
} as any);
|
||||
}
|
||||
@@ -1706,6 +1735,86 @@ export const getScreenSubTables = async (req: Request, res: Response) => {
|
||||
screenIds,
|
||||
rightPanelCount: rightPanelResult.rows.length
|
||||
});
|
||||
|
||||
// 5. joinedTables에 대한 FK 컬럼을 column_labels에서 조회
|
||||
// rightPanelRelation에서 joinedTables가 있는 경우, 해당 테이블과 조인하는 FK 컬럼 찾기
|
||||
const joinedTableFKLookups: Array<{ subTableName: string; refTable: string }> = [];
|
||||
Object.values(screenSubTables).forEach((screenData: any) => {
|
||||
screenData.subTables.forEach((subTable: any) => {
|
||||
if (subTable.joinedTables && Array.isArray(subTable.joinedTables)) {
|
||||
subTable.joinedTables.forEach((refTable: string) => {
|
||||
joinedTableFKLookups.push({ subTableName: subTable.tableName, refTable });
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// column_labels에서 FK 컬럼 조회 (reference_table로 조인하는 컬럼 찾기)
|
||||
const joinColumnsByTable: { [key: string]: string[] } = {}; // tableName → [FK 컬럼들]
|
||||
if (joinedTableFKLookups.length > 0) {
|
||||
const uniqueLookups = joinedTableFKLookups.filter((item, index, self) =>
|
||||
index === self.findIndex((t) => t.subTableName === item.subTableName && t.refTable === item.refTable)
|
||||
);
|
||||
|
||||
// 각 subTable에 대해 reference_table이 일치하는 컬럼 조회
|
||||
const subTableNames = [...new Set(uniqueLookups.map(l => l.subTableName))];
|
||||
const refTableNames = [...new Set(uniqueLookups.map(l => l.refTable))];
|
||||
|
||||
const fkQuery = `
|
||||
SELECT
|
||||
cl.table_name,
|
||||
cl.column_name,
|
||||
cl.column_label,
|
||||
cl.reference_table,
|
||||
cl.reference_column,
|
||||
tl.table_label as reference_table_label
|
||||
FROM column_labels cl
|
||||
LEFT JOIN table_labels tl ON cl.reference_table = tl.table_name
|
||||
WHERE cl.table_name = ANY($1)
|
||||
AND cl.reference_table = ANY($2)
|
||||
`;
|
||||
|
||||
const fkResult = await pool.query(fkQuery, [subTableNames, refTableNames]);
|
||||
|
||||
// 참조 정보 포함 객체 배열로 저장 (한글명 포함)
|
||||
const joinColumnRefsByTable: Record<string, Array<{ column: string; columnLabel: string; refTable: string; refTableLabel: string; refColumn: string }>> = {};
|
||||
|
||||
fkResult.rows.forEach((row: any) => {
|
||||
if (!joinColumnRefsByTable[row.table_name]) {
|
||||
joinColumnRefsByTable[row.table_name] = [];
|
||||
}
|
||||
// 중복 체크
|
||||
const exists = joinColumnRefsByTable[row.table_name].some(
|
||||
(ref) => ref.column === row.column_name && ref.refTable === row.reference_table
|
||||
);
|
||||
if (!exists) {
|
||||
joinColumnRefsByTable[row.table_name].push({
|
||||
column: row.column_name,
|
||||
columnLabel: row.column_label || row.column_name, // 컬럼 한글명 (없으면 영문명)
|
||||
refTable: row.reference_table,
|
||||
refTableLabel: row.reference_table_label || row.reference_table, // 참조 테이블 한글명 (없으면 영문명)
|
||||
refColumn: row.reference_column || 'id',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// subTables에 joinColumns (문자열 배열) 및 joinColumnRefs (참조 정보 배열) 추가
|
||||
Object.values(screenSubTables).forEach((screenData: any) => {
|
||||
screenData.subTables.forEach((subTable: any) => {
|
||||
const refs = joinColumnRefsByTable[subTable.tableName];
|
||||
if (refs) {
|
||||
(subTable as any).joinColumns = refs.map(r => r.column);
|
||||
(subTable as any).joinColumnRefs = refs;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
logger.info("rightPanel joinedTables FK 조회 완료", {
|
||||
lookupCount: uniqueLookups.length,
|
||||
resultCount: fkResult.rows.length,
|
||||
joinColumnsByTable
|
||||
});
|
||||
}
|
||||
|
||||
// 5. 모든 fieldMappings의 한글명을 column_labels에서 가져와서 적용
|
||||
// 모든 테이블/컬럼 조합을 수집
|
||||
|
||||
Reference in New Issue
Block a user