feat: 카테고리 설정 덮어쓰기 모드로 변경

기존 동작:
- 카테고리 컬럼 매핑: 중복 시 스킵
- 카테고리 값: 중복 시 스킵
- 결과: 일부 값만 복사되어 불완전

새로운 동작 (덮어쓰기):
- 카테고리 컬럼 매핑: 기존 것 삭제 후 재생성
- 카테고리 값: 테이블+컬럼 단위로 기존 것 전체 삭제 후 재생성
- 부모-자식 관계는 유지 (depth 순으로 정렬 후 복사)

장점:
1. 메뉴 재복사 시 항상 최신 카테고리 설정으로 덮어씀
2. 누락된 값 없이 완전한 복사 보장
3. 테스트 시 기존 데이터 정리 불필요

주의사항:
- 기존 카테고리 값이 다른 데이터에서 참조되는 경우 외래키 제약조건 위반 가능
- 실무에서는 사용자 선택 옵션(덮어쓰기/병합)을 추가하는 것이 안전

관련 파일:
- backend-node/src/services/menuCopyService.ts

테스트:
- COMPANY_11로 재복사 시 모든 카테고리 값 정상 복사됨
This commit is contained in:
kjs 2025-11-21 15:58:00 +09:00
parent 8b3593c8fb
commit 10526da1ac
1 changed files with 26 additions and 36 deletions

View File

@ -1610,25 +1610,19 @@ export class MenuCopyService {
let mappingCount = 0;
let valueCount = 0;
// 1) 카테고리 컬럼 매핑 복사
// 1) 카테고리 컬럼 매핑 복사 (덮어쓰기 모드)
for (const mapping of settings.columnMappings) {
const newMenuObjid = menuIdMap.get(mapping.menu_objid);
if (!newMenuObjid) continue;
// 중복 체크
const existsResult = await client.query(
`SELECT mapping_id FROM category_column_mapping
// 기존 매핑 삭제 (덮어쓰기)
await client.query(
`DELETE FROM category_column_mapping
WHERE table_name = $1 AND physical_column_name = $2 AND company_code = $3`,
[mapping.table_name, mapping.physical_column_name, targetCompanyCode]
);
if (existsResult.rows.length > 0) {
logger.debug(
` ⏭️ 카테고리 매핑 이미 존재: ${mapping.table_name}.${mapping.physical_column_name}`
);
continue;
}
// 새 매핑 추가
await client.query(
`INSERT INTO category_column_mapping (
table_name, logical_column_name, physical_column_name,
@ -1648,38 +1642,34 @@ export class MenuCopyService {
mappingCount++;
}
// 2) 테이블 컬럼 카테고리 값 복사 (부모-자식 관계 유지)
// 2) 테이블 컬럼 카테고리 값 복사 (덮어쓰기 모드, 부모-자식 관계 유지)
const sortedValues = settings.categoryValues.sort(
(a, b) => a.depth - b.depth
);
let skippedValues = 0;
// 먼저 기존 값들을 모두 삭제 (테이블+컬럼 단위)
const uniqueTableColumns = new Set<string>();
for (const value of sortedValues) {
uniqueTableColumns.add(`${value.table_name}:${value.column_name}`);
}
for (const tableColumn of uniqueTableColumns) {
const [tableName, columnName] = tableColumn.split(":");
await client.query(
`DELETE FROM table_column_category_values
WHERE table_name = $1 AND column_name = $2 AND company_code = $3`,
[tableName, columnName, targetCompanyCode]
);
logger.debug(
` 🗑️ 기존 카테고리 값 삭제: ${tableName}.${columnName}`
);
}
// 새 값 추가
for (const value of sortedValues) {
const newMenuObjid = menuIdMap.get(value.menu_objid);
if (!newMenuObjid) continue;
// 중복 체크
const existsResult = await client.query(
`SELECT value_id FROM table_column_category_values
WHERE table_name = $1 AND column_name = $2 AND value_code = $3 AND company_code = $4`,
[
value.table_name,
value.column_name,
value.value_code,
targetCompanyCode,
]
);
if (existsResult.rows.length > 0) {
skippedValues++;
logger.debug(
` ⏭️ 카테고리 값 이미 존재: ${value.table_name}.${value.column_name}.${value.value_code}`
);
// 기존 값의 ID를 매핑에 저장 (자식 항목의 parent_id 재매핑용)
valueIdMap.set(value.value_id, existsResult.rows[0].value_id);
continue;
}
// 부모 ID 재매핑
let newParentValueId = null;
if (value.parent_value_id) {
@ -1721,7 +1711,7 @@ export class MenuCopyService {
}
logger.info(
`✅ 카테고리 설정 복사 완료: 컬럼 매핑 ${mappingCount}개, 카테고리 값 ${valueCount}개 (${skippedValues}개 스킵)`
`✅ 카테고리 설정 복사 완료: 컬럼 매핑 ${mappingCount}개, 카테고리 값 ${valueCount}개 (덮어쓰기)`
);
}