테이블 컬럼추가 오류 수정

This commit is contained in:
kjs 2025-11-12 17:52:08 +09:00
parent 77faba7e77
commit 214bd829e9
5 changed files with 86 additions and 40 deletions

View File

@ -104,7 +104,7 @@ export class DDLExecutionService {
await this.saveTableMetadata(client, tableName, description);
// 5-3. 컬럼 메타데이터 저장
await this.saveColumnMetadata(client, tableName, columns);
await this.saveColumnMetadata(client, tableName, columns, userCompanyCode);
});
// 6. 성공 로그 기록
@ -272,7 +272,7 @@ export class DDLExecutionService {
await client.query(ddlQuery);
// 6-2. 컬럼 메타데이터 저장
await this.saveColumnMetadata(client, tableName, [column]);
await this.saveColumnMetadata(client, tableName, [column], userCompanyCode);
});
// 7. 성공 로그 기록
@ -446,7 +446,8 @@ CREATE TABLE "${tableName}" (${baseColumns},
private async saveColumnMetadata(
client: any,
tableName: string,
columns: CreateColumnDefinition[]
columns: CreateColumnDefinition[],
companyCode: string
): Promise<void> {
// 먼저 table_labels에 테이블 정보가 있는지 확인하고 없으면 생성
await client.query(
@ -508,19 +509,19 @@ CREATE TABLE "${tableName}" (${baseColumns},
await client.query(
`
INSERT INTO table_type_columns (
table_name, column_name, input_type, detail_settings,
table_name, column_name, company_code, input_type, detail_settings,
is_nullable, display_order, created_date, updated_date
) VALUES (
$1, $2, $3, '{}',
'Y', $4, now(), now()
$1, $2, $3, $4, '{}',
'Y', $5, now(), now()
)
ON CONFLICT (table_name, column_name)
ON CONFLICT (table_name, column_name, company_code)
DO UPDATE SET
input_type = $3,
display_order = $4,
input_type = $4,
display_order = $5,
updated_date = now()
`,
[tableName, defaultCol.name, defaultCol.inputType, defaultCol.order]
[tableName, defaultCol.name, companyCode, defaultCol.inputType, defaultCol.order]
);
}
@ -535,20 +536,20 @@ CREATE TABLE "${tableName}" (${baseColumns},
await client.query(
`
INSERT INTO table_type_columns (
table_name, column_name, input_type, detail_settings,
table_name, column_name, company_code, input_type, detail_settings,
is_nullable, display_order, created_date, updated_date
) VALUES (
$1, $2, $3, $4,
'Y', $5, now(), now()
$1, $2, $3, $4, $5,
'Y', $6, now(), now()
)
ON CONFLICT (table_name, column_name)
ON CONFLICT (table_name, column_name, company_code)
DO UPDATE SET
input_type = $3,
detail_settings = $4,
display_order = $5,
input_type = $4,
detail_settings = $5,
display_order = $6,
updated_date = now()
`,
[tableName, column.name, inputType, detailSettings, i]
[tableName, column.name, companyCode, inputType, detailSettings, i]
);
}

View File

@ -36,29 +36,61 @@ export async function getSiblingMenuObjids(menuObjid: number): Promise<number[]>
try {
logger.debug("메뉴 스코프 조회 시작", { menuObjid });
// 1. 현재 메뉴 자신을 포함
const menuObjids = [menuObjid];
// 1. 현재 메뉴 정보 조회 (부모 ID 확인)
const currentMenuQuery = `
SELECT parent_obj_id FROM menu_info
WHERE objid = $1
`;
const currentMenuResult = await pool.query(currentMenuQuery, [menuObjid]);
// 2. 현재 메뉴의 자식 메뉴들 조회
const childrenQuery = `
if (currentMenuResult.rows.length === 0) {
logger.warn("메뉴를 찾을 수 없음, 자기 자신만 반환", { menuObjid });
return [menuObjid];
}
const parentObjId = Number(currentMenuResult.rows[0].parent_obj_id);
// 2. 최상위 메뉴(parent_obj_id = 0)는 자기 자신만 반환
if (parentObjId === 0) {
logger.debug("최상위 메뉴, 자기 자신만 반환", { menuObjid });
return [menuObjid];
}
// 3. 형제 메뉴들 조회 (같은 부모를 가진 메뉴들)
const siblingsQuery = `
SELECT objid FROM menu_info
WHERE parent_obj_id = $1
ORDER BY objid
`;
const childrenResult = await pool.query(childrenQuery, [menuObjid]);
const siblingsResult = await pool.query(siblingsQuery, [parentObjId]);
const childObjids = childrenResult.rows.map((row) => Number(row.objid));
const siblingObjids = siblingsResult.rows.map((row) => Number(row.objid));
// 3. 자신 + 자식을 합쳐서 정렬
const allObjids = Array.from(new Set([...menuObjids, ...childObjids])).sort((a, b) => a - b);
// 4. 각 형제 메뉴(자기 자신 포함)의 자식 메뉴들도 조회
const allObjids = [...siblingObjids];
for (const siblingObjid of siblingObjids) {
const childrenQuery = `
SELECT objid FROM menu_info
WHERE parent_obj_id = $1
ORDER BY objid
`;
const childrenResult = await pool.query(childrenQuery, [siblingObjid]);
const childObjids = childrenResult.rows.map((row) => Number(row.objid));
allObjids.push(...childObjids);
}
// 5. 중복 제거 및 정렬
const uniqueObjids = Array.from(new Set(allObjids)).sort((a, b) => a - b);
logger.debug("메뉴 스코프 조회 완료", {
menuObjid,
childCount: childObjids.length,
totalCount: allObjids.length
menuObjid,
parentObjId,
siblingCount: siblingObjids.length,
totalCount: uniqueObjids.length
});
return allObjids;
return uniqueObjids;
} catch (error: any) {
logger.error("메뉴 스코프 조회 실패", {
menuObjid,

View File

@ -179,7 +179,8 @@ class TableCategoryValueService {
} else {
// 일반 회사: 자신의 카테고리 값만 조회
if (menuObjid && siblingObjids.length > 0) {
// 메뉴 스코프 적용
// 메뉴 스코프 적용 + created_menu_objid 필터링
// 현재 메뉴 스코프(형제 메뉴)에서 생성된 값만 표시
query = `
SELECT
value_id AS "valueId",
@ -197,6 +198,7 @@ class TableCategoryValueService {
is_default AS "isDefault",
company_code AS "companyCode",
menu_objid AS "menuObjid",
created_menu_objid AS "createdMenuObjid",
created_at AS "createdAt",
updated_at AS "updatedAt",
created_by AS "createdBy",
@ -206,6 +208,10 @@ class TableCategoryValueService {
AND column_name = $2
AND menu_objid = ANY($3)
AND company_code = $4
AND (
created_menu_objid = ANY($3) --
OR created_menu_objid IS NULL -- ( )
)
`;
params = [tableName, columnName, siblingObjids, companyCode];
} else {
@ -331,8 +337,8 @@ class TableCategoryValueService {
INSERT INTO table_column_category_values (
table_name, column_name, value_code, value_label, value_order,
parent_value_id, depth, description, color, icon,
is_active, is_default, company_code, menu_objid, created_by
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
is_active, is_default, company_code, menu_objid, created_menu_objid, created_by
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
RETURNING
value_id AS "valueId",
table_name AS "tableName",
@ -349,6 +355,7 @@ class TableCategoryValueService {
is_default AS "isDefault",
company_code AS "companyCode",
menu_objid AS "menuObjid",
created_menu_objid AS "createdMenuObjid",
created_at AS "createdAt",
created_by AS "createdBy"
`;
@ -368,6 +375,7 @@ class TableCategoryValueService {
value.isDefault || false,
companyCode,
menuObjid, // ← 메뉴 OBJID 저장
menuObjid, // ← 🆕 생성 메뉴 OBJID 저장 (같은 값)
userId,
]);

View File

@ -49,6 +49,7 @@ export function CategoryWidget({ widgetId, tableName, menuObjid, component, ...p
const effectiveMenuObjid = menuObjid || props.menuObjid;
const [selectedColumn, setSelectedColumn] = useState<{
uniqueKey: string; // 테이블명.컬럼명 형식
columnName: string;
columnLabel: string;
tableName: string;
@ -98,10 +99,12 @@ export function CategoryWidget({ widgetId, tableName, menuObjid, component, ...p
<div style={{ width: `${leftWidth}%` }} className="pr-3">
<CategoryColumnList
tableName={tableName}
selectedColumn={selectedColumn?.columnName || null}
onColumnSelect={(columnName, columnLabel, tableName) =>
setSelectedColumn({ columnName, columnLabel, tableName })
}
selectedColumn={selectedColumn?.uniqueKey || null}
onColumnSelect={(uniqueKey, columnLabel, tableName) => {
// uniqueKey는 "테이블명.컬럼명" 형식
const columnName = uniqueKey.split('.')[1];
setSelectedColumn({ uniqueKey, columnName, columnLabel, tableName });
}}
menuObjid={effectiveMenuObjid}
/>
</div>
@ -118,6 +121,7 @@ export function CategoryWidget({ widgetId, tableName, menuObjid, component, ...p
<div style={{ width: `${100 - leftWidth - 1}%` }} className="pl-3">
{selectedColumn ? (
<CategoryValueManager
key={selectedColumn.uniqueKey} // 테이블명.컬럼명으로 컴포넌트 재생성
tableName={selectedColumn.tableName}
columnName={selectedColumn.columnName}
columnLabel={selectedColumn.columnLabel}

View File

@ -147,17 +147,18 @@ export function CategoryColumnList({ tableName, selectedColumn, onColumnSelect,
<div className="space-y-2">
{columns.map((column) => {
const uniqueKey = `${column.tableName}.${column.columnName}`;
const isSelected = selectedColumn === uniqueKey; // 테이블명.컬럼명으로 비교
return (
<div
key={uniqueKey}
onClick={() => onColumnSelect(column.columnName, column.columnLabel || column.columnName, column.tableName)}
onClick={() => onColumnSelect(uniqueKey, column.columnLabel || column.columnName, column.tableName)}
className={`cursor-pointer rounded-lg border px-4 py-2 transition-all ${
selectedColumn === column.columnName ? "border-primary bg-primary/10 shadow-sm" : "hover:bg-muted/50"
isSelected ? "border-primary bg-primary/10 shadow-sm" : "hover:bg-muted/50"
}`}
>
<div className="flex items-center gap-2">
<FolderTree
className={`h-4 w-4 ${selectedColumn === column.columnName ? "text-primary" : "text-muted-foreground"}`}
className={`h-4 w-4 ${isSelected ? "text-primary" : "text-muted-foreground"}`}
/>
<div className="flex-1">
<h4 className="text-sm font-semibold">{column.columnLabel || column.columnName}</h4>