feat: Implement screen group screens duplication in menu copy service

- Added a new method `copyScreenGroupScreens` to handle the duplication of screen group screens during the menu copy process.
- Implemented logic to create a mapping of screen group IDs from the source to the target company.
- Enhanced the existing menu copy functionality to include the copying of screen group screens, ensuring that the screen-role and display order are preserved.
- Added logging for better traceability of the duplication process.

This update improves the menu copy service by allowing for a more comprehensive duplication of associated screen group screens, enhancing the overall functionality of the menu management system.
This commit is contained in:
DDD1542 2026-03-05 10:09:37 +09:00
parent 772514c270
commit 4f639dec34
1 changed files with 107 additions and 1 deletions

View File

@ -1127,6 +1127,16 @@ export class MenuCopyService {
logger.info("\n🔄 [6.5단계] 메뉴 URL 화면 ID 재매핑"); logger.info("\n🔄 [6.5단계] 메뉴 URL 화면 ID 재매핑");
await this.updateMenuUrls(menuIdMap, screenIdMap, client); await this.updateMenuUrls(menuIdMap, screenIdMap, client);
// === 6.7단계: screen_group_screens 복제 ===
logger.info("\n🏷 [6.7단계] screen_group_screens 복제");
await this.copyScreenGroupScreens(
screenIds,
screenIdMap,
sourceCompanyCode,
targetCompanyCode,
client
);
// === 7단계: 테이블 타입 설정 복사 === // === 7단계: 테이블 타입 설정 복사 ===
if (additionalCopyOptions?.copyTableTypeColumns) { if (additionalCopyOptions?.copyTableTypeColumns) {
logger.info("\n📦 [7단계] 테이블 타입 설정 복사"); logger.info("\n📦 [7단계] 테이블 타입 설정 복사");
@ -2108,6 +2118,26 @@ export class MenuCopyService {
logger.info(`📂 메뉴 복사 중: ${menus.length}`); logger.info(`📂 메뉴 복사 중: ${menus.length}`);
// screen_group_id 재매핑 맵 생성 (source company → target company)
const screenGroupIdMap = new Map<number, number>();
const sourceGroupIds = [...new Set(menus.map(m => m.screen_group_id).filter(Boolean))] as number[];
if (sourceGroupIds.length > 0) {
const sourceGroups = await client.query<{ id: number; group_name: string }>(
`SELECT id, group_name FROM screen_groups WHERE id = ANY($1)`,
[sourceGroupIds]
);
for (const sg of sourceGroups.rows) {
const targetGroup = await client.query<{ id: number }>(
`SELECT id FROM screen_groups WHERE group_name = $1 AND company_code = $2 LIMIT 1`,
[sg.group_name, targetCompanyCode]
);
if (targetGroup.rows.length > 0) {
screenGroupIdMap.set(sg.id, targetGroup.rows[0].id);
}
}
logger.info(`🏷️ screen_group 매핑: ${screenGroupIdMap.size}/${sourceGroupIds.length}`);
}
// 위상 정렬 (부모 먼저 삽입) // 위상 정렬 (부모 먼저 삽입)
const sortedMenus = this.topologicalSortMenus(menus); const sortedMenus = this.topologicalSortMenus(menus);
@ -2252,7 +2282,7 @@ export class MenuCopyService {
menu.menu_code, menu.menu_code,
sourceMenuObjid, sourceMenuObjid,
menu.menu_icon, menu.menu_icon,
menu.screen_group_id, menu.screen_group_id ? (screenGroupIdMap.get(menu.screen_group_id) || menu.screen_group_id) : null,
] ]
); );
@ -2500,6 +2530,82 @@ export class MenuCopyService {
logger.info(`✅ 메뉴 URL 업데이트: ${updatedUrlCount}개, screen_code 업데이트: ${updatedCodeCount}`); logger.info(`✅ 메뉴 URL 업데이트: ${updatedUrlCount}개, screen_code 업데이트: ${updatedCodeCount}`);
} }
/**
* screen_group_screens (- )
*/
private async copyScreenGroupScreens(
screenIds: Set<number>,
screenIdMap: Map<number, number>,
sourceCompanyCode: string,
targetCompanyCode: string,
client: PoolClient
): Promise<void> {
if (screenIds.size === 0 || screenIdMap.size === 0) {
logger.info("📭 screen_group_screens 복제 대상 없음");
return;
}
// 기존 COMPANY_10의 screen_group_screens 삭제 (깨진 이전 데이터 정리)
await client.query(
`DELETE FROM screen_group_screens WHERE company_code = $1`,
[targetCompanyCode]
);
// 소스 회사의 screen_group_screens 조회
const sourceScreenIds = Array.from(screenIds);
const sourceResult = await client.query<{
group_id: number;
screen_id: number;
screen_role: string;
display_order: number;
is_default: string;
}>(
`SELECT group_id, screen_id, screen_role, display_order, is_default
FROM screen_group_screens
WHERE company_code = $1 AND screen_id = ANY($2)`,
[sourceCompanyCode, sourceScreenIds]
);
if (sourceResult.rows.length === 0) {
logger.info("📭 소스에 screen_group_screens 없음");
return;
}
// screen_group ID 매핑 (source group_name → target group_id)
const sourceGroupIds = [...new Set(sourceResult.rows.map(r => r.group_id))];
const sourceGroups = await client.query<{ id: number; group_name: string }>(
`SELECT id, group_name FROM screen_groups WHERE id = ANY($1)`,
[sourceGroupIds]
);
const groupIdMap = new Map<number, number>();
for (const sg of sourceGroups.rows) {
const targetGroup = await client.query<{ id: number }>(
`SELECT id FROM screen_groups WHERE group_name = $1 AND company_code = $2 LIMIT 1`,
[sg.group_name, targetCompanyCode]
);
if (targetGroup.rows.length > 0) {
groupIdMap.set(sg.id, targetGroup.rows[0].id);
}
}
let insertedCount = 0;
for (const row of sourceResult.rows) {
const newGroupId = groupIdMap.get(row.group_id);
const newScreenId = screenIdMap.get(row.screen_id);
if (!newGroupId || !newScreenId) continue;
await client.query(
`INSERT INTO screen_group_screens (group_id, screen_id, screen_role, display_order, is_default, company_code, writer)
VALUES ($1, $2, $3, $4, $5, $6, 'system')
ON CONFLICT DO NOTHING`,
[newGroupId, newScreenId, row.screen_role, row.display_order, row.is_default, targetCompanyCode]
);
insertedCount++;
}
logger.info(`✅ screen_group_screens 복제: ${insertedCount}`);
}
/** /**
* + (최적화: 배치 /) * + (최적화: 배치 /)
*/ */