diff --git a/backend-node/src/services/menuCopyService.ts b/backend-node/src/services/menuCopyService.ts index bc80569f..053641ff 100644 --- a/backend-node/src/services/menuCopyService.ts +++ b/backend-node/src/services/menuCopyService.ts @@ -247,9 +247,7 @@ export class MenuCopyService { typeof screenId === "number" ? screenId : parseInt(screenId); if (!isNaN(numId)) { referenced.push(numId); - logger.debug( - ` ๐Ÿ“‘ ํƒญ ์ปดํฌ๋„ŒํŠธ์—์„œ ํ™”๋ฉด ์ฐธ์กฐ ๋ฐœ๊ฒฌ: ${numId} (ํƒญ: ${tab.label || tab.id})` - ); + logger.debug(` ๐Ÿ“‘ ํƒญ ์ปดํฌ๋„ŒํŠธ์—์„œ ํ™”๋ฉด ์ฐธ์กฐ ๋ฐœ๊ฒฌ: ${numId} (ํƒญ: ${tab.label || tab.id})`); } } } @@ -259,9 +257,7 @@ export class MenuCopyService { if (props?.componentConfig?.leftScreenId) { const leftScreenId = props.componentConfig.leftScreenId; const numId = - typeof leftScreenId === "number" - ? leftScreenId - : parseInt(leftScreenId); + typeof leftScreenId === "number" ? leftScreenId : parseInt(leftScreenId); if (!isNaN(numId) && numId > 0) { referenced.push(numId); logger.debug(` ๐Ÿ“ ๋ถ„ํ•  ํŒจ๋„ ์ขŒ์ธก ํ™”๋ฉด ์ฐธ์กฐ ๋ฐœ๊ฒฌ: ${numId}`); @@ -271,9 +267,7 @@ export class MenuCopyService { if (props?.componentConfig?.rightScreenId) { const rightScreenId = props.componentConfig.rightScreenId; const numId = - typeof rightScreenId === "number" - ? rightScreenId - : parseInt(rightScreenId); + typeof rightScreenId === "number" ? rightScreenId : parseInt(rightScreenId); if (!isNaN(numId) && numId > 0) { referenced.push(numId); logger.debug(` ๐Ÿ“ ๋ถ„ํ•  ํŒจ๋„ ์šฐ์ธก ํ™”๋ฉด ์ฐธ์กฐ ๋ฐœ๊ฒฌ: ${numId}`); @@ -299,16 +293,18 @@ export class MenuCopyService { const screenIds = new Set(); const visited = new Set(); - // 1) ๋ฉ”๋‰ด์— ์ง์ ‘ ํ• ๋‹น๋œ ํ™”๋ฉด - ๋ฐฐ์น˜ ์กฐํšŒ - const assignmentsResult = await client.query<{ screen_id: number }>( - `SELECT DISTINCT screen_id - FROM screen_menu_assignments - WHERE menu_objid = ANY($1) AND company_code = $2`, - [menuObjids, sourceCompanyCode] - ); + // 1) ๋ฉ”๋‰ด์— ์ง์ ‘ ํ• ๋‹น๋œ ํ™”๋ฉด + for (const menuObjid of menuObjids) { + const assignmentsResult = await client.query<{ screen_id: number }>( + `SELECT DISTINCT screen_id + FROM screen_menu_assignments + WHERE menu_objid = $1 AND company_code = $2`, + [menuObjid, sourceCompanyCode] + ); - for (const assignment of assignmentsResult.rows) { - screenIds.add(assignment.screen_id); + for (const assignment of assignmentsResult.rows) { + screenIds.add(assignment.screen_id); + } } logger.info(`๐Ÿ“Œ ์ง์ ‘ ํ• ๋‹น ํ™”๋ฉด: ${screenIds.size}๊ฐœ`); @@ -363,62 +359,37 @@ export class MenuCopyService { logger.info(`๐Ÿ”„ ํ”Œ๋กœ์šฐ ์ˆ˜์ง‘ ์‹œ์ž‘: ${screenIds.size}๊ฐœ ํ™”๋ฉด`); const flowIds = new Set(); - const flowDetails: Array<{ - flowId: number; - flowName: string; - screenId: number; - }> = []; + const flowDetails: Array<{ flowId: number; flowName: string; screenId: number }> = []; - // ๋ฐฐ์น˜ ์กฐํšŒ: ๋ชจ๋“  ํ™”๋ฉด์˜ ๋ ˆ์ด์•„์›ƒ์„ ํ•œ ๋ฒˆ์— ์กฐํšŒ - const screenIdArray = Array.from(screenIds); - if (screenIdArray.length === 0) { - return flowIds; - } + for (const screenId of screenIds) { + const layoutsResult = await client.query( + `SELECT properties FROM screen_layouts WHERE screen_id = $1`, + [screenId] + ); - const layoutsResult = await client.query< - ScreenLayout & { screen_id: number } - >( - `SELECT screen_id, properties FROM screen_layouts WHERE screen_id = ANY($1)`, - [screenIdArray] - ); + for (const layout of layoutsResult.rows) { + const props = layout.properties; - for (const layout of layoutsResult.rows) { - const props = layout.properties; - const screenId = layout.screen_id; - - // webTypeConfig.dataflowConfig.flowConfig.flowId - const flowId = props?.webTypeConfig?.dataflowConfig?.flowConfig?.flowId; - const flowName = - props?.webTypeConfig?.dataflowConfig?.flowConfig?.flowName || "Unknown"; - - if (flowId && typeof flowId === "number" && flowId > 0) { - if (!flowIds.has(flowId)) { - flowIds.add(flowId); - flowDetails.push({ flowId, flowName, screenId }); - logger.info( - ` ๐Ÿ“Ž ํ™”๋ฉด ${screenId}์—์„œ ํ”Œ๋กœ์šฐ ๋ฐœ๊ฒฌ: id=${flowId}, name="${flowName}"` - ); + // webTypeConfig.dataflowConfig.flowConfig.flowId + const flowId = props?.webTypeConfig?.dataflowConfig?.flowConfig?.flowId; + const flowName = props?.webTypeConfig?.dataflowConfig?.flowConfig?.flowName || "Unknown"; + + if (flowId && typeof flowId === "number" && flowId > 0) { + if (!flowIds.has(flowId)) { + flowIds.add(flowId); + flowDetails.push({ flowId, flowName, screenId }); + logger.info(` ๐Ÿ“Ž ํ™”๋ฉด ${screenId}์—์„œ ํ”Œ๋กœ์šฐ ๋ฐœ๊ฒฌ: id=${flowId}, name="${flowName}"`); + } } - } - // selectedDiagramId๋„ ํ™•์ธ (flowId์™€ ๋™์ผํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๋‹ค๋ฅผ ์ˆ˜๋„ ์žˆ์Œ) - const selectedDiagramId = - props?.webTypeConfig?.dataflowConfig?.selectedDiagramId; - if ( - selectedDiagramId && - typeof selectedDiagramId === "number" && - selectedDiagramId > 0 - ) { - if (!flowIds.has(selectedDiagramId)) { - flowIds.add(selectedDiagramId); - flowDetails.push({ - flowId: selectedDiagramId, - flowName: "SelectedDiagram", - screenId, - }); - logger.info( - ` ๐Ÿ“Ž ํ™”๋ฉด ${screenId}์—์„œ selectedDiagramId ๋ฐœ๊ฒฌ: id=${selectedDiagramId}` - ); + // selectedDiagramId๋„ ํ™•์ธ (flowId์™€ ๋™์ผํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๋‹ค๋ฅผ ์ˆ˜๋„ ์žˆ์Œ) + const selectedDiagramId = props?.webTypeConfig?.dataflowConfig?.selectedDiagramId; + if (selectedDiagramId && typeof selectedDiagramId === "number" && selectedDiagramId > 0) { + if (!flowIds.has(selectedDiagramId)) { + flowIds.add(selectedDiagramId); + flowDetails.push({ flowId: selectedDiagramId, flowName: "SelectedDiagram", screenId }); + logger.info(` ๐Ÿ“Ž ํ™”๋ฉด ${screenId}์—์„œ selectedDiagramId ๋ฐœ๊ฒฌ: id=${selectedDiagramId}`); + } } } } @@ -429,7 +400,7 @@ export class MenuCopyService { } else { logger.info(`๐Ÿ“ญ ์ˆ˜์ง‘๋œ ํ”Œ๋กœ์šฐ ์—†์Œ (ํ™”๋ฉด์— ํ”Œ๋กœ์šฐ ์ฐธ์กฐ๊ฐ€ ์—†์Œ)`); } - + return flowIds; } @@ -491,13 +462,7 @@ export class MenuCopyService { const updated = JSON.parse(JSON.stringify(properties)); // ์žฌ๊ท€์ ์œผ๋กœ ๊ฐ์ฒด/๋ฐฐ์—ด ํƒ์ƒ‰ - this.recursiveUpdateReferences( - updated, - screenIdMap, - flowIdMap, - "", - numberingRuleIdMap - ); + this.recursiveUpdateReferences(updated, screenIdMap, flowIdMap, "", numberingRuleIdMap); return updated; } @@ -574,24 +539,13 @@ export class MenuCopyService { } // numberingRuleId ๋งคํ•‘ (๋ฌธ์ž์—ด) - if ( - key === "numberingRuleId" && - numberingRuleIdMap && - typeof value === "string" && - value - ) { + if (key === "numberingRuleId" && numberingRuleIdMap && typeof value === "string" && value) { const newRuleId = numberingRuleIdMap.get(value); if (newRuleId) { obj[key] = newRuleId; logger.info( ` ๐Ÿ”— ์ฑ„๋ฒˆ๊ทœ์น™ ์ฐธ์กฐ ์—…๋ฐ์ดํŠธ (${currentPath}): ${value} โ†’ ${newRuleId}` ); - } else { - // ๋งคํ•‘์ด ์—†๋Š” ์ฑ„๋ฒˆ๊ทœ์น™์€ ๋นˆ ๊ฐ’์œผ๋กœ ์„ค์ • (๋‹ค๋ฅธ ํšŒ์‚ฌ ์ฑ„๋ฒˆ๊ทœ์น™ ์ฐธ์กฐ ๋ฐฉ์ง€) - logger.warn( - ` โš ๏ธ ์ฑ„๋ฒˆ๊ทœ์น™ ๋งคํ•‘ ์—†์Œ (${currentPath}): ${value} โ†’ ๋นˆ ๊ฐ’์œผ๋กœ ์„ค์ •` - ); - obj[key] = ""; } } @@ -636,15 +590,11 @@ export class MenuCopyService { } const sourceMenu = sourceMenuResult.rows[0]; - const isRootMenu = - !sourceMenu.parent_obj_id || sourceMenu.parent_obj_id === 0; + const isRootMenu = !sourceMenu.parent_obj_id || sourceMenu.parent_obj_id === 0; // 2. ๋Œ€์ƒ ํšŒ์‚ฌ์— ๊ฐ™์€ ์›๋ณธ์—์„œ ๋ณต์‚ฌ๋œ ๋ฉ”๋‰ด ์ฐพ๊ธฐ (source_menu_objid๋กœ ์ •ํ™•ํžˆ ๋งค์นญ) // ์ตœ์ƒ์œ„/ํ•˜์œ„ ๊ตฌ๋ถ„ ์—†์ด ๋ชจ๋“  ๋ณต์‚ฌ๋ณธ ๊ฒ€์ƒ‰ - const existingMenuResult = await client.query<{ - objid: number; - parent_obj_id: number | null; - }>( + const existingMenuResult = await client.query<{ objid: number; parent_obj_id: number | null }>( `SELECT objid, parent_obj_id FROM menu_info WHERE source_menu_objid = $1 @@ -658,9 +608,8 @@ export class MenuCopyService { } const existingMenuObjid = existingMenuResult.rows[0].objid; - const existingIsRoot = - !existingMenuResult.rows[0].parent_obj_id || - existingMenuResult.rows[0].parent_obj_id === 0; + const existingIsRoot = !existingMenuResult.rows[0].parent_obj_id || + existingMenuResult.rows[0].parent_obj_id === 0; logger.info( `๐Ÿ” ๊ธฐ์กด ๋ณต์‚ฌ๋ณธ ๋ฐœ๊ฒฌ: ${sourceMenu.menu_name_kor} (์›๋ณธ: ${sourceMenuObjid}, ๋ณต์‚ฌ๋ณธ: ${existingMenuObjid}, ์ตœ์ƒ์œ„: ${existingIsRoot})` @@ -700,14 +649,10 @@ export class MenuCopyService { WHERE screen_id = ANY($1) AND company_code = $2`, [screenIds, targetCompanyCode] ); - const sharedScreenIds = new Set( - sharedScreensResult.rows.map((r) => r.screen_id) - ); + const sharedScreenIds = new Set(sharedScreensResult.rows.map(r => r.screen_id)); // ๊ณต์œ ๋˜์ง€ ์•Š์€ ํ™”๋ฉด๋งŒ ์‚ญ์ œ - const screensToDelete = screenIds.filter( - (id) => !sharedScreenIds.has(id) - ); + const screensToDelete = screenIds.filter(id => !sharedScreenIds.has(id)); if (screensToDelete.length > 0) { // ๋ ˆ์ด์•„์›ƒ ์‚ญ์ œ @@ -717,8 +662,8 @@ export class MenuCopyService { ); // ํ™”๋ฉด ์ •์˜ ์‚ญ์ œ - await client.query( - `DELETE FROM screen_definitions + await client.query( + `DELETE FROM screen_definitions WHERE screen_id = ANY($1) AND company_code = $2`, [screensToDelete, targetCompanyCode] ); @@ -726,9 +671,7 @@ export class MenuCopyService { } if (sharedScreenIds.size > 0) { - logger.info( - ` โ™ป๏ธ ๊ณต์œ  ํ™”๋ฉด ์œ ์ง€: ${sharedScreenIds.size}๊ฐœ (๋‹ค๋ฅธ ๋ฉ”๋‰ด์—์„œ ์‚ฌ์šฉ ์ค‘)` - ); + logger.info(` โ™ป๏ธ ๊ณต์œ  ํ™”๋ฉด ์œ ์ง€: ${sharedScreenIds.size}๊ฐœ (๋‹ค๋ฅธ ๋ฉ”๋‰ด์—์„œ ์‚ฌ์šฉ ์ค‘)`); } } @@ -738,45 +681,56 @@ export class MenuCopyService { ]); logger.info(` โœ… ๋ฉ”๋‰ด ๊ถŒํ•œ ์‚ญ์ œ ์™„๋ฃŒ`); - // 5-4. ์ฑ„๋ฒˆ ๊ทœ์น™์˜ menu_objid ์ฐธ์กฐ ํ•ด์ œ (์‚ญ์ œํ•˜์ง€ ์•Š๊ณ  ์—ฐ๊ฒฐ๋งŒ ๋Š์Œ) - // ์ฑ„๋ฒˆ ๊ทœ์น™์€ ํšŒ์‚ฌ์˜ ํ•ต์‹ฌ ์—…๋ฌด ๋ฐ์ดํ„ฐ์ด๋ฏ€๋กœ ๋ณด์กดํ•ด์•ผ ํ•จ - const updatedNumberingRules = await client.query( - `UPDATE numbering_rules + // 5-4. ์ฑ„๋ฒˆ ๊ทœ์น™ ์ฒ˜๋ฆฌ (์™ธ๋ž˜ํ‚ค ์ œ์•ฝ์กฐ๊ฑด ํ•ด๊ฒฐ) + // scope_type = 'menu'์ธ ์ฑ„๋ฒˆ ๊ทœ์น™: ๋ฉ”๋‰ด ์ „์šฉ์ด๋ฏ€๋กœ ์‚ญ์ œ (ํŒŒํŠธ ํฌํ•จ) + const menuScopedRulesResult = await client.query( + `SELECT rule_id FROM numbering_rules + WHERE menu_objid = ANY($1) AND company_code = $2 AND scope_type = 'menu'`, + [existingMenuIds, targetCompanyCode] + ); + if (menuScopedRulesResult.rows.length > 0) { + const menuScopedRuleIds = menuScopedRulesResult.rows.map(r => r.rule_id); + // ์ฑ„๋ฒˆ ๊ทœ์น™ ํŒŒํŠธ ๋จผ์ € ์‚ญ์ œ + await client.query( + `DELETE FROM numbering_rule_parts WHERE rule_id = ANY($1)`, + [menuScopedRuleIds] + ); + // ์ฑ„๋ฒˆ ๊ทœ์น™ ์‚ญ์ œ + await client.query( + `DELETE FROM numbering_rules WHERE rule_id = ANY($1)`, + [menuScopedRuleIds] + ); + logger.info(` โœ… ๋ฉ”๋‰ด ์ „์šฉ ์ฑ„๋ฒˆ ๊ทœ์น™ ์‚ญ์ œ: ${menuScopedRuleIds.length}๊ฐœ`); + } + + // scope_type != 'menu'์ธ ์ฑ„๋ฒˆ ๊ทœ์น™: menu_objid๋งŒ NULL๋กœ ์„ค์ • (๊ทœ์น™ ๋ณด์กด) + const tableScopedRulesResult = await client.query( + `UPDATE numbering_rules SET menu_objid = NULL - WHERE menu_objid = ANY($1) AND company_code = $2 + WHERE menu_objid = ANY($1) AND company_code = $2 AND (scope_type IS NULL OR scope_type != 'menu') RETURNING rule_id`, [existingMenuIds, targetCompanyCode] ); - if (updatedNumberingRules.rowCount && updatedNumberingRules.rowCount > 0) { - logger.info( - ` โœ… ์ฑ„๋ฒˆ ๊ทœ์น™ ๋ฉ”๋‰ด ์—ฐ๊ฒฐ ํ•ด์ œ: ${updatedNumberingRules.rowCount}๊ฐœ (๋ฐ์ดํ„ฐ ๋ณด์กด๋จ)` - ); + if (tableScopedRulesResult.rows.length > 0) { + logger.info(` โœ… ํ…Œ์ด๋ธ” ์Šค์ฝ”ํ”„ ์ฑ„๋ฒˆ ๊ทœ์น™ ์—ฐ๊ฒฐ ํ•ด์ œ: ${tableScopedRulesResult.rows.length}๊ฐœ (๋ฐ์ดํ„ฐ ๋ณด์กด)`); } - // 5-5. ์นดํ…Œ๊ณ ๋ฆฌ ๋งคํ•‘ ์‚ญ์ œ (menu_objid๊ฐ€ NOT NULL์ด๋ฏ€๋กœ NULL ์„ค์ • ๋ถˆ๊ฐ€) - // ์นดํ…Œ๊ณ ๋ฆฌ ๋งคํ•‘์€ ๋ฉ”๋‰ด์™€ ๊ฐ•ํ•˜๊ฒŒ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ํ•จ๊ป˜ ์‚ญ์ œ + // 5-5. ์นดํ…Œ๊ณ ๋ฆฌ ์ปฌ๋Ÿผ ๋งคํ•‘ ์‚ญ์ œ (NOT NULL ์ œ์•ฝ์กฐ๊ฑด์œผ๋กœ ์ธํ•ด ์‚ญ์ œ) const deletedCategoryMappings = await client.query( - `DELETE FROM category_column_mapping + `DELETE FROM category_column_mapping WHERE menu_objid = ANY($1) AND company_code = $2 RETURNING mapping_id`, [existingMenuIds, targetCompanyCode] ); - if ( - deletedCategoryMappings.rowCount && - deletedCategoryMappings.rowCount > 0 - ) { - logger.info( - ` โœ… ์นดํ…Œ๊ณ ๋ฆฌ ๋งคํ•‘ ์‚ญ์ œ ์™„๋ฃŒ: ${deletedCategoryMappings.rowCount}๊ฐœ` - ); + if (deletedCategoryMappings.rows.length > 0) { + logger.info(` โœ… ์นดํ…Œ๊ณ ๋ฆฌ ๋งคํ•‘ ์‚ญ์ œ: ${deletedCategoryMappings.rows.length}๊ฐœ`); } - // 5-6. ๋ฉ”๋‰ด ์‚ญ์ œ (๋ฐฐ์น˜ ์‚ญ์ œ - ํ•˜์œ„ ๋ฉ”๋‰ด๋ถ€ํ„ฐ ์‚ญ์ œ๋ฅผ ์œ„ํ•ด ์—ญ์ˆœ ์ •๋ ฌ๋œ ID ์‚ฌ์šฉ) - // ์™ธ๋ž˜ํ‚ค ์ œ์•ฝ์ด ํ•ด์ œ๋˜์—ˆ์œผ๋ฏ€๋กœ ๋ฐฐ์น˜ ์‚ญ์ œ ๊ฐ€๋Šฅ - if (existingMenuIds.length > 0) { - await client.query(`DELETE FROM menu_info WHERE objid = ANY($1)`, [ - existingMenuIds, - ]); - } + // 5-6. ๋ฉ”๋‰ด ์‚ญ์ œ (๋ฐฐ์น˜) + await client.query( + `DELETE FROM menu_info WHERE objid = ANY($1)`, + [existingMenuIds] + ); logger.info(` โœ… ๋ฉ”๋‰ด ์‚ญ์ œ ์™„๋ฃŒ: ${existingMenus.length}๊ฐœ`); logger.info("โœ… ๊ธฐ์กด ๋ณต์‚ฌ๋ณธ ์‚ญ์ œ ์™„๋ฃŒ - ๋ฎ์–ด์“ฐ๊ธฐ ์ค€๋น„๋จ"); @@ -883,10 +837,10 @@ export class MenuCopyService { const ruleResult = await this.copyNumberingRulesWithMap( menuObjids, menuIdMap, // ์‹ค์ œ ์ƒ์„ฑ๋œ ๋ฉ”๋‰ด ID ์‚ฌ์šฉ - targetCompanyCode, - userId, - client - ); + targetCompanyCode, + userId, + client + ); copiedNumberingRules = ruleResult.copiedCount; numberingRuleIdMap = ruleResult.ruleIdMap; } @@ -929,20 +883,6 @@ export class MenuCopyService { ); } - // === 4.9๋‹จ๊ณ„: ํ™”๋ฉด์—์„œ ์ฐธ์กฐํ•˜๋Š” ์ฑ„๋ฒˆ๊ทœ์น™ ๋งคํ•‘ ๋ณด์™„ === - // ํ™”๋ฉด properties์—์„œ ์ฐธ์กฐํ•˜๋Š” ์ฑ„๋ฒˆ๊ทœ์น™ ์ค‘ ์•„์ง ๋งคํ•‘๋˜์ง€ ์•Š์€ ๊ฒƒ๋“ค์„ - // ๋Œ€์ƒ ํšŒ์‚ฌ์—์„œ ๊ฐ™์€ ์ด๋ฆ„์˜ ์ฑ„๋ฒˆ๊ทœ์น™์œผ๋กœ ๋งคํ•‘ - if (screenIds.size > 0) { - logger.info("\n๐Ÿ”— [4.9๋‹จ๊ณ„] ํ™”๋ฉด ์ฑ„๋ฒˆ๊ทœ์น™ ์ฐธ์กฐ ๋งคํ•‘ ๋ณด์™„"); - await this.supplementNumberingRuleMapping( - Array.from(screenIds), - sourceCompanyCode, - targetCompanyCode, - numberingRuleIdMap, - client - ); - } - // === 5๋‹จ๊ณ„: ํ™”๋ฉด ๋ณต์‚ฌ === logger.info("\n๐Ÿ“„ [5๋‹จ๊ณ„] ํ™”๋ฉด ๋ณต์‚ฌ"); const screenIdMap = await this.copyScreens( @@ -1051,21 +991,17 @@ export class MenuCopyService { `SELECT * FROM flow_definition WHERE id = ANY($1)`, [flowIdArray] ); - const flowDefMap = new Map(allFlowDefsResult.rows.map((f) => [f.id, f])); + const flowDefMap = new Map(allFlowDefsResult.rows.map(f => [f.id, f])); // 2) ๋Œ€์ƒ ํšŒ์‚ฌ์˜ ๊ธฐ์กด ํ”Œ๋กœ์šฐ ํ•œ ๋ฒˆ์— ์กฐํšŒ (์ด๋ฆ„+ํ…Œ์ด๋ธ” ๊ธฐ์ค€) - const flowNames = allFlowDefsResult.rows.map((f) => f.name); - const existingFlowsResult = await client.query<{ - id: number; - name: string; - table_name: string; - }>( + const flowNames = allFlowDefsResult.rows.map(f => f.name); + const existingFlowsResult = await client.query<{ id: number; name: string; table_name: string }>( `SELECT id, name, table_name FROM flow_definition WHERE company_code = $1 AND name = ANY($2)`, [targetCompanyCode, flowNames] ); const existingFlowMap = new Map( - existingFlowsResult.rows.map((f) => [`${f.name}|${f.table_name}`, f.id]) + existingFlowsResult.rows.map(f => [`${f.name}|${f.table_name}`, f.id]) ); // 3) ๋ณต์‚ฌ๊ฐ€ ํ•„์š”ํ•œ ํ”Œ๋กœ์šฐ ID ๋ชฉ๋ก @@ -1074,18 +1010,16 @@ export class MenuCopyService { for (const originalFlowId of flowIdArray) { const flowDef = flowDefMap.get(originalFlowId); if (!flowDef) { - logger.warn(`โš ๏ธ ํ”Œ๋กœ์šฐ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Œ: id=${originalFlowId}`); - continue; - } + logger.warn(`โš ๏ธ ํ”Œ๋กœ์šฐ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Œ: id=${originalFlowId}`); + continue; + } const key = `${flowDef.name}|${flowDef.table_name}`; const existingId = existingFlowMap.get(key); if (existingId) { flowIdMap.set(originalFlowId, existingId); - logger.info( - ` โ™ป๏ธ ๊ธฐ์กด ํ”Œ๋กœ์šฐ ์žฌ์‚ฌ์šฉ: ${originalFlowId} โ†’ ${existingId} (${flowDef.name})` - ); + logger.info(` โ™ป๏ธ ๊ธฐ์กด ํ”Œ๋กœ์šฐ ์žฌ์‚ฌ์šฉ: ${originalFlowId} โ†’ ${existingId} (${flowDef.name})`); } else { flowsToCopy.push(flowDef); } @@ -1094,26 +1028,17 @@ export class MenuCopyService { // 4) ์ƒˆ ํ”Œ๋กœ์šฐ ๋ณต์‚ฌ (๋ฐฐ์น˜ ์ฒ˜๋ฆฌ) if (flowsToCopy.length > 0) { // ๋ฐฐ์น˜ INSERT๋กœ ํ”Œ๋กœ์šฐ ์ƒ์„ฑ - const flowValues = flowsToCopy - .map( - (f, i) => - `($${i * 8 + 1}, $${i * 8 + 2}, $${i * 8 + 3}, $${i * 8 + 4}, $${i * 8 + 5}, $${i * 8 + 6}, $${i * 8 + 7}, $${i * 8 + 8})` - ) - .join(", "); - - const flowParams = flowsToCopy.flatMap((f) => [ - f.name, - f.description, - f.table_name, - f.is_active, - targetCompanyCode, - userId, - f.db_source_type, - f.db_connection_id, + const flowValues = flowsToCopy.map((f, i) => + `($${i * 8 + 1}, $${i * 8 + 2}, $${i * 8 + 3}, $${i * 8 + 4}, $${i * 8 + 5}, $${i * 8 + 6}, $${i * 8 + 7}, $${i * 8 + 8})` + ).join(", "); + + const flowParams = flowsToCopy.flatMap(f => [ + f.name, f.description, f.table_name, f.is_active, + targetCompanyCode, userId, f.db_source_type, f.db_connection_id ]); const newFlowsResult = await client.query<{ id: number }>( - `INSERT INTO flow_definition ( + `INSERT INTO flow_definition ( name, description, table_name, is_active, company_code, created_by, db_source_type, db_connection_id ) VALUES ${flowValues} @@ -1125,13 +1050,11 @@ export class MenuCopyService { flowsToCopy.forEach((flowDef, index) => { const newFlowId = newFlowsResult.rows[index].id; flowIdMap.set(flowDef.id, newFlowId); - logger.info( - ` โœ… ํ”Œ๋กœ์šฐ ์‹ ๊ทœ ๋ณต์‚ฌ: ${flowDef.id} โ†’ ${newFlowId} (${flowDef.name})` - ); + logger.info(` โœ… ํ”Œ๋กœ์šฐ ์‹ ๊ทœ ๋ณต์‚ฌ: ${flowDef.id} โ†’ ${newFlowId} (${flowDef.name})`); }); // 5) ์Šคํ… ๋ฐ ์—ฐ๊ฒฐ ๋ณต์‚ฌ (๋ณต์‚ฌ๋œ ํ”Œ๋กœ์šฐ๋งŒ) - const originalFlowIdsToCopy = flowsToCopy.map((f) => f.id); + const originalFlowIdsToCopy = flowsToCopy.map(f => f.id); // ๋ชจ๋“  ์Šคํ… ํ•œ ๋ฒˆ์— ์กฐํšŒ const allStepsResult = await client.query( @@ -1150,7 +1073,7 @@ export class MenuCopyService { // ์Šคํ… ๋ณต์‚ฌ (ํ”Œ๋กœ์šฐ๋ณ„) const allStepIdMaps = new Map>(); // originalFlowId -> stepIdMap - + for (const originalFlowId of originalFlowIdsToCopy) { const newFlowId = flowIdMap.get(originalFlowId)!; const steps = stepsByFlow.get(originalFlowId) || []; @@ -1158,31 +1081,15 @@ export class MenuCopyService { if (steps.length > 0) { // ๋ฐฐ์น˜ INSERT๋กœ ์Šคํ… ์ƒ์„ฑ - const stepValues = steps - .map( - (_, i) => - `($${i * 17 + 1}, $${i * 17 + 2}, $${i * 17 + 3}, $${i * 17 + 4}, $${i * 17 + 5}, $${i * 17 + 6}, $${i * 17 + 7}, $${i * 17 + 8}, $${i * 17 + 9}, $${i * 17 + 10}, $${i * 17 + 11}, $${i * 17 + 12}, $${i * 17 + 13}, $${i * 17 + 14}, $${i * 17 + 15}, $${i * 17 + 16}, $${i * 17 + 17})` - ) - .join(", "); + const stepValues = steps.map((_, i) => + `($${i * 17 + 1}, $${i * 17 + 2}, $${i * 17 + 3}, $${i * 17 + 4}, $${i * 17 + 5}, $${i * 17 + 6}, $${i * 17 + 7}, $${i * 17 + 8}, $${i * 17 + 9}, $${i * 17 + 10}, $${i * 17 + 11}, $${i * 17 + 12}, $${i * 17 + 13}, $${i * 17 + 14}, $${i * 17 + 15}, $${i * 17 + 16}, $${i * 17 + 17})` + ).join(", "); - const stepParams = steps.flatMap((s) => [ - newFlowId, - s.step_name, - s.step_order, - s.condition_json, - s.color, - s.position_x, - s.position_y, - s.table_name, - s.move_type, - s.status_column, - s.status_value, - s.target_table, - s.field_mappings, - s.required_fields, - s.integration_type, - s.integration_config, - s.display_config, + const stepParams = steps.flatMap(s => [ + newFlowId, s.step_name, s.step_order, s.condition_json, + s.color, s.position_x, s.position_y, s.table_name, s.move_type, + s.status_column, s.status_value, s.target_table, s.field_mappings, + s.required_fields, s.integration_type, s.integration_config, s.display_config ]); const newStepsResult = await client.query<{ id: number }>( @@ -1200,9 +1107,7 @@ export class MenuCopyService { stepIdMap.set(step.id, newStepsResult.rows[index].id); }); - logger.info( - ` โ†ณ ํ”Œ๋กœ์šฐ ${originalFlowId}: ์Šคํ… ${steps.length}๊ฐœ ๋ณต์‚ฌ` - ); + logger.info(` โ†ณ ํ”Œ๋กœ์šฐ ${originalFlowId}: ์Šคํ… ${steps.length}๊ฐœ ๋ณต์‚ฌ`); } allStepIdMaps.set(originalFlowId, stepIdMap); @@ -1215,19 +1120,14 @@ export class MenuCopyService { ); // ์—ฐ๊ฒฐ ๋ณต์‚ฌ (๋ฐฐ์น˜ INSERT) - const connectionsToInsert: { - newFlowId: number; - newFromStepId: number; - newToStepId: number; - label: string; - }[] = []; + const connectionsToInsert: { newFlowId: number; newFromStepId: number; newToStepId: number; label: string }[] = []; for (const conn of allConnectionsResult.rows) { const stepIdMap = allStepIdMaps.get(conn.flow_definition_id); if (!stepIdMap) continue; - const newFromStepId = stepIdMap.get(conn.from_step_id); - const newToStepId = stepIdMap.get(conn.to_step_id); + const newFromStepId = stepIdMap.get(conn.from_step_id); + const newToStepId = stepIdMap.get(conn.to_step_id); const newFlowId = flowIdMap.get(conn.flow_definition_id); if (newFromStepId && newToStepId && newFlowId) { @@ -1235,32 +1135,26 @@ export class MenuCopyService { newFlowId, newFromStepId, newToStepId, - label: conn.label || "", + label: conn.label || "" }); } } if (connectionsToInsert.length > 0) { - const connValues = connectionsToInsert - .map( - (_, i) => - `($${i * 4 + 1}, $${i * 4 + 2}, $${i * 4 + 3}, $${i * 4 + 4})` - ) - .join(", "); + const connValues = connectionsToInsert.map((_, i) => + `($${i * 4 + 1}, $${i * 4 + 2}, $${i * 4 + 3}, $${i * 4 + 4})` + ).join(", "); - const connParams = connectionsToInsert.flatMap((c) => [ - c.newFlowId, - c.newFromStepId, - c.newToStepId, - c.label, + const connParams = connectionsToInsert.flatMap(c => [ + c.newFlowId, c.newFromStepId, c.newToStepId, c.label ]); - await client.query( - `INSERT INTO flow_step_connection ( + await client.query( + `INSERT INTO flow_step_connection ( flow_definition_id, from_step_id, to_step_id, label ) VALUES ${connValues}`, connParams - ); + ); logger.info(` โ†ณ ์—ฐ๊ฒฐ ${connectionsToInsert.length}๊ฐœ ๋ณต์‚ฌ`); } @@ -1297,37 +1191,6 @@ export class MenuCopyService { logger.info(`๐Ÿ“„ ํ™”๋ฉด ๋ณต์‚ฌ/์—…๋ฐ์ดํŠธ ์ค‘: ${screenIds.size}๊ฐœ`); - // === 0๋‹จ๊ณ„: ์›๋ณธ ํ™”๋ฉด ์ •์˜ ๋ฐฐ์น˜ ์กฐํšŒ === - const screenIdArray = Array.from(screenIds); - const allScreenDefsResult = await client.query( - `SELECT * FROM screen_definitions WHERE screen_id = ANY($1)`, - [screenIdArray] - ); - const screenDefMap = new Map(); - for (const def of allScreenDefsResult.rows) { - screenDefMap.set(def.screen_id, def); - } - - // ๋Œ€์ƒ ํšŒ์‚ฌ์˜ ๊ธฐ์กด ๋ณต์‚ฌ๋ณธ ๋ฐฐ์น˜ ์กฐํšŒ (source_screen_id ๊ธฐ์ค€) - const existingCopiesResult = await client.query<{ - screen_id: number; - screen_name: string; - source_screen_id: number; - updated_date: Date; - }>( - `SELECT screen_id, screen_name, source_screen_id, updated_date - FROM screen_definitions - WHERE source_screen_id = ANY($1) AND company_code = $2 AND deleted_date IS NULL`, - [screenIdArray, targetCompanyCode] - ); - const existingCopyMap = new Map< - number, - { screen_id: number; screen_name: string; updated_date: Date } - >(); - for (const copy of existingCopiesResult.rows) { - existingCopyMap.set(copy.source_screen_id, copy); - } - // === 1๋‹จ๊ณ„: ๋ชจ๋“  screen_definitions ์ฒ˜๋ฆฌ (screenIdMap ์ƒ์„ฑ) === const screenDefsToProcess: Array<{ originalScreenId: number; @@ -1338,20 +1201,35 @@ export class MenuCopyService { for (const originalScreenId of screenIds) { try { - // 1) ์›๋ณธ screen_definitions ์กฐํšŒ (์บ์‹œ์—์„œ) - const screenDef = screenDefMap.get(originalScreenId); + // 1) ์›๋ณธ screen_definitions ์กฐํšŒ + const screenDefResult = await client.query( + `SELECT * FROM screen_definitions WHERE screen_id = $1`, + [originalScreenId] + ); - if (!screenDef) { + if (screenDefResult.rows.length === 0) { logger.warn(`โš ๏ธ ํ™”๋ฉด์„ ์ฐพ์„ ์ˆ˜ ์—†์Œ: screen_id=${originalScreenId}`); continue; } - // 2) ๊ธฐ์กด ๋ณต์‚ฌ๋ณธ ์ฐพ๊ธฐ: ์บ์‹œ์—์„œ ์กฐํšŒ (source_screen_id ๊ธฐ์ค€) - let existingCopy = existingCopyMap.get(originalScreenId); + const screenDef = screenDefResult.rows[0]; + + // 2) ๊ธฐ์กด ๋ณต์‚ฌ๋ณธ ์ฐพ๊ธฐ: source_screen_id๋กœ ๊ฒ€์ƒ‰ + let existingCopyResult = await client.query<{ + screen_id: number; + screen_name: string; + updated_date: Date; + }>( + `SELECT screen_id, screen_name, updated_date + FROM screen_definitions + WHERE source_screen_id = $1 AND company_code = $2 AND deleted_date IS NULL + LIMIT 1`, + [originalScreenId, targetCompanyCode] + ); // 2-1) source_screen_id๊ฐ€ ์—†๋Š” ๊ธฐ์กด ๋ณต์‚ฌ๋ณธ (์ด๋ฆ„ + ํ…Œ์ด๋ธ”๋กœ ๊ฒ€์ƒ‰) - ํ˜ธํ™˜์„ฑ ์œ ์ง€ - if (!existingCopy && screenDef.screen_name) { - const legacyCopyResult = await client.query<{ + if (existingCopyResult.rows.length === 0 && screenDef.screen_name) { + existingCopyResult = await client.query<{ screen_id: number; screen_name: string; updated_date: Date; @@ -1367,15 +1245,14 @@ export class MenuCopyService { [screenDef.screen_name, screenDef.table_name, targetCompanyCode] ); - if (legacyCopyResult.rows.length > 0) { - existingCopy = legacyCopyResult.rows[0]; + if (existingCopyResult.rows.length > 0) { // ๊ธฐ์กด ๋ณต์‚ฌ๋ณธ์— source_screen_id ์—…๋ฐ์ดํŠธ (๋งˆ์ด๊ทธ๋ ˆ์ด์…˜) await client.query( `UPDATE screen_definitions SET source_screen_id = $1 WHERE screen_id = $2`, - [originalScreenId, existingCopy.screen_id] + [originalScreenId, existingCopyResult.rows[0].screen_id] ); logger.info( - ` ๐Ÿ“ ๊ธฐ์กด ํ™”๋ฉด์— source_screen_id ์ถ”๊ฐ€: ${existingCopy.screen_id} โ† ${originalScreenId}` + ` ๐Ÿ“ ๊ธฐ์กด ํ™”๋ฉด์— source_screen_id ์ถ”๊ฐ€: ${existingCopyResult.rows[0].screen_id} โ† ${originalScreenId}` ); } } @@ -1396,9 +1273,10 @@ export class MenuCopyService { } } - if (existingCopy) { + if (existingCopyResult.rows.length > 0) { // === ๊ธฐ์กด ๋ณต์‚ฌ๋ณธ์ด ์žˆ๋Š” ๊ฒฝ์šฐ: ์—…๋ฐ์ดํŠธ === - const existingScreenId = existingCopy.screen_id; + const existingScreen = existingCopyResult.rows[0]; + const existingScreenId = existingScreen.screen_id; // ์›๋ณธ ๋ ˆ์ด์•„์›ƒ ์กฐํšŒ const sourceLayoutsResult = await client.query( @@ -1512,7 +1390,10 @@ export class MenuCopyService { }); } } catch (error: any) { - logger.error(`โŒ ํ™”๋ฉด ์ฒ˜๋ฆฌ ์‹คํŒจ: screen_id=${originalScreenId}`, error); + logger.error( + `โŒ ํ™”๋ฉด ์ฒ˜๋ฆฌ ์‹คํŒจ: screen_id=${originalScreenId}`, + error + ); throw error; } } @@ -1546,39 +1427,36 @@ export class MenuCopyService { // component_id ๋งคํ•‘ ์ƒ์„ฑ (์›๋ณธ โ†’ ์ƒˆ ID) const componentIdMap = new Map(); - const timestamp = Date.now(); - layoutsResult.rows.forEach((layout, idx) => { - const newComponentId = `comp_${timestamp}_${idx}_${Math.random().toString(36).substr(2, 5)}`; + for (const layout of layoutsResult.rows) { + const newComponentId = `comp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; componentIdMap.set(layout.component_id, newComponentId); - }); + } - // ๋ ˆ์ด์•„์›ƒ ๋ฐฐ์น˜ ์‚ฝ์ž… ์ค€๋น„ - if (layoutsResult.rows.length > 0) { - const layoutValues: string[] = []; - const layoutParams: any[] = []; - let paramIdx = 1; + // ๋ ˆ์ด์•„์›ƒ ์‚ฝ์ž… + for (const layout of layoutsResult.rows) { + const newComponentId = componentIdMap.get(layout.component_id)!; - for (const layout of layoutsResult.rows) { - const newComponentId = componentIdMap.get(layout.component_id)!; + const newParentId = layout.parent_id + ? componentIdMap.get(layout.parent_id) || layout.parent_id + : null; + const newZoneId = layout.zone_id + ? componentIdMap.get(layout.zone_id) || layout.zone_id + : null; - const newParentId = layout.parent_id - ? componentIdMap.get(layout.parent_id) || layout.parent_id - : null; - const newZoneId = layout.zone_id - ? componentIdMap.get(layout.zone_id) || layout.zone_id - : null; + const updatedProperties = this.updateReferencesInProperties( + layout.properties, + screenIdMap, + flowIdMap, + numberingRuleIdMap + ); - const updatedProperties = this.updateReferencesInProperties( - layout.properties, - screenIdMap, - flowIdMap, - numberingRuleIdMap - ); - - layoutValues.push( - `($${paramIdx}, $${paramIdx + 1}, $${paramIdx + 2}, $${paramIdx + 3}, $${paramIdx + 4}, $${paramIdx + 5}, $${paramIdx + 6}, $${paramIdx + 7}, $${paramIdx + 8}, $${paramIdx + 9}, $${paramIdx + 10}, $${paramIdx + 11}, $${paramIdx + 12}, $${paramIdx + 13})` - ); - layoutParams.push( + await client.query( + `INSERT INTO screen_layouts ( + screen_id, component_type, component_id, parent_id, + position_x, position_y, width, height, properties, + display_order, layout_type, layout_config, zones_config, zone_id + ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)`, + [ targetScreenId, layout.component_type, newComponentId, @@ -1592,19 +1470,8 @@ export class MenuCopyService { layout.layout_type, layout.layout_config, layout.zones_config, - newZoneId - ); - paramIdx += 14; - } - - // ๋ฐฐ์น˜ INSERT - await client.query( - `INSERT INTO screen_layouts ( - screen_id, component_type, component_id, parent_id, - position_x, position_y, width, height, properties, - display_order, layout_type, layout_config, zones_config, zone_id - ) VALUES ${layoutValues.join(", ")}`, - layoutParams + newZoneId, + ] ); } @@ -1764,7 +1631,7 @@ export class MenuCopyService { const parentMenu = parentMenuResult.rows[0]; // ๋Œ€์ƒ ํšŒ์‚ฌ์—์„œ ๊ฐ™์€ ์ด๋ฆ„ + ๊ฐ™์€ ์›๋ณธ ํšŒ์‚ฌ์—์„œ ๋ณต์‚ฌ๋œ ๋ฉ”๋‰ด ์ฐพ๊ธฐ - // source_menu_objid๊ฐ€ ์žˆ๋Š” ๋ฉ”๋‰ด(๋ณต์‚ฌ๋œ ๋ฉ”๋‰ด)๋งŒ ๋Œ€์ƒ์œผ๋กœ, + // source_menu_objid๊ฐ€ ์žˆ๋Š” ๋ฉ”๋‰ด(๋ณต์‚ฌ๋œ ๋ฉ”๋‰ด)๋งŒ ๋Œ€์ƒ์œผ๋กœ, // ํ•ด๋‹น source_menu_objid์˜ ์›๋ณธ ๋ฉ”๋‰ด๊ฐ€ ๊ฐ™์€ ํšŒ์‚ฌ(sourceCompanyCode)์— ์†ํ•˜๋Š”์ง€ ํ™•์ธ const sameNameResult = await client.query<{ objid: number }>( `SELECT m.objid FROM menu_info m @@ -1819,10 +1686,7 @@ export class MenuCopyService { try { // 0. ์ด๋ฏธ ๋ณต์‚ฌ๋œ ๋ฉ”๋‰ด๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ (๊ณ ์•„ ๋ฉ”๋‰ด ์žฌ์—ฐ๊ฒฐ์šฉ) // 1์ฐจ: source_menu_objid๋กœ ๊ฒ€์ƒ‰ - let existingCopyResult = await client.query<{ - objid: number; - parent_obj_id: number | null; - }>( + let existingCopyResult = await client.query<{ objid: number; parent_obj_id: number | null }>( `SELECT objid, parent_obj_id FROM menu_info WHERE source_menu_objid = $1 AND company_code = $2 LIMIT 1`, @@ -1831,10 +1695,7 @@ export class MenuCopyService { // 2์ฐจ: source_menu_objid๊ฐ€ ์—†๋Š” ๊ธฐ์กด ๋ณต์‚ฌ๋ณธ (์ด๋ฆ„ + ๋ฉ”๋‰ดํƒ€์ž…์œผ๋กœ ๊ฒ€์ƒ‰) - ํ˜ธํ™˜์„ฑ ์œ ์ง€ if (existingCopyResult.rows.length === 0 && menu.menu_name_kor) { - existingCopyResult = await client.query<{ - objid: number; - parent_obj_id: number | null; - }>( + existingCopyResult = await client.query<{ objid: number; parent_obj_id: number | null }>( `SELECT objid, parent_obj_id FROM menu_info WHERE menu_name_kor = $1 AND company_code = $2 @@ -1915,9 +1776,7 @@ export class MenuCopyService { // === ์‹ ๊ทœ ๋ฉ”๋‰ด ๋ณต์‚ฌ === // ๋ฏธ๋ฆฌ ํ• ๋‹น๋œ ID๊ฐ€ ์žˆ์œผ๋ฉด ์‚ฌ์šฉ, ์—†์œผ๋ฉด ์ƒˆ๋กœ ์ƒ์„ฑ - const newObjId = - preAllocatedMenuIdMap?.get(menu.objid) ?? - (await this.getNextMenuObjid(client)); + const newObjId = preAllocatedMenuIdMap?.get(menu.objid) ?? await this.getNextMenuObjid(client); // source_menu_objid ์ €์žฅ: ๋ชจ๋“  ๋ณต์‚ฌ๋œ ๋ฉ”๋‰ด์— ์›๋ณธ ID ์ €์žฅ (์ถ”์ ์šฉ) const sourceMenuObjid = menu.objid; @@ -1991,15 +1850,15 @@ export class MenuCopyService { // === ์ตœ์ ํ™”: ๋ฐฐ์น˜ ์กฐํšŒ === // 1. ๋ชจ๋“  ์›๋ณธ ๋ฉ”๋‰ด์˜ ํ™”๋ฉด ํ• ๋‹น ํ•œ ๋ฒˆ์— ์กฐํšŒ - const menuObjids = menus.map((m) => m.objid); - const companyCodes = [...new Set(menus.map((m) => m.company_code))]; + const menuObjids = menus.map(m => m.objid); + const companyCodes = [...new Set(menus.map(m => m.company_code))]; const allAssignmentsResult = await client.query<{ menu_objid: number; - screen_id: number; - display_order: number; - is_active: string; - }>( + screen_id: number; + display_order: number; + is_active: string; + }>( `SELECT menu_objid, screen_id, display_order, is_active FROM screen_menu_assignments WHERE menu_objid = ANY($1) AND company_code = ANY($2)`, @@ -2021,45 +1880,36 @@ export class MenuCopyService { for (const assignment of allAssignmentsResult.rows) { const newMenuObjid = menuIdMap.get(assignment.menu_objid); - const newScreenId = screenIdMap.get(assignment.screen_id); + const newScreenId = screenIdMap.get(assignment.screen_id); if (!newMenuObjid || !newScreenId) { if (!newScreenId) { - logger.warn( - `โš ๏ธ ํ™”๋ฉด ID ๋งคํ•‘ ์—†์Œ: screen_id=${assignment.screen_id}` - ); + logger.warn(`โš ๏ธ ํ™”๋ฉด ID ๋งคํ•‘ ์—†์Œ: screen_id=${assignment.screen_id}`); + } + continue; } - continue; - } validAssignments.push({ newScreenId, newMenuObjid, displayOrder: assignment.display_order, - isActive: assignment.is_active, + isActive: assignment.is_active }); } // 3. ๋ฐฐ์น˜ INSERT if (validAssignments.length > 0) { - const assignmentValues = validAssignments - .map( - (_, i) => - `($${i * 6 + 1}, $${i * 6 + 2}, $${i * 6 + 3}, $${i * 6 + 4}, $${i * 6 + 5}, $${i * 6 + 6})` - ) - .join(", "); + const assignmentValues = validAssignments.map((_, i) => + `($${i * 6 + 1}, $${i * 6 + 2}, $${i * 6 + 3}, $${i * 6 + 4}, $${i * 6 + 5}, $${i * 6 + 6})` + ).join(", "); - const assignmentParams = validAssignments.flatMap((a) => [ - a.newScreenId, - a.newMenuObjid, - targetCompanyCode, - a.displayOrder, - a.isActive, - "system", + const assignmentParams = validAssignments.flatMap(a => [ + a.newScreenId, a.newMenuObjid, targetCompanyCode, + a.displayOrder, a.isActive, "system" ]); - await client.query( - `INSERT INTO screen_menu_assignments ( + await client.query( + `INSERT INTO screen_menu_assignments ( screen_id, menu_objid, company_code, display_order, is_active, created_by ) VALUES ${assignmentValues}`, assignmentParams @@ -2099,42 +1949,30 @@ export class MenuCopyService { } // 2. ๋Œ€์ƒ ํšŒ์‚ฌ์— ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์นดํ…Œ๊ณ ๋ฆฌ ํ•œ ๋ฒˆ์— ์กฐํšŒ - const categoryCodes = allCategoriesResult.rows.map((c) => c.category_code); + const categoryCodes = allCategoriesResult.rows.map(c => c.category_code); const existingCategoriesResult = await client.query( `SELECT category_code FROM code_category WHERE category_code = ANY($1) AND company_code = $2`, [categoryCodes, targetCompanyCode] ); - const existingCategoryCodes = new Set( - existingCategoriesResult.rows.map((c) => c.category_code) - ); + const existingCategoryCodes = new Set(existingCategoriesResult.rows.map(c => c.category_code)); // 3. ๋ณต์‚ฌํ•  ์นดํ…Œ๊ณ ๋ฆฌ ํ•„ํ„ฐ๋ง const categoriesToCopy = allCategoriesResult.rows.filter( - (c) => !existingCategoryCodes.has(c.category_code) + c => !existingCategoryCodes.has(c.category_code) ); // 4. ๋ฐฐ์น˜ INSERT๋กœ ์นดํ…Œ๊ณ ๋ฆฌ ๋ณต์‚ฌ if (categoriesToCopy.length > 0) { - const categoryValues = categoriesToCopy - .map( - (_, i) => - `($${i * 9 + 1}, $${i * 9 + 2}, $${i * 9 + 3}, $${i * 9 + 4}, $${i * 9 + 5}, $${i * 9 + 6}, NOW(), $${i * 9 + 7}, $${i * 9 + 8}, $${i * 9 + 9})` - ) - .join(", "); + const categoryValues = categoriesToCopy.map((_, i) => + `($${i * 9 + 1}, $${i * 9 + 2}, $${i * 9 + 3}, $${i * 9 + 4}, $${i * 9 + 5}, $${i * 9 + 6}, NOW(), $${i * 9 + 7}, $${i * 9 + 8}, $${i * 9 + 9})` + ).join(", "); - const categoryParams = categoriesToCopy.flatMap((c) => { + const categoryParams = categoriesToCopy.flatMap(c => { const newMenuObjid = menuIdMap.get(c.menu_objid); return [ - c.category_code, - c.category_name, - c.category_name_eng, - c.description, - c.sort_order, - c.is_active, - userId, - targetCompanyCode, - newMenuObjid, + c.category_code, c.category_name, c.category_name_eng, c.description, + c.sort_order, c.is_active, userId, targetCompanyCode, newMenuObjid ]; }); @@ -2168,36 +2006,25 @@ export class MenuCopyService { [Array.from(menuIdMap.values()), targetCompanyCode] ); const existingCodeKeys = new Set( - existingCodesResult.rows.map((c) => `${c.code_category}|${c.code_value}`) + existingCodesResult.rows.map(c => `${c.code_category}|${c.code_value}`) ); // 7. ๋ณต์‚ฌํ•  ์ฝ”๋“œ ํ•„ํ„ฐ๋ง const codesToCopy = allCodesResult.rows.filter( - (c) => !existingCodeKeys.has(`${c.code_category}|${c.code_value}`) + c => !existingCodeKeys.has(`${c.code_category}|${c.code_value}`) ); // 8. ๋ฐฐ์น˜ INSERT๋กœ ์ฝ”๋“œ ๋ณต์‚ฌ if (codesToCopy.length > 0) { - const codeValues = codesToCopy - .map( - (_, i) => - `($${i * 10 + 1}, $${i * 10 + 2}, $${i * 10 + 3}, $${i * 10 + 4}, $${i * 10 + 5}, $${i * 10 + 6}, $${i * 10 + 7}, NOW(), $${i * 10 + 8}, $${i * 10 + 9}, $${i * 10 + 10})` - ) - .join(", "); + const codeValues = codesToCopy.map((_, i) => + `($${i * 10 + 1}, $${i * 10 + 2}, $${i * 10 + 3}, $${i * 10 + 4}, $${i * 10 + 5}, $${i * 10 + 6}, $${i * 10 + 7}, NOW(), $${i * 10 + 8}, $${i * 10 + 9}, $${i * 10 + 10})` + ).join(", "); - const codeParams = codesToCopy.flatMap((c) => { + const codeParams = codesToCopy.flatMap(c => { const newMenuObjid = menuIdMap.get(c.menu_objid); return [ - c.code_category, - c.code_value, - c.code_name, - c.code_name_eng, - c.description, - c.sort_order, - c.is_active, - userId, - targetCompanyCode, - newMenuObjid, + c.code_category, c.code_value, c.code_name, c.code_name_eng, c.description, + c.sort_order, c.is_active, userId, targetCompanyCode, newMenuObjid ]; }); @@ -2213,107 +2040,10 @@ export class MenuCopyService { logger.info(` โœ… ์ฝ”๋“œ ${copiedCodes}๊ฐœ ๋ณต์‚ฌ`); } - logger.info( - `โœ… ์ฝ”๋“œ ์นดํ…Œ๊ณ ๋ฆฌ + ์ฝ”๋“œ ๋ณต์‚ฌ ์™„๋ฃŒ: ์นดํ…Œ๊ณ ๋ฆฌ ${copiedCategories}๊ฐœ, ์ฝ”๋“œ ${copiedCodes}๊ฐœ` - ); + logger.info(`โœ… ์ฝ”๋“œ ์นดํ…Œ๊ณ ๋ฆฌ + ์ฝ”๋“œ ๋ณต์‚ฌ ์™„๋ฃŒ: ์นดํ…Œ๊ณ ๋ฆฌ ${copiedCategories}๊ฐœ, ์ฝ”๋“œ ${copiedCodes}๊ฐœ`); return { copiedCategories, copiedCodes }; } - /** - * ํ™”๋ฉด์—์„œ ์ฐธ์กฐํ•˜๋Š” ์ฑ„๋ฒˆ๊ทœ์น™ ๋งคํ•‘ ๋ณด์™„ - * ํ™”๋ฉด properties์—์„œ ์ฐธ์กฐํ•˜๋Š” ์ฑ„๋ฒˆ๊ทœ์น™ ์ค‘ ์•„์ง ๋งคํ•‘๋˜์ง€ ์•Š์€ ๊ฒƒ๋“ค์„ - * ๋Œ€์ƒ ํšŒ์‚ฌ์—์„œ ๊ฐ™์€ ์ด๋ฆ„(rule_name)์˜ ์ฑ„๋ฒˆ๊ทœ์น™์œผ๋กœ ๋งคํ•‘ - */ - private async supplementNumberingRuleMapping( - screenIds: number[], - sourceCompanyCode: string, - targetCompanyCode: string, - numberingRuleIdMap: Map, - client: PoolClient - ): Promise { - if (screenIds.length === 0) return; - - // 1. ํ™”๋ฉด ๋ ˆ์ด์•„์›ƒ์—์„œ ๋ชจ๋“  ์ฑ„๋ฒˆ๊ทœ์น™ ID ์ถ”์ถœ - const layoutsResult = await client.query( - `SELECT properties::text as props FROM screen_layouts WHERE screen_id = ANY($1)`, - [screenIds] - ); - - const referencedRuleIds = new Set(); - const ruleIdRegex = /"numberingRuleId"\s*:\s*"([^"]+)"/g; - - for (const row of layoutsResult.rows) { - if (!row.props) continue; - let match; - while ((match = ruleIdRegex.exec(row.props)) !== null) { - const ruleId = match[1]; - // ์ด๋ฏธ ๋งคํ•‘๋œ ๊ฒƒ์€ ์ œ์™ธ - if (ruleId && !numberingRuleIdMap.has(ruleId)) { - referencedRuleIds.add(ruleId); - } - } - } - - if (referencedRuleIds.size === 0) { - logger.info(` ๐Ÿ“ญ ์ถ”๊ฐ€ ๋งคํ•‘ ํ•„์š” ์—†์Œ`); - return; - } - - logger.info(` ๐Ÿ” ๋งคํ•‘ ํ•„์š”ํ•œ ์ฑ„๋ฒˆ๊ทœ์น™: ${referencedRuleIds.size}๊ฐœ`); - - // 2. ์›๋ณธ ์ฑ„๋ฒˆ๊ทœ์น™ ์ •๋ณด ์กฐํšŒ (rule_name์œผ๋กœ ๋Œ€์ƒ ํšŒ์‚ฌ์—์„œ ์ฐพ๊ธฐ ์œ„ํ•ด) - const sourceRulesResult = await client.query( - `SELECT rule_id, rule_name, table_name FROM numbering_rules - WHERE rule_id = ANY($1)`, - [Array.from(referencedRuleIds)] - ); - - if (sourceRulesResult.rows.length === 0) { - logger.warn( - ` โš ๏ธ ์›๋ณธ ์ฑ„๋ฒˆ๊ทœ์น™ ์กฐํšŒ ์‹คํŒจ: ${Array.from(referencedRuleIds).join(", ")}` - ); - return; - } - - // 3. ๋Œ€์ƒ ํšŒ์‚ฌ์—์„œ ๊ฐ™์€ ์ด๋ฆ„์˜ ์ฑ„๋ฒˆ๊ทœ์น™ ์ฐพ๊ธฐ - const ruleNames = sourceRulesResult.rows.map((r) => r.rule_name); - const targetRulesResult = await client.query( - `SELECT rule_id, rule_name, table_name FROM numbering_rules - WHERE rule_name = ANY($1) AND company_code = $2`, - [ruleNames, targetCompanyCode] - ); - - // rule_name -> target_rule_id ๋งคํ•‘ - const targetRulesByName = new Map(); - for (const r of targetRulesResult.rows) { - // ๊ฐ™์€ ์ด๋ฆ„์ด ์—ฌ๋Ÿฌ ๊ฐœ์ผ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ฒซ ๋ฒˆ์งธ๋งŒ ์‚ฌ์šฉ - if (!targetRulesByName.has(r.rule_name)) { - targetRulesByName.set(r.rule_name, r.rule_id); - } - } - - // 4. ๋งคํ•‘ ์ถ”๊ฐ€ - let mappedCount = 0; - for (const sourceRule of sourceRulesResult.rows) { - const targetRuleId = targetRulesByName.get(sourceRule.rule_name); - if (targetRuleId) { - numberingRuleIdMap.set(sourceRule.rule_id, targetRuleId); - logger.info( - ` โœ… ์ฑ„๋ฒˆ๊ทœ์น™ ๋งคํ•‘ ์ถ”๊ฐ€: ${sourceRule.rule_id} (${sourceRule.rule_name}) โ†’ ${targetRuleId}` - ); - mappedCount++; - } else { - logger.warn( - ` โš ๏ธ ๋Œ€์ƒ ํšŒ์‚ฌ์— ๊ฐ™์€ ์ด๋ฆ„์˜ ์ฑ„๋ฒˆ๊ทœ์น™ ์—†์Œ: ${sourceRule.rule_name}` - ); - } - } - - logger.info( - ` โœ… ์ฑ„๋ฒˆ๊ทœ์น™ ๋งคํ•‘ ๋ณด์™„ ์™„๋ฃŒ: ${mappedCount}/${referencedRuleIds.size}๊ฐœ` - ); - } - /** * ์ฑ„๋ฒˆ ๊ทœ์น™ ๋ณต์‚ฌ (์ตœ์ ํ™”: ๋ฐฐ์น˜ ์กฐํšŒ/์‚ฝ์ž…) * ํ™”๋ฉด ๋ณต์‚ฌ ์ „์— ํ˜ธ์ถœ๋˜์–ด numberingRuleId ์ฐธ์กฐ ์—…๋ฐ์ดํŠธ์— ์‚ฌ์šฉ๋จ @@ -2344,44 +2074,32 @@ export class MenuCopyService { return { copiedCount, ruleIdMap }; } - // 2. ๋Œ€์ƒ ํšŒ์‚ฌ์— ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์ฑ„๋ฒˆ ๊ทœ์น™ ํ•œ ๋ฒˆ์— ์กฐํšŒ - const ruleIds = allRulesResult.rows.map((r) => r.rule_id); + // 2. ๋Œ€์ƒ ํšŒ์‚ฌ์— ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ๋ชจ๋“  ์ฑ„๋ฒˆ ๊ทœ์น™ ์กฐํšŒ (์›๋ณธ ID + ์ƒˆ๋กœ ์ƒ์„ฑ๋  ID ๋ชจ๋‘ ์ฒดํฌ) const existingRulesResult = await client.query( - `SELECT rule_id FROM numbering_rules - WHERE rule_id = ANY($1) AND company_code = $2`, - [ruleIds, targetCompanyCode] - ); - const existingRuleIds = new Set( - existingRulesResult.rows.map((r) => r.rule_id) + `SELECT rule_id FROM numbering_rules WHERE company_code = $1`, + [targetCompanyCode] ); + const existingRuleIds = new Set(existingRulesResult.rows.map(r => r.rule_id)); // 3. ๋ณต์‚ฌํ•  ๊ทœ์น™๊ณผ ์Šคํ‚ตํ•  ๊ทœ์น™ ๋ถ„๋ฅ˜ const rulesToCopy: any[] = []; const originalToNewRuleMap: Array<{ original: string; new: string }> = []; - // ๊ธฐ์กด ๊ทœ์น™ ์ค‘ menu_objid ์—…๋ฐ์ดํŠธ๊ฐ€ ํ•„์š”ํ•œ ๊ทœ์น™๋“ค - const rulesToUpdate: Array<{ ruleId: string; newMenuObjid: number }> = []; - for (const rule of allRulesResult.rows) { + // ์ƒˆ rule_id ์ƒ์„ฑ + const originalSuffix = rule.rule_id.includes('_') + ? rule.rule_id.replace(/^[^_]*_/, '') + : rule.rule_id; + const newRuleId = `${targetCompanyCode}_${originalSuffix}`; + + // ์›๋ณธ ID ๋˜๋Š” ์ƒˆ๋กœ ์ƒ์„ฑ๋  ID๊ฐ€ ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ ์Šคํ‚ต if (existingRuleIds.has(rule.rule_id)) { - // ๊ธฐ์กด ๊ทœ์น™์€ ๋™์ผํ•œ ID๋กœ ๋งคํ•‘ ruleIdMap.set(rule.rule_id, rule.rule_id); - - // ์ƒˆ ๋ฉ”๋‰ด ID๋กœ ์—ฐ๊ฒฐ ์—…๋ฐ์ดํŠธ ํ•„์š” - const newMenuObjid = menuIdMap.get(rule.menu_objid); - if (newMenuObjid) { - rulesToUpdate.push({ ruleId: rule.rule_id, newMenuObjid }); - } - logger.info( - ` โ™ป๏ธ ์ฑ„๋ฒˆ๊ทœ์น™ ์ด๋ฏธ ์กด์žฌ (๋ฉ”๋‰ด ์—ฐ๊ฒฐ ๊ฐฑ์‹ ): ${rule.rule_id}` - ); + logger.info(` โ™ป๏ธ ์ฑ„๋ฒˆ๊ทœ์น™ ์ด๋ฏธ ์กด์žฌ (์›๋ณธ ID): ${rule.rule_id}`); + } else if (existingRuleIds.has(newRuleId)) { + ruleIdMap.set(rule.rule_id, newRuleId); + logger.info(` โ™ป๏ธ ์ฑ„๋ฒˆ๊ทœ์น™ ์ด๋ฏธ ์กด์žฌ (๋Œ€์ƒ ID): ${newRuleId}`); } else { - // ์ƒˆ rule_id ์ƒ์„ฑ - const originalSuffix = rule.rule_id.includes("_") - ? rule.rule_id.replace(/^[^_]*_/, "") - : rule.rule_id; - const newRuleId = `${targetCompanyCode}_${originalSuffix}`; - ruleIdMap.set(rule.rule_id, newRuleId); originalToNewRuleMap.push({ original: rule.rule_id, new: newRuleId }); rulesToCopy.push({ ...rule, newRuleId }); @@ -2390,29 +2108,16 @@ export class MenuCopyService { // 4. ๋ฐฐ์น˜ INSERT๋กœ ์ฑ„๋ฒˆ ๊ทœ์น™ ๋ณต์‚ฌ if (rulesToCopy.length > 0) { - const ruleValues = rulesToCopy - .map( - (_, i) => - `($${i * 13 + 1}, $${i * 13 + 2}, $${i * 13 + 3}, $${i * 13 + 4}, $${i * 13 + 5}, $${i * 13 + 6}, $${i * 13 + 7}, $${i * 13 + 8}, $${i * 13 + 9}, NOW(), $${i * 13 + 10}, $${i * 13 + 11}, $${i * 13 + 12}, $${i * 13 + 13})` - ) - .join(", "); + const ruleValues = rulesToCopy.map((_, i) => + `($${i * 13 + 1}, $${i * 13 + 2}, $${i * 13 + 3}, $${i * 13 + 4}, $${i * 13 + 5}, $${i * 13 + 6}, $${i * 13 + 7}, $${i * 13 + 8}, $${i * 13 + 9}, NOW(), $${i * 13 + 10}, $${i * 13 + 11}, $${i * 13 + 12}, $${i * 13 + 13})` + ).join(", "); - const ruleParams = rulesToCopy.flatMap((r) => { + const ruleParams = rulesToCopy.flatMap(r => { const newMenuObjid = menuIdMap.get(r.menu_objid); return [ - r.newRuleId, - r.rule_name, - r.description, - r.separator, - r.reset_period, - 0, - r.table_name, - r.column_name, - targetCompanyCode, - userId, - newMenuObjid, - r.scope_type, - null, + r.newRuleId, r.rule_name, r.description, r.separator, r.reset_period, + 0, r.table_name, r.column_name, targetCompanyCode, + userId, newMenuObjid, r.scope_type, null ]; }); @@ -2427,31 +2132,9 @@ export class MenuCopyService { copiedCount = rulesToCopy.length; logger.info(` โœ… ์ฑ„๋ฒˆ ๊ทœ์น™ ${copiedCount}๊ฐœ ๋ณต์‚ฌ`); - } - // 4-1. ๊ธฐ์กด ์ฑ„๋ฒˆ ๊ทœ์น™์˜ menu_objid ์—…๋ฐ์ดํŠธ (์ƒˆ ๋ฉ”๋‰ด์™€ ์—ฐ๊ฒฐ) - ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ - if (rulesToUpdate.length > 0) { - // CASE WHEN์„ ์‚ฌ์šฉํ•œ ๋ฐฐ์น˜ ์—…๋ฐ์ดํŠธ - const caseWhen = rulesToUpdate - .map((_, i) => `WHEN rule_id = $${i * 2 + 1} THEN $${i * 2 + 2}`) - .join(" "); - const ruleIdsForUpdate = rulesToUpdate.map((r) => r.ruleId); - const params = rulesToUpdate.flatMap((r) => [r.ruleId, r.newMenuObjid]); - - await client.query( - `UPDATE numbering_rules - SET menu_objid = CASE ${caseWhen} END, updated_at = NOW() - WHERE rule_id = ANY($${params.length + 1}) AND company_code = $${params.length + 2}`, - [...params, ruleIdsForUpdate, targetCompanyCode] - ); - logger.info( - ` โœ… ๊ธฐ์กด ์ฑ„๋ฒˆ ๊ทœ์น™ ${rulesToUpdate.length}๊ฐœ ๋ฉ”๋‰ด ์—ฐ๊ฒฐ ๊ฐฑ์‹ ` - ); - } - - // 5. ๋ชจ๋“  ์›๋ณธ ํŒŒํŠธ ํ•œ ๋ฒˆ์— ์กฐํšŒ (์ƒˆ๋กœ ๋ณต์‚ฌํ•œ ๊ทœ์น™๋งŒ ๋Œ€์ƒ) - if (rulesToCopy.length > 0) { - const originalRuleIds = rulesToCopy.map((r) => r.rule_id); + // 5. ๋ชจ๋“  ์›๋ณธ ํŒŒํŠธ ํ•œ ๋ฒˆ์— ์กฐํšŒ + const originalRuleIds = rulesToCopy.map(r => r.rule_id); const allPartsResult = await client.query( `SELECT * FROM numbering_rule_parts WHERE rule_id = ANY($1) ORDER BY rule_id, part_order`, @@ -2461,25 +2144,15 @@ export class MenuCopyService { // 6. ๋ฐฐ์น˜ INSERT๋กœ ์ฑ„๋ฒˆ ๊ทœ์น™ ํŒŒํŠธ ๋ณต์‚ฌ if (allPartsResult.rows.length > 0) { // ์›๋ณธ rule_id -> ์ƒˆ rule_id ๋งคํ•‘ - const ruleMapping = new Map( - originalToNewRuleMap.map((m) => [m.original, m.new]) - ); + const ruleMapping = new Map(originalToNewRuleMap.map(m => [m.original, m.new])); - const partValues = allPartsResult.rows - .map( - (_, i) => - `($${i * 7 + 1}, $${i * 7 + 2}, $${i * 7 + 3}, $${i * 7 + 4}, $${i * 7 + 5}, $${i * 7 + 6}, $${i * 7 + 7}, NOW())` - ) - .join(", "); + const partValues = allPartsResult.rows.map((_, i) => + `($${i * 7 + 1}, $${i * 7 + 2}, $${i * 7 + 3}, $${i * 7 + 4}, $${i * 7 + 5}, $${i * 7 + 6}, $${i * 7 + 7}, NOW())` + ).join(", "); - const partParams = allPartsResult.rows.flatMap((p) => [ - ruleMapping.get(p.rule_id), - p.part_order, - p.part_type, - p.generation_method, - p.auto_config, - p.manual_config, - targetCompanyCode, + const partParams = allPartsResult.rows.flatMap(p => [ + ruleMapping.get(p.rule_id), p.part_order, p.part_type, p.generation_method, + p.auto_config, p.manual_config, targetCompanyCode ]); await client.query( @@ -2494,9 +2167,7 @@ export class MenuCopyService { } } - logger.info( - `โœ… ์ฑ„๋ฒˆ ๊ทœ์น™ ๋ณต์‚ฌ ์™„๋ฃŒ: ${copiedCount}๊ฐœ, ๋งคํ•‘: ${ruleIdMap.size}๊ฐœ` - ); + logger.info(`โœ… ์ฑ„๋ฒˆ ๊ทœ์น™ ๋ณต์‚ฌ ์™„๋ฃŒ: ${copiedCount}๊ฐœ, ๋งคํ•‘: ${ruleIdMap.size}๊ฐœ`); return { copiedCount, ruleIdMap }; } @@ -2535,53 +2206,28 @@ export class MenuCopyService { [targetCompanyCode] ); const existingMappingKeys = new Map( - existingMappingsResult.rows.map((m) => [ - `${m.table_name}|${m.logical_column_name}`, - m.mapping_id, - ]) + existingMappingsResult.rows.map(m => [`${m.table_name}|${m.logical_column_name}`, m.mapping_id]) ); - // 3. ๋ณต์‚ฌํ•  ๋งคํ•‘ ํ•„ํ„ฐ๋ง ๋ฐ ๊ธฐ์กด ๋งคํ•‘ ์—…๋ฐ์ดํŠธ ๋Œ€์ƒ ๋ถ„๋ฅ˜ - const mappingsToCopy: any[] = []; - const mappingsToUpdate: Array<{ mappingId: number; newMenuObjid: number }> = - []; - - for (const m of allMappingsResult.rows) { - const key = `${m.table_name}|${m.logical_column_name}`; - if (existingMappingKeys.has(key)) { - // ๊ธฐ์กด ๋งคํ•‘์€ menu_objid๋งŒ ์—…๋ฐ์ดํŠธ - const existingMappingId = existingMappingKeys.get(key); - const newMenuObjid = menuIdMap.get(m.menu_objid); - if (existingMappingId && newMenuObjid) { - mappingsToUpdate.push({ mappingId: existingMappingId, newMenuObjid }); - } - } else { - mappingsToCopy.push(m); - } - } + // 3. ๋ณต์‚ฌํ•  ๋งคํ•‘ ํ•„ํ„ฐ๋ง ๋ฐ ๋ฐฐ์น˜ INSERT + const mappingsToCopy = allMappingsResult.rows.filter( + m => !existingMappingKeys.has(`${m.table_name}|${m.logical_column_name}`) + ); // ์ƒˆ ๋งคํ•‘ ID -> ์›๋ณธ ๋งคํ•‘ ์ •๋ณด ์ถ”์  const mappingInsertInfo: Array<{ mapping: any; newMenuObjid: number }> = []; if (mappingsToCopy.length > 0) { - const mappingValues = mappingsToCopy - .map( - (_, i) => - `($${i * 7 + 1}, $${i * 7 + 2}, $${i * 7 + 3}, $${i * 7 + 4}, $${i * 7 + 5}, $${i * 7 + 6}, NOW(), $${i * 7 + 7})` - ) - .join(", "); + const mappingValues = mappingsToCopy.map((_, i) => + `($${i * 7 + 1}, $${i * 7 + 2}, $${i * 7 + 3}, $${i * 7 + 4}, $${i * 7 + 5}, $${i * 7 + 6}, NOW(), $${i * 7 + 7})` + ).join(", "); - const mappingParams = mappingsToCopy.flatMap((m) => { + const mappingParams = mappingsToCopy.flatMap(m => { const newMenuObjid = menuIdMap.get(m.menu_objid) || 0; mappingInsertInfo.push({ mapping: m, newMenuObjid }); return [ - m.table_name, - m.logical_column_name, - m.physical_column_name, - newMenuObjid, - targetCompanyCode, - m.description, - userId, + m.table_name, m.logical_column_name, m.physical_column_name, + newMenuObjid, targetCompanyCode, m.description, userId ]; }); @@ -2597,39 +2243,13 @@ export class MenuCopyService { // ์ƒˆ๋กœ ์ƒ์„ฑ๋œ ๋งคํ•‘ ID๋ฅผ ๊ธฐ์กด ๋งคํ•‘ ๋งต์— ์ถ”๊ฐ€ insertResult.rows.forEach((row, index) => { const m = mappingsToCopy[index]; - existingMappingKeys.set( - `${m.table_name}|${m.logical_column_name}`, - row.mapping_id - ); + existingMappingKeys.set(`${m.table_name}|${m.logical_column_name}`, row.mapping_id); }); copiedCount = mappingsToCopy.length; logger.info(` โœ… ์นดํ…Œ๊ณ ๋ฆฌ ๋งคํ•‘ ${copiedCount}๊ฐœ ๋ณต์‚ฌ`); } - // 3-1. ๊ธฐ์กด ์นดํ…Œ๊ณ ๋ฆฌ ๋งคํ•‘์˜ menu_objid ์—…๋ฐ์ดํŠธ (์ƒˆ ๋ฉ”๋‰ด์™€ ์—ฐ๊ฒฐ) - ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ - if (mappingsToUpdate.length > 0) { - // CASE WHEN์„ ์‚ฌ์šฉํ•œ ๋ฐฐ์น˜ ์—…๋ฐ์ดํŠธ - const caseWhen = mappingsToUpdate - .map((_, i) => `WHEN mapping_id = $${i * 2 + 1} THEN $${i * 2 + 2}`) - .join(" "); - const mappingIdsForUpdate = mappingsToUpdate.map((m) => m.mappingId); - const params = mappingsToUpdate.flatMap((m) => [ - m.mappingId, - m.newMenuObjid, - ]); - - await client.query( - `UPDATE category_column_mapping - SET menu_objid = CASE ${caseWhen} END - WHERE mapping_id = ANY($${params.length + 1}) AND company_code = $${params.length + 2}`, - [...params, mappingIdsForUpdate, targetCompanyCode] - ); - logger.info( - ` โœ… ๊ธฐ์กด ์นดํ…Œ๊ณ ๋ฆฌ ๋งคํ•‘ ${mappingsToUpdate.length}๊ฐœ ๋ฉ”๋‰ด ์—ฐ๊ฒฐ ๊ฐฑ์‹ ` - ); - } - // 4. ๋ชจ๋“  ์›๋ณธ ์นดํ…Œ๊ณ ๋ฆฌ ๊ฐ’ ํ•œ ๋ฒˆ์— ์กฐํšŒ const allValuesResult = await client.query( `SELECT * FROM table_column_category_values @@ -2650,10 +2270,7 @@ export class MenuCopyService { [targetCompanyCode] ); const existingValueKeys = new Map( - existingValuesResult.rows.map((v) => [ - `${v.table_name}|${v.column_name}|${v.value_code}`, - v.value_id, - ]) + existingValuesResult.rows.map(v => [`${v.table_name}|${v.column_name}|${v.value_code}`, v.value_id]) ); // 6. ๊ฐ’ ๋ณต์‚ฌ (๋ถ€๋ชจ-์ž์‹ ๊ด€๊ณ„ ์œ ์ง€๋ฅผ ์œ„ํ•ด depth ์ˆœ์„œ๋กœ ์ฒ˜๋ฆฌ) @@ -2689,34 +2306,17 @@ export class MenuCopyService { const values = valuesByDepth.get(depth)!; if (values.length === 0) continue; - const valueStrings = values - .map( - (_, i) => - `($${i * 15 + 1}, $${i * 15 + 2}, $${i * 15 + 3}, $${i * 15 + 4}, $${i * 15 + 5}, $${i * 15 + 6}, $${i * 15 + 7}, $${i * 15 + 8}, $${i * 15 + 9}, $${i * 15 + 10}, $${i * 15 + 11}, $${i * 15 + 12}, NOW(), $${i * 15 + 13}, $${i * 15 + 14}, $${i * 15 + 15})` - ) - .join(", "); + const valueStrings = values.map((_, i) => + `($${i * 15 + 1}, $${i * 15 + 2}, $${i * 15 + 3}, $${i * 15 + 4}, $${i * 15 + 5}, $${i * 15 + 6}, $${i * 15 + 7}, $${i * 15 + 8}, $${i * 15 + 9}, $${i * 15 + 10}, $${i * 15 + 11}, $${i * 15 + 12}, NOW(), $${i * 15 + 13}, $${i * 15 + 14}, $${i * 15 + 15})` + ).join(", "); - const valueParams = values.flatMap((v) => { + const valueParams = values.flatMap(v => { const newMenuObjid = menuIdMap.get(v.menu_objid); - const newParentId = v.parent_value_id - ? valueIdMap.get(v.parent_value_id) || null - : null; + const newParentId = v.parent_value_id ? valueIdMap.get(v.parent_value_id) || null : null; return [ - v.table_name, - v.column_name, - v.value_code, - v.value_label, - v.value_order, - newParentId, - v.depth, - v.description, - v.color, - v.icon, - v.is_active, - v.is_default, - userId, - targetCompanyCode, - newMenuObjid, + v.table_name, v.column_name, v.value_code, v.value_label, v.value_order, + newParentId, v.depth, v.description, v.color, v.icon, + v.is_active, v.is_default, userId, targetCompanyCode, newMenuObjid ]; }); @@ -2797,35 +2397,25 @@ export class MenuCopyService { [tableNames, targetCompanyCode] ); const existingKeys = new Set( - existingSettingsResult.rows.map((s) => `${s.table_name}|${s.column_name}`) + existingSettingsResult.rows.map(s => `${s.table_name}|${s.column_name}`) ); // 4. ๋ณต์‚ฌํ•  ์„ค์ • ํ•„ํ„ฐ๋ง const settingsToCopy = sourceSettingsResult.rows.filter( - (s) => !existingKeys.has(`${s.table_name}|${s.column_name}`) + s => !existingKeys.has(`${s.table_name}|${s.column_name}`) ); - logger.info( - ` ์›๋ณธ ์„ค์ •: ${sourceSettingsResult.rows.length}๊ฐœ, ๋ณต์‚ฌ ๋Œ€์ƒ: ${settingsToCopy.length}๊ฐœ` - ); + logger.info(` ์›๋ณธ ์„ค์ •: ${sourceSettingsResult.rows.length}๊ฐœ, ๋ณต์‚ฌ ๋Œ€์ƒ: ${settingsToCopy.length}๊ฐœ`); // 5. ๋ฐฐ์น˜ INSERT if (settingsToCopy.length > 0) { - const settingValues = settingsToCopy - .map( - (_, i) => - `($${i * 7 + 1}, $${i * 7 + 2}, $${i * 7 + 3}, $${i * 7 + 4}, $${i * 7 + 5}, $${i * 7 + 6}, NOW(), NOW(), $${i * 7 + 7})` - ) - .join(", "); + const settingValues = settingsToCopy.map((_, i) => + `($${i * 7 + 1}, $${i * 7 + 2}, $${i * 7 + 3}, $${i * 7 + 4}, $${i * 7 + 5}, $${i * 7 + 6}, NOW(), NOW(), $${i * 7 + 7})` + ).join(", "); - const settingParams = settingsToCopy.flatMap((s) => [ - s.table_name, - s.column_name, - s.input_type, - s.detail_settings, - s.is_nullable, - s.display_order, - targetCompanyCode, + const settingParams = settingsToCopy.flatMap(s => [ + s.table_name, s.column_name, s.input_type, s.detail_settings, + s.is_nullable, s.display_order, targetCompanyCode ]); await client.query( @@ -2875,7 +2465,7 @@ export class MenuCopyService { [targetCompanyCode] ); const existingGroupsByCode = new Map( - existingGroupsResult.rows.map((g) => [g.relation_code, g.group_id]) + existingGroupsResult.rows.map(g => [g.relation_code, g.group_id]) ); // group_id ๋งคํ•‘ @@ -2891,9 +2481,7 @@ export class MenuCopyService { } } - logger.info( - ` ๊ธฐ์กด: ${groupsResult.rows.length - groupsToCopy.length}๊ฐœ, ์‹ ๊ทœ: ${groupsToCopy.length}๊ฐœ` - ); + logger.info(` ๊ธฐ์กด: ${groupsResult.rows.length - groupsToCopy.length}๊ฐœ, ์‹ ๊ทœ: ${groupsToCopy.length}๊ฐœ`); // ๊ทธ๋ฃน๋ณ„๋กœ ์‚ฝ์ž…ํ•˜๊ณ  ๋งคํ•‘ ์ €์žฅ (RETURNING์ด ํ•„์š”ํ•ด์„œ ๋ฐฐ์น˜ ๋ถˆ๊ฐ€) for (const group of groupsToCopy) { @@ -2915,22 +2503,12 @@ export class MenuCopyService { ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, NOW()) RETURNING group_id`, [ - group.relation_code, - group.relation_name, - group.description, - group.parent_table_name, - group.parent_column_name, - newParentMenuObjid, - group.child_table_name, - group.child_column_name, - newChildMenuObjid, - group.clear_on_parent_change, - group.show_group_label, - group.empty_parent_message, - group.no_options_message, - targetCompanyCode, - "Y", - userId, + group.relation_code, group.relation_name, group.description, + group.parent_table_name, group.parent_column_name, newParentMenuObjid, + group.child_table_name, group.child_column_name, newChildMenuObjid, + group.clear_on_parent_change, group.show_group_label, + group.empty_parent_message, group.no_options_message, + targetCompanyCode, "Y", userId ] ); @@ -2940,7 +2518,7 @@ export class MenuCopyService { } // ๋ชจ๋“  ๋งคํ•‘ ํ•œ ๋ฒˆ์— ์กฐํšŒ (๋ณต์‚ฌํ•  ๊ทธ๋ฃน๋งŒ) - const groupIdsToCopy = groupsToCopy.map((g) => g.group_id); + const groupIdsToCopy = groupsToCopy.map(g => g.group_id); if (groupIdsToCopy.length > 0) { const allMappingsResult = await client.query( `SELECT * FROM category_value_cascading_mapping @@ -2951,24 +2529,16 @@ export class MenuCopyService { // ๋ฐฐ์น˜ INSERT if (allMappingsResult.rows.length > 0) { - const mappingValues = allMappingsResult.rows - .map( - (_, i) => - `($${i * 8 + 1}, $${i * 8 + 2}, $${i * 8 + 3}, $${i * 8 + 4}, $${i * 8 + 5}, $${i * 8 + 6}, $${i * 8 + 7}, $${i * 8 + 8}, NOW())` - ) - .join(", "); + const mappingValues = allMappingsResult.rows.map((_, i) => + `($${i * 8 + 1}, $${i * 8 + 2}, $${i * 8 + 3}, $${i * 8 + 4}, $${i * 8 + 5}, $${i * 8 + 6}, $${i * 8 + 7}, $${i * 8 + 8}, NOW())` + ).join(", "); - const mappingParams = allMappingsResult.rows.flatMap((m) => { + const mappingParams = allMappingsResult.rows.flatMap(m => { const newGroupId = groupIdMap.get(m.group_id); return [ - newGroupId, - m.parent_value_code, - m.parent_value_label, - m.child_value_code, - m.child_value_label, - m.display_order, - targetCompanyCode, - "Y", + newGroupId, m.parent_value_code, m.parent_value_label, + m.child_value_code, m.child_value_label, m.display_order, + targetCompanyCode, "Y" ]; }); @@ -3005,47 +2575,29 @@ export class MenuCopyService { [targetCompanyCode] ); const existingRelationCodes = new Set( - existingRelationsResult.rows.map((r) => r.relation_code) + existingRelationsResult.rows.map(r => r.relation_code) ); // ๋ณต์‚ฌํ•  ๊ด€๊ณ„ ํ•„ํ„ฐ๋ง const relationsToCopy = relationsResult.rows.filter( - (r) => !existingRelationCodes.has(r.relation_code) + r => !existingRelationCodes.has(r.relation_code) ); - logger.info( - ` ๊ธฐ์กด: ${relationsResult.rows.length - relationsToCopy.length}๊ฐœ, ์‹ ๊ทœ: ${relationsToCopy.length}๊ฐœ` - ); + logger.info(` ๊ธฐ์กด: ${relationsResult.rows.length - relationsToCopy.length}๊ฐœ, ์‹ ๊ทœ: ${relationsToCopy.length}๊ฐœ`); // ๋ฐฐ์น˜ INSERT if (relationsToCopy.length > 0) { - const relationValues = relationsToCopy - .map( - (_, i) => - `($${i * 19 + 1}, $${i * 19 + 2}, $${i * 19 + 3}, $${i * 19 + 4}, $${i * 19 + 5}, $${i * 19 + 6}, $${i * 19 + 7}, $${i * 19 + 8}, $${i * 19 + 9}, $${i * 19 + 10}, $${i * 19 + 11}, $${i * 19 + 12}, $${i * 19 + 13}, $${i * 19 + 14}, $${i * 19 + 15}, $${i * 19 + 16}, $${i * 19 + 17}, $${i * 19 + 18}, $${i * 19 + 19}, NOW())` - ) - .join(", "); + const relationValues = relationsToCopy.map((_, i) => + `($${i * 19 + 1}, $${i * 19 + 2}, $${i * 19 + 3}, $${i * 19 + 4}, $${i * 19 + 5}, $${i * 19 + 6}, $${i * 19 + 7}, $${i * 19 + 8}, $${i * 19 + 9}, $${i * 19 + 10}, $${i * 19 + 11}, $${i * 19 + 12}, $${i * 19 + 13}, $${i * 19 + 14}, $${i * 19 + 15}, $${i * 19 + 16}, $${i * 19 + 17}, $${i * 19 + 18}, $${i * 19 + 19}, NOW())` + ).join(", "); - const relationParams = relationsToCopy.flatMap((r) => [ - r.relation_code, - r.relation_name, - r.description, - r.parent_table, - r.parent_value_column, - r.parent_label_column, - r.child_table, - r.child_filter_column, - r.child_value_column, - r.child_label_column, - r.child_order_column, - r.child_order_direction, - r.empty_parent_message, - r.no_options_message, - r.loading_message, - r.clear_on_parent_change, - targetCompanyCode, - "Y", - userId, + const relationParams = relationsToCopy.flatMap(r => [ + r.relation_code, r.relation_name, r.description, + r.parent_table, r.parent_value_column, r.parent_label_column, + r.child_table, r.child_filter_column, r.child_value_column, r.child_label_column, + r.child_order_column, r.child_order_direction, + r.empty_parent_message, r.no_options_message, r.loading_message, + r.clear_on_parent_change, targetCompanyCode, "Y", userId ]); await client.query( @@ -3067,4 +2619,5 @@ export class MenuCopyService { logger.info(`โœ… ์—ฐ์‡„๊ด€๊ณ„ ๋ณต์‚ฌ ์™„๋ฃŒ: ${copiedCount}๊ฐœ`); return copiedCount; } + }