카테고리 설정 안풀리는 오류 수정

This commit is contained in:
kjs 2025-11-28 15:15:35 +09:00
parent f15846fd10
commit c78ba865b6
5 changed files with 226 additions and 62 deletions

View File

@ -481,6 +481,52 @@ export const deleteColumnMapping = async (req: AuthenticatedRequest, res: Respon
}
};
/**
* +
*
* DELETE /api/categories/column-mapping/:tableName/:columnName
*
*
*/
export const deleteColumnMappingsByColumn = async (req: AuthenticatedRequest, res: Response) => {
try {
const companyCode = req.user!.companyCode;
const { tableName, columnName } = req.params;
if (!tableName || !columnName) {
return res.status(400).json({
success: false,
message: "tableName과 columnName은 필수입니다",
});
}
logger.info("테이블+컬럼 기준 매핑 삭제", {
tableName,
columnName,
companyCode,
});
const deletedCount = await tableCategoryValueService.deleteColumnMappingsByColumn(
tableName,
columnName,
companyCode
);
return res.json({
success: true,
message: `${deletedCount}개의 컬럼 매핑이 삭제되었습니다`,
deletedCount,
});
} catch (error: any) {
logger.error(`테이블+컬럼 기준 매핑 삭제 실패: ${error.message}`);
return res.status(500).json({
success: false,
message: error.message || "컬럼 매핑 삭제 중 오류가 발생했습니다",
error: error.message,
});
}
};
/**
* 2
*

View File

@ -11,6 +11,7 @@ import {
createColumnMapping,
getLogicalColumns,
deleteColumnMapping,
deleteColumnMappingsByColumn,
getSecondLevelMenus,
} from "../controllers/tableCategoryValueController";
import { authenticateToken } from "../middleware/authMiddleware";
@ -57,7 +58,11 @@ router.get("/logical-columns/:tableName/:menuObjid", getLogicalColumns);
// 컬럼 매핑 생성/수정
router.post("/column-mapping", createColumnMapping);
// 컬럼 매핑 삭제
// 테이블+컬럼 기준 매핑 삭제 (메뉴 선택 변경 시 기존 매핑 모두 삭제용)
// 주의: 더 구체적인 라우트가 먼저 와야 함 (3개 세그먼트 > 1개 세그먼트)
router.delete("/column-mapping/:tableName/:columnName/all", deleteColumnMappingsByColumn);
// 컬럼 매핑 삭제 (단일)
router.delete("/column-mapping/:mappingId", deleteColumnMapping);
export default router;

View File

@ -1066,6 +1066,66 @@ class TableCategoryValueService {
}
}
/**
* +
*
*
*
* @param tableName -
* @param columnName -
* @param companyCode -
* @returns
*/
async deleteColumnMappingsByColumn(
tableName: string,
columnName: string,
companyCode: string
): Promise<number> {
const pool = getPool();
try {
logger.info("테이블+컬럼 기준 매핑 삭제", { tableName, columnName, companyCode });
// 멀티테넌시 적용
let deleteQuery: string;
let deleteParams: any[];
if (companyCode === "*") {
// 최고 관리자: 해당 테이블+컬럼의 모든 매핑 삭제
deleteQuery = `
DELETE FROM category_column_mapping
WHERE table_name = $1
AND logical_column_name = $2
`;
deleteParams = [tableName, columnName];
} else {
// 일반 회사: 자신의 매핑만 삭제
deleteQuery = `
DELETE FROM category_column_mapping
WHERE table_name = $1
AND logical_column_name = $2
AND company_code = $3
`;
deleteParams = [tableName, columnName, companyCode];
}
const result = await pool.query(deleteQuery, deleteParams);
const deletedCount = result.rowCount || 0;
logger.info("테이블+컬럼 기준 매핑 삭제 완료", {
tableName,
columnName,
companyCode,
deletedCount
});
return deletedCount;
} catch (error: any) {
logger.error(`테이블+컬럼 기준 매핑 삭제 실패: ${error.message}`);
throw error;
}
}
/**
*
*

View File

@ -17,7 +17,7 @@ import { apiClient } from "@/lib/api/client";
import { commonCodeApi } from "@/lib/api/commonCode";
import { entityJoinApi, ReferenceTableColumn } from "@/lib/api/entityJoin";
import { ddlApi } from "@/lib/api/ddl";
import { getSecondLevelMenus, createColumnMapping } from "@/lib/api/tableCategoryValue";
import { getSecondLevelMenus, createColumnMapping, deleteColumnMappingsByColumn } from "@/lib/api/tableCategoryValue";
import { CreateTableModal } from "@/components/admin/CreateTableModal";
import { AddColumnModal } from "@/components/admin/AddColumnModal";
import { DDLLogViewer } from "@/components/admin/DDLLogViewer";
@ -488,52 +488,69 @@ export default function TableManagementPage() {
if (response.data.success) {
console.log("✅ 컬럼 설정 저장 성공");
// 🆕 Category 타입인 경우 컬럼 매핑 생성
// 🆕 Category 타입인 경우 컬럼 매핑 처리
console.log("🔍 카테고리 조건 체크:", {
isCategory: column.inputType === "category",
hasCategoryMenus: !!column.categoryMenus,
length: column.categoryMenus?.length || 0,
});
if (column.inputType === "category" && column.categoryMenus && column.categoryMenus.length > 0) {
console.log("📥 카테고리 메뉴 매핑 시작:", {
if (column.inputType === "category") {
// 1. 먼저 기존 매핑 모두 삭제
console.log("🗑️ 기존 카테고리 메뉴 매핑 삭제 시작:", {
tableName: selectedTable,
columnName: column.columnName,
categoryMenus: column.categoryMenus,
count: column.categoryMenus.length,
});
let successCount = 0;
let failCount = 0;
for (const menuObjid of column.categoryMenus) {
try {
const mappingResponse = await createColumnMapping({
tableName: selectedTable,
logicalColumnName: column.columnName,
physicalColumnName: column.columnName,
menuObjid,
description: `${column.displayName} (메뉴별 카테고리)`,
});
if (mappingResponse.success) {
successCount++;
} else {
console.error("❌ 매핑 생성 실패:", mappingResponse);
failCount++;
}
} catch (error) {
console.error(`❌ 메뉴 ${menuObjid}에 대한 매핑 생성 실패:`, error);
failCount++;
}
try {
const deleteResponse = await deleteColumnMappingsByColumn(selectedTable, column.columnName);
console.log("🗑️ 기존 매핑 삭제 결과:", deleteResponse);
} catch (error) {
console.error("❌ 기존 매핑 삭제 실패:", error);
}
// 2. 새로운 매핑 추가 (선택된 메뉴가 있는 경우만)
if (column.categoryMenus && column.categoryMenus.length > 0) {
console.log("📥 카테고리 메뉴 매핑 시작:", {
columnName: column.columnName,
categoryMenus: column.categoryMenus,
count: column.categoryMenus.length,
});
if (successCount > 0 && failCount === 0) {
toast.success(`컬럼 설정 및 ${successCount}개 메뉴 매핑이 저장되었습니다.`);
} else if (successCount > 0 && failCount > 0) {
toast.warning(`컬럼 설정 저장 성공. ${successCount}개 메뉴 매핑 성공, ${failCount}개 실패.`);
} else if (failCount > 0) {
toast.error(`컬럼 설정 저장 성공. 메뉴 매핑 생성 실패.`);
let successCount = 0;
let failCount = 0;
for (const menuObjid of column.categoryMenus) {
try {
const mappingResponse = await createColumnMapping({
tableName: selectedTable,
logicalColumnName: column.columnName,
physicalColumnName: column.columnName,
menuObjid,
description: `${column.displayName} (메뉴별 카테고리)`,
});
if (mappingResponse.success) {
successCount++;
} else {
console.error("❌ 매핑 생성 실패:", mappingResponse);
failCount++;
}
} catch (error) {
console.error(`❌ 메뉴 ${menuObjid}에 대한 매핑 생성 실패:`, error);
failCount++;
}
}
if (successCount > 0 && failCount === 0) {
toast.success(`컬럼 설정 및 ${successCount}개 메뉴 매핑이 저장되었습니다.`);
} else if (successCount > 0 && failCount > 0) {
toast.warning(`컬럼 설정 저장 성공. ${successCount}개 메뉴 매핑 성공, ${failCount}개 실패.`);
} else if (failCount > 0) {
toast.error(`컬럼 설정 저장 성공. 메뉴 매핑 생성 실패.`);
}
} else {
toast.success("컬럼 설정이 저장되었습니다. (메뉴 매핑 없음)");
}
} else {
toast.success("컬럼 설정이 성공적으로 저장되었습니다.");
@ -596,10 +613,8 @@ export default function TableManagementPage() {
);
if (response.data.success) {
// 🆕 Category 타입 컬럼들의 메뉴 매핑 생성
const categoryColumns = columns.filter(
(col) => col.inputType === "category" && col.categoryMenus && col.categoryMenus.length > 0
);
// 🆕 Category 타입 컬럼들의 메뉴 매핑 처리
const categoryColumns = columns.filter((col) => col.inputType === "category");
console.log("📥 전체 저장: 카테고리 컬럼 확인", {
totalColumns: columns.length,
@ -615,33 +630,49 @@ export default function TableManagementPage() {
let totalFailCount = 0;
for (const column of categoryColumns) {
for (const menuObjid of column.categoryMenus!) {
try {
console.log("🔄 매핑 API 호출:", {
tableName: selectedTable,
columnName: column.columnName,
menuObjid,
});
// 1. 먼저 기존 매핑 모두 삭제
console.log("🗑️ 기존 카테고리 메뉴 매핑 삭제:", {
tableName: selectedTable,
columnName: column.columnName,
});
const mappingResponse = await createColumnMapping({
tableName: selectedTable,
logicalColumnName: column.columnName,
physicalColumnName: column.columnName,
menuObjid,
description: `${column.displayName} (메뉴별 카테고리)`,
});
try {
const deleteResponse = await deleteColumnMappingsByColumn(selectedTable, column.columnName);
console.log("🗑️ 기존 매핑 삭제 결과:", deleteResponse);
} catch (error) {
console.error("❌ 기존 매핑 삭제 실패:", error);
}
console.log("✅ 매핑 API 응답:", mappingResponse);
// 2. 새로운 매핑 추가 (선택된 메뉴가 있는 경우만)
if (column.categoryMenus && column.categoryMenus.length > 0) {
for (const menuObjid of column.categoryMenus) {
try {
console.log("🔄 매핑 API 호출:", {
tableName: selectedTable,
columnName: column.columnName,
menuObjid,
});
if (mappingResponse.success) {
totalSuccessCount++;
} else {
console.error("❌ 매핑 생성 실패:", mappingResponse);
const mappingResponse = await createColumnMapping({
tableName: selectedTable,
logicalColumnName: column.columnName,
physicalColumnName: column.columnName,
menuObjid,
description: `${column.displayName} (메뉴별 카테고리)`,
});
console.log("✅ 매핑 API 응답:", mappingResponse);
if (mappingResponse.success) {
totalSuccessCount++;
} else {
console.error("❌ 매핑 생성 실패:", mappingResponse);
totalFailCount++;
}
} catch (error) {
console.error(`❌ 메뉴 ${menuObjid}에 대한 매핑 생성 실패:`, error);
totalFailCount++;
}
} catch (error) {
console.error(`❌ 메뉴 ${menuObjid}에 대한 매핑 생성 실패:`, error);
totalFailCount++;
}
}
}

View File

@ -259,6 +259,28 @@ export async function deleteColumnMapping(mappingId: number) {
}
}
/**
* +
*
*
*
* @param tableName -
* @param columnName -
*/
export async function deleteColumnMappingsByColumn(tableName: string, columnName: string) {
try {
const response = await apiClient.delete<{
success: boolean;
message: string;
deletedCount: number;
}>(`/table-categories/column-mapping/${tableName}/${columnName}/all`);
return response.data;
} catch (error: any) {
console.error("테이블+컬럼 기준 매핑 삭제 실패:", error);
return { success: false, error: error.message, deletedCount: 0 };
}
}
/**
* 2
*