feat: 카테고리 설정 덮어쓰기 모드로 변경
기존 동작: - 카테고리 컬럼 매핑: 중복 시 스킵 - 카테고리 값: 중복 시 스킵 - 결과: 일부 값만 복사되어 불완전 새로운 동작 (덮어쓰기): - 카테고리 컬럼 매핑: 기존 것 삭제 후 재생성 - 카테고리 값: 테이블+컬럼 단위로 기존 것 전체 삭제 후 재생성 - 부모-자식 관계는 유지 (depth 순으로 정렬 후 복사) 장점: 1. 메뉴 재복사 시 항상 최신 카테고리 설정으로 덮어씀 2. 누락된 값 없이 완전한 복사 보장 3. 테스트 시 기존 데이터 정리 불필요 주의사항: - 기존 카테고리 값이 다른 데이터에서 참조되는 경우 외래키 제약조건 위반 가능 - 실무에서는 사용자 선택 옵션(덮어쓰기/병합)을 추가하는 것이 안전 관련 파일: - backend-node/src/services/menuCopyService.ts 테스트: - COMPANY_11로 재복사 시 모든 카테고리 값 정상 복사됨
This commit is contained in:
parent
8b3593c8fb
commit
10526da1ac
|
|
@ -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}개 (덮어쓰기)`
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue