feat: 화면 그룹 및 서브 테이블 관련 로직 개선
- 화면 그룹 조회 시 삭제된 화면(is_active = 'D')을 제외하도록 쿼리를 수정하였습니다. - 화면 서브 테이블 API에서 전역 메인 테이블 목록을 수집하여, 메인 테이블과 서브 테이블의 우선순위를 적용하였습니다. - 화면 삭제 시 연결된 화면 그룹의 관계를 해제하는 로직을 추가하였습니다. - 화면 관계 흐름에서 연결된 화면들을 추가하는 로직을 개선하여, 그룹 모드와 개별 화면 모드에서의 동작을 명확히 하였습니다. - 관련 문서 및 주석을 업데이트하여 새로운 기능에 대한 이해를 돕도록 하였습니다.
This commit is contained in:
@@ -46,11 +46,13 @@ export const getScreenGroups = async (req: AuthenticatedRequest, res: Response)
|
||||
const countResult = await pool.query(countQuery, params);
|
||||
const total = parseInt(countResult.rows[0].total);
|
||||
|
||||
// 데이터 조회 (screens 배열 포함)
|
||||
// 데이터 조회 (screens 배열 포함) - 삭제된 화면(is_active = 'D') 제외
|
||||
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
|
||||
LEFT JOIN screen_definitions sd ON sgs.screen_id = sd.screen_id
|
||||
WHERE sgs.group_id = sg.id AND sd.is_active != 'D') as screen_count,
|
||||
(SELECT json_agg(
|
||||
json_build_object(
|
||||
'id', sgs.id,
|
||||
@@ -64,6 +66,7 @@ export const getScreenGroups = async (req: AuthenticatedRequest, res: Response)
|
||||
) FROM screen_group_screens sgs
|
||||
LEFT JOIN screen_definitions sd ON sgs.screen_id = sd.screen_id
|
||||
WHERE sgs.group_id = sg.id
|
||||
AND sd.is_active != 'D'
|
||||
) as screens
|
||||
FROM screen_groups sg
|
||||
${whereClause}
|
||||
@@ -111,6 +114,7 @@ export const getScreenGroup = async (req: AuthenticatedRequest, res: Response) =
|
||||
) FROM screen_group_screens sgs
|
||||
LEFT JOIN screen_definitions sd ON sgs.screen_id = sd.screen_id
|
||||
WHERE sgs.group_id = sg.id
|
||||
AND sd.is_active != 'D'
|
||||
) as screens
|
||||
FROM screen_groups sg
|
||||
WHERE sg.id = $1
|
||||
@@ -1737,7 +1741,9 @@ export const getScreenSubTables = async (req: AuthenticatedRequest, res: Respons
|
||||
});
|
||||
|
||||
// 4. rightPanel.relation 파싱 (split-panel-layout 등에서 사용)
|
||||
// screen_layouts (v1)와 screen_layouts_v2 모두 조회
|
||||
const rightPanelQuery = `
|
||||
-- V1: screen_layouts에서 조회
|
||||
SELECT
|
||||
sd.screen_id,
|
||||
sd.screen_name,
|
||||
@@ -1750,6 +1756,23 @@ export const getScreenSubTables = async (req: AuthenticatedRequest, res: Respons
|
||||
JOIN screen_layouts sl ON sd.screen_id = sl.screen_id
|
||||
WHERE sd.screen_id = ANY($1)
|
||||
AND sl.properties->'componentConfig'->'rightPanel'->'relation' IS NOT NULL
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- V2: screen_layouts_v2에서 조회 (v2-split-panel-layout 컴포넌트)
|
||||
SELECT
|
||||
sd.screen_id,
|
||||
sd.screen_name,
|
||||
sd.table_name as main_table,
|
||||
comp->'overrides'->>'type' as component_type,
|
||||
comp->'overrides'->'rightPanel'->'relation' as right_panel_relation,
|
||||
comp->'overrides'->'rightPanel'->>'tableName' as right_panel_table,
|
||||
comp->'overrides'->'rightPanel'->'columns' as right_panel_columns
|
||||
FROM screen_definitions sd
|
||||
JOIN screen_layouts_v2 slv2 ON sd.screen_id = slv2.screen_id,
|
||||
jsonb_array_elements(slv2.layout_data->'components') as comp
|
||||
WHERE sd.screen_id = ANY($1)
|
||||
AND comp->'overrides'->'rightPanel'->'relation' IS NOT NULL
|
||||
`;
|
||||
|
||||
const rightPanelResult = await pool.query(rightPanelQuery, [screenIds]);
|
||||
@@ -2118,9 +2141,56 @@ export const getScreenSubTables = async (req: AuthenticatedRequest, res: Respons
|
||||
}))
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// 6. 전역 메인 테이블 목록 수집 (우선순위 적용용)
|
||||
// ============================================================
|
||||
// 메인 테이블 조건:
|
||||
// 1. screen_definitions.table_name (컴포넌트 직접 연결)
|
||||
// 2. v2-split-panel-layout의 rightPanel.tableName (WHERE 조건 대상)
|
||||
//
|
||||
// 이 목록에 있으면 서브 테이블로 분류되지 않음 (우선순위: 메인 > 서브)
|
||||
const globalMainTablesQuery = `
|
||||
-- 1. 모든 화면의 메인 테이블 (screen_definitions.table_name)
|
||||
SELECT DISTINCT table_name as main_table
|
||||
FROM screen_definitions
|
||||
WHERE screen_id = ANY($1)
|
||||
AND table_name IS NOT NULL
|
||||
|
||||
UNION
|
||||
|
||||
-- 2. v2-split-panel-layout의 rightPanel.tableName (WHERE 조건 대상)
|
||||
-- 현재 그룹의 화면들에서 마스터-디테일로 연결된 테이블
|
||||
SELECT DISTINCT comp->'overrides'->'rightPanel'->>'tableName' as main_table
|
||||
FROM screen_definitions sd
|
||||
JOIN screen_layouts_v2 slv2 ON sd.screen_id = slv2.screen_id,
|
||||
jsonb_array_elements(slv2.layout_data->'components') as comp
|
||||
WHERE sd.screen_id = ANY($1)
|
||||
AND comp->'overrides'->'rightPanel'->>'tableName' IS NOT NULL
|
||||
|
||||
UNION
|
||||
|
||||
-- 3. v1 screen_layouts의 rightPanel.tableName (WHERE 조건 대상)
|
||||
SELECT DISTINCT sl.properties->'componentConfig'->'rightPanel'->>'tableName' as main_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'->'rightPanel'->>'tableName' IS NOT NULL
|
||||
`;
|
||||
|
||||
const globalMainTablesResult = await pool.query(globalMainTablesQuery, [screenIds]);
|
||||
const globalMainTables = globalMainTablesResult.rows
|
||||
.map((r: any) => r.main_table)
|
||||
.filter((t: string) => t != null && t !== '');
|
||||
|
||||
logger.info("전역 메인 테이블 목록 수집 완료", {
|
||||
count: globalMainTables.length,
|
||||
tables: globalMainTables
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: screenSubTables,
|
||||
globalMainTables: globalMainTables, // 메인 테이블로 분류되어야 하는 테이블 목록
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error("화면 서브 테이블 정보 조회 실패:", error);
|
||||
|
||||
@@ -731,6 +731,14 @@ export class ScreenManagementService {
|
||||
WHERE screen_id = $1 AND is_active = 'Y'`,
|
||||
[screenId],
|
||||
);
|
||||
|
||||
// 5. 화면 그룹 연결 삭제 (screen_group_screens)
|
||||
await client.query(
|
||||
`DELETE FROM screen_group_screens WHERE screen_id = $1`,
|
||||
[screenId],
|
||||
);
|
||||
|
||||
logger.info("화면 삭제 시 그룹 연결 해제", { screenId });
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5110,18 +5118,6 @@ export class ScreenManagementService {
|
||||
console.log(
|
||||
`V2 레이아웃 로드 완료: ${layout.layout_data?.components?.length || 0}개 컴포넌트`,
|
||||
);
|
||||
|
||||
// 🐛 디버깅: finished_timeline의 fieldMapping 확인
|
||||
const splitPanel = layout.layout_data?.components?.find((c: any) =>
|
||||
c.url?.includes("v2-split-panel-layout")
|
||||
);
|
||||
const finishedTimeline = splitPanel?.overrides?.rightPanel?.components?.find(
|
||||
(c: any) => c.id === "finished_timeline"
|
||||
);
|
||||
if (finishedTimeline) {
|
||||
console.log("🐛 [Backend] finished_timeline fieldMapping:", JSON.stringify(finishedTimeline.componentConfig?.fieldMapping));
|
||||
}
|
||||
|
||||
return layout.layout_data;
|
||||
}
|
||||
|
||||
@@ -5161,20 +5157,16 @@ export class ScreenManagementService {
|
||||
...layoutData
|
||||
};
|
||||
|
||||
// SUPER_ADMIN인 경우 화면 정의의 company_code로 저장 (로드와 일관성 유지)
|
||||
const saveCompanyCode = companyCode === "*" ? existingScreen.company_code : companyCode;
|
||||
console.log(`저장할 company_code: ${saveCompanyCode} (원본: ${companyCode}, 화면 정의: ${existingScreen.company_code})`);
|
||||
|
||||
// UPSERT (있으면 업데이트, 없으면 삽입)
|
||||
await query(
|
||||
`INSERT INTO screen_layouts_v2 (screen_id, company_code, layout_data, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, NOW(), NOW())
|
||||
ON CONFLICT (screen_id, company_code)
|
||||
DO UPDATE SET layout_data = $3, updated_at = NOW()`,
|
||||
[screenId, saveCompanyCode, JSON.stringify(dataToSave)],
|
||||
[screenId, companyCode, JSON.stringify(dataToSave)],
|
||||
);
|
||||
|
||||
console.log(`V2 레이아웃 저장 완료 (company_code: ${saveCompanyCode})`);
|
||||
console.log(`V2 레이아웃 저장 완료`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user