ERP-node/backend-node/src/controllers/categoryValueCascadingContr...

1063 lines
28 KiB
TypeScript
Raw Normal View History

2025-12-18 14:12:48 +09:00
import { Response } from "express";
import { AuthenticatedRequest } from "../types/auth";
import { getPool } from "../database/db";
import { logger } from "../utils/logger";
const pool = getPool();
// ============================================
// 카테고리 값 연쇄관계 그룹 CRUD
// ============================================
/**
*
*/
export const getCategoryValueCascadingGroups = async (
req: AuthenticatedRequest,
res: Response
) => {
try {
const companyCode = req.user?.companyCode || "*";
const { isActive } = req.query;
let query = `
SELECT
group_id,
relation_code,
relation_name,
description,
parent_table_name,
parent_column_name,
parent_menu_objid,
child_table_name,
child_column_name,
child_menu_objid,
clear_on_parent_change,
show_group_label,
empty_parent_message,
no_options_message,
company_code,
is_active,
created_by,
created_date,
updated_by,
updated_date
FROM category_value_cascading_group
WHERE 1=1
`;
const params: any[] = [];
let paramIndex = 1;
// 멀티테넌시 필터링
if (companyCode !== "*") {
query += ` AND (company_code = $${paramIndex} OR company_code = '*')`;
params.push(companyCode);
paramIndex++;
}
if (isActive !== undefined) {
query += ` AND is_active = $${paramIndex}`;
params.push(isActive);
paramIndex++;
}
query += ` ORDER BY relation_name ASC`;
const result = await pool.query(query, params);
logger.info("카테고리 값 연쇄관계 그룹 목록 조회", {
companyCode,
count: result.rowCount,
});
return res.json({
success: true,
data: result.rows,
});
} catch (error: any) {
2025-12-18 15:16:34 +09:00
logger.error("카테고리 값 연쇄관계 그룹 목록 조회 실패", {
error: error.message,
});
2025-12-18 14:12:48 +09:00
return res.status(500).json({
success: false,
message: "카테고리 값 연쇄관계 그룹 목록 조회에 실패했습니다.",
error: error.message,
});
}
};
/**
*
*/
export const getCategoryValueCascadingGroupById = async (
req: AuthenticatedRequest,
res: Response
) => {
try {
const { groupId } = req.params;
const companyCode = req.user?.companyCode || "*";
// 그룹 정보 조회
let groupQuery = `
SELECT
group_id,
relation_code,
relation_name,
description,
parent_table_name,
parent_column_name,
parent_menu_objid,
child_table_name,
child_column_name,
child_menu_objid,
clear_on_parent_change,
show_group_label,
empty_parent_message,
no_options_message,
company_code,
is_active
FROM category_value_cascading_group
WHERE group_id = $1
`;
const groupParams: any[] = [groupId];
if (companyCode !== "*") {
groupQuery += ` AND (company_code = $2 OR company_code = '*')`;
groupParams.push(companyCode);
}
const groupResult = await pool.query(groupQuery, groupParams);
if (groupResult.rowCount === 0) {
return res.status(404).json({
success: false,
message: "카테고리 값 연쇄관계 그룹을 찾을 수 없습니다.",
});
}
// 매핑 정보 조회
const mappingQuery = `
SELECT
mapping_id,
parent_value_code,
parent_value_label,
child_value_code,
child_value_label,
display_order,
is_active
FROM category_value_cascading_mapping
WHERE group_id = $1 AND is_active = 'Y'
ORDER BY parent_value_code, display_order, child_value_label
`;
const mappingResult = await pool.query(mappingQuery, [groupId]);
// 부모 값별로 자식 값 그룹화
const mappingsByParent: Record<string, any[]> = {};
for (const row of mappingResult.rows) {
const parentKey = row.parent_value_code;
if (!mappingsByParent[parentKey]) {
mappingsByParent[parentKey] = [];
}
mappingsByParent[parentKey].push({
childValueCode: row.child_value_code,
childValueLabel: row.child_value_label,
displayOrder: row.display_order,
});
}
return res.json({
success: true,
data: {
...groupResult.rows[0],
mappings: mappingResult.rows,
mappingsByParent,
},
});
} catch (error: any) {
2025-12-18 15:16:34 +09:00
logger.error("카테고리 값 연쇄관계 그룹 상세 조회 실패", {
error: error.message,
});
2025-12-18 14:12:48 +09:00
return res.status(500).json({
success: false,
message: "카테고리 값 연쇄관계 그룹 조회에 실패했습니다.",
error: error.message,
});
}
};
/**
*
*/
export const getCategoryValueCascadingByCode = async (
req: AuthenticatedRequest,
res: Response
) => {
try {
const { code } = req.params;
const companyCode = req.user?.companyCode || "*";
let query = `
SELECT
group_id,
relation_code,
relation_name,
description,
parent_table_name,
parent_column_name,
parent_menu_objid,
child_table_name,
child_column_name,
child_menu_objid,
clear_on_parent_change,
show_group_label,
empty_parent_message,
no_options_message,
company_code,
is_active
FROM category_value_cascading_group
WHERE relation_code = $1 AND is_active = 'Y'
`;
const params: any[] = [code];
if (companyCode !== "*") {
query += ` AND (company_code = $2 OR company_code = '*')`;
params.push(companyCode);
}
query += ` LIMIT 1`;
const result = await pool.query(query, params);
if (result.rowCount === 0) {
return res.status(404).json({
success: false,
message: "카테고리 값 연쇄관계를 찾을 수 없습니다.",
});
}
return res.json({
success: true,
data: result.rows[0],
});
} catch (error: any) {
2025-12-18 15:16:34 +09:00
logger.error("카테고리 값 연쇄관계 코드 조회 실패", {
error: error.message,
});
2025-12-18 14:12:48 +09:00
return res.status(500).json({
success: false,
message: "카테고리 값 연쇄관계 조회에 실패했습니다.",
error: error.message,
});
}
};
/**
*
*/
export const createCategoryValueCascadingGroup = async (
req: AuthenticatedRequest,
res: Response
) => {
try {
const companyCode = req.user?.companyCode || "*";
const userId = req.user?.userId || "system";
const {
relationCode,
relationName,
description,
parentTableName,
parentColumnName,
parentMenuObjid,
childTableName,
childColumnName,
childMenuObjid,
clearOnParentChange = true,
showGroupLabel = true,
emptyParentMessage,
noOptionsMessage,
} = req.body;
// 필수 필드 검증
2025-12-18 15:16:34 +09:00
if (
!relationCode ||
!relationName ||
!parentTableName ||
!parentColumnName ||
!childTableName ||
!childColumnName
) {
2025-12-18 14:12:48 +09:00
return res.status(400).json({
success: false,
message: "필수 필드가 누락되었습니다.",
});
}
// 중복 코드 체크
const duplicateCheck = await pool.query(
`SELECT group_id FROM category_value_cascading_group
WHERE relation_code = $1 AND (company_code = $2 OR company_code = '*')`,
[relationCode, companyCode]
);
if (duplicateCheck.rowCount && duplicateCheck.rowCount > 0) {
return res.status(400).json({
success: false,
message: "이미 존재하는 관계 코드입니다.",
});
}
const query = `
INSERT INTO category_value_cascading_group (
relation_code,
relation_name,
description,
parent_table_name,
parent_column_name,
parent_menu_objid,
child_table_name,
child_column_name,
child_menu_objid,
clear_on_parent_change,
show_group_label,
empty_parent_message,
no_options_message,
company_code,
is_active,
created_by,
created_date
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, 'Y', $15, NOW())
RETURNING *
`;
const result = await pool.query(query, [
relationCode,
relationName,
description || null,
parentTableName,
parentColumnName,
parentMenuObjid || null,
childTableName,
childColumnName,
childMenuObjid || null,
clearOnParentChange ? "Y" : "N",
showGroupLabel ? "Y" : "N",
emptyParentMessage || "상위 항목을 먼저 선택하세요",
noOptionsMessage || "선택 가능한 항목이 없습니다",
companyCode,
userId,
]);
logger.info("카테고리 값 연쇄관계 그룹 생성", {
groupId: result.rows[0].group_id,
relationCode,
companyCode,
userId,
});
return res.status(201).json({
success: true,
data: result.rows[0],
message: "카테고리 값 연쇄관계 그룹이 생성되었습니다.",
});
} catch (error: any) {
2025-12-18 15:16:34 +09:00
logger.error("카테고리 값 연쇄관계 그룹 생성 실패", {
error: error.message,
});
2025-12-18 14:12:48 +09:00
return res.status(500).json({
success: false,
message: "카테고리 값 연쇄관계 그룹 생성에 실패했습니다.",
error: error.message,
});
}
};
/**
*
*/
export const updateCategoryValueCascadingGroup = async (
req: AuthenticatedRequest,
res: Response
) => {
try {
const { groupId } = req.params;
const companyCode = req.user?.companyCode || "*";
const userId = req.user?.userId || "system";
const {
relationName,
description,
parentTableName,
parentColumnName,
parentMenuObjid,
childTableName,
childColumnName,
childMenuObjid,
clearOnParentChange,
showGroupLabel,
emptyParentMessage,
noOptionsMessage,
isActive,
} = req.body;
// 권한 체크
const existingCheck = await pool.query(
`SELECT group_id, company_code FROM category_value_cascading_group WHERE group_id = $1`,
[groupId]
);
if (existingCheck.rowCount === 0) {
return res.status(404).json({
success: false,
message: "카테고리 값 연쇄관계 그룹을 찾을 수 없습니다.",
});
}
const existingCompanyCode = existingCheck.rows[0].company_code;
2025-12-18 15:16:34 +09:00
if (
companyCode !== "*" &&
existingCompanyCode !== companyCode &&
existingCompanyCode !== "*"
) {
2025-12-18 14:12:48 +09:00
return res.status(403).json({
success: false,
message: "수정 권한이 없습니다.",
});
}
const query = `
UPDATE category_value_cascading_group SET
relation_name = COALESCE($1, relation_name),
description = COALESCE($2, description),
parent_table_name = COALESCE($3, parent_table_name),
parent_column_name = COALESCE($4, parent_column_name),
parent_menu_objid = COALESCE($5, parent_menu_objid),
child_table_name = COALESCE($6, child_table_name),
child_column_name = COALESCE($7, child_column_name),
child_menu_objid = COALESCE($8, child_menu_objid),
clear_on_parent_change = COALESCE($9, clear_on_parent_change),
show_group_label = COALESCE($10, show_group_label),
empty_parent_message = COALESCE($11, empty_parent_message),
no_options_message = COALESCE($12, no_options_message),
is_active = COALESCE($13, is_active),
updated_by = $14,
updated_date = NOW()
WHERE group_id = $15
RETURNING *
`;
const result = await pool.query(query, [
relationName,
description,
parentTableName,
parentColumnName,
parentMenuObjid,
childTableName,
childColumnName,
childMenuObjid,
2025-12-18 15:16:34 +09:00
clearOnParentChange !== undefined
? clearOnParentChange
? "Y"
: "N"
: null,
2025-12-18 14:12:48 +09:00
showGroupLabel !== undefined ? (showGroupLabel ? "Y" : "N") : null,
emptyParentMessage,
noOptionsMessage,
isActive !== undefined ? (isActive ? "Y" : "N") : null,
userId,
groupId,
]);
logger.info("카테고리 값 연쇄관계 그룹 수정", {
groupId,
companyCode,
userId,
});
return res.json({
success: true,
data: result.rows[0],
message: "카테고리 값 연쇄관계 그룹이 수정되었습니다.",
});
} catch (error: any) {
2025-12-18 15:16:34 +09:00
logger.error("카테고리 값 연쇄관계 그룹 수정 실패", {
error: error.message,
});
2025-12-18 14:12:48 +09:00
return res.status(500).json({
success: false,
message: "카테고리 값 연쇄관계 그룹 수정에 실패했습니다.",
error: error.message,
});
}
};
/**
*
*/
export const deleteCategoryValueCascadingGroup = async (
req: AuthenticatedRequest,
res: Response
) => {
try {
const { groupId } = req.params;
const companyCode = req.user?.companyCode || "*";
const userId = req.user?.userId || "system";
// 권한 체크
const existingCheck = await pool.query(
`SELECT group_id, company_code FROM category_value_cascading_group WHERE group_id = $1`,
[groupId]
);
if (existingCheck.rowCount === 0) {
return res.status(404).json({
success: false,
message: "카테고리 값 연쇄관계 그룹을 찾을 수 없습니다.",
});
}
const existingCompanyCode = existingCheck.rows[0].company_code;
2025-12-18 15:16:34 +09:00
if (
companyCode !== "*" &&
existingCompanyCode !== companyCode &&
existingCompanyCode !== "*"
) {
2025-12-18 14:12:48 +09:00
return res.status(403).json({
success: false,
message: "삭제 권한이 없습니다.",
});
}
// 소프트 삭제
await pool.query(
`UPDATE category_value_cascading_group
SET is_active = 'N', updated_by = $1, updated_date = NOW()
WHERE group_id = $2`,
[userId, groupId]
);
logger.info("카테고리 값 연쇄관계 그룹 삭제", {
groupId,
companyCode,
userId,
});
return res.json({
success: true,
message: "카테고리 값 연쇄관계 그룹이 삭제되었습니다.",
});
} catch (error: any) {
2025-12-18 15:16:34 +09:00
logger.error("카테고리 값 연쇄관계 그룹 삭제 실패", {
error: error.message,
});
2025-12-18 14:12:48 +09:00
return res.status(500).json({
success: false,
message: "카테고리 값 연쇄관계 그룹 삭제에 실패했습니다.",
error: error.message,
});
}
};
// ============================================
// 카테고리 값 연쇄관계 매핑 CRUD
// ============================================
/**
* ( )
*/
export const saveCategoryValueCascadingMappings = async (
req: AuthenticatedRequest,
res: Response
) => {
try {
const { groupId } = req.params;
const companyCode = req.user?.companyCode || "*";
const { mappings } = req.body; // [{ parentValueCode, parentValueLabel, childValueCode, childValueLabel, displayOrder }]
if (!Array.isArray(mappings)) {
return res.status(400).json({
success: false,
message: "mappings는 배열이어야 합니다.",
});
}
// 그룹 존재 확인
const groupCheck = await pool.query(
`SELECT group_id FROM category_value_cascading_group WHERE group_id = $1 AND is_active = 'Y'`,
[groupId]
);
if (groupCheck.rowCount === 0) {
return res.status(404).json({
success: false,
message: "카테고리 값 연쇄관계 그룹을 찾을 수 없습니다.",
});
}
// 트랜잭션으로 처리
const client = await pool.connect();
try {
await client.query("BEGIN");
// 기존 매핑 삭제 (하드 삭제)
await client.query(
`DELETE FROM category_value_cascading_mapping WHERE group_id = $1`,
[groupId]
);
// 새 매핑 삽입
if (mappings.length > 0) {
const insertQuery = `
INSERT INTO category_value_cascading_mapping (
group_id, parent_value_code, parent_value_label,
child_value_code, child_value_label, display_order,
company_code, is_active, created_date
) VALUES ($1, $2, $3, $4, $5, $6, $7, 'Y', NOW())
`;
for (const mapping of mappings) {
await client.query(insertQuery, [
groupId,
mapping.parentValueCode,
mapping.parentValueLabel || null,
mapping.childValueCode,
mapping.childValueLabel || null,
mapping.displayOrder || 0,
companyCode,
]);
}
}
await client.query("COMMIT");
logger.info("카테고리 값 연쇄관계 매핑 저장", {
groupId,
mappingCount: mappings.length,
companyCode,
});
return res.json({
success: true,
message: `${mappings.length}개의 매핑이 저장되었습니다.`,
});
} catch (err) {
await client.query("ROLLBACK");
throw err;
} finally {
client.release();
}
} catch (error: any) {
2025-12-18 15:16:34 +09:00
logger.error("카테고리 값 연쇄관계 매핑 저장 실패", {
error: error.message,
});
2025-12-18 14:12:48 +09:00
return res.status(500).json({
success: false,
message: "카테고리 값 연쇄관계 매핑 저장에 실패했습니다.",
error: error.message,
});
}
};
// ============================================
// 연쇄 옵션 조회 (실제 드롭다운에서 사용)
// ============================================
/**
*
* ()
*
*/
export const getCategoryValueCascadingOptions = async (
req: AuthenticatedRequest,
res: Response
) => {
try {
const { code } = req.params;
const { parentValue, parentValues } = req.query;
const companyCode = req.user?.companyCode || "*";
// 다중 부모값 파싱
let parentValueArray: string[] = [];
2025-12-18 15:16:34 +09:00
2025-12-18 14:12:48 +09:00
if (parentValues) {
if (Array.isArray(parentValues)) {
2025-12-18 15:16:34 +09:00
parentValueArray = parentValues.map((v) => String(v));
2025-12-18 14:12:48 +09:00
} else {
2025-12-18 15:16:34 +09:00
parentValueArray = String(parentValues)
.split(",")
.map((v) => v.trim())
.filter((v) => v);
2025-12-18 14:12:48 +09:00
}
} else if (parentValue) {
parentValueArray = [String(parentValue)];
}
if (parentValueArray.length === 0) {
return res.json({
success: true,
data: [],
message: "부모 값이 없습니다.",
});
}
// 관계 정보 조회
let groupQuery = `
SELECT group_id, show_group_label
FROM category_value_cascading_group
WHERE relation_code = $1 AND is_active = 'Y'
`;
const groupParams: any[] = [code];
if (companyCode !== "*") {
groupQuery += ` AND (company_code = $2 OR company_code = '*')`;
groupParams.push(companyCode);
}
groupQuery += ` LIMIT 1`;
const groupResult = await pool.query(groupQuery, groupParams);
if (groupResult.rowCount === 0) {
return res.status(404).json({
success: false,
message: "카테고리 값 연쇄관계를 찾을 수 없습니다.",
});
}
const group = groupResult.rows[0];
// 매핑된 자식 값 조회 (다중 부모값에 대해 IN 절 사용)
2025-12-18 15:16:34 +09:00
const placeholders = parentValueArray
.map((_, idx) => `$${idx + 2}`)
.join(", ");
2025-12-18 14:12:48 +09:00
const optionsQuery = `
SELECT DISTINCT
child_value_code as value,
child_value_label as label,
parent_value_code as parent_value,
parent_value_label as parent_label,
display_order
FROM category_value_cascading_mapping
WHERE group_id = $1
AND parent_value_code IN (${placeholders})
AND is_active = 'Y'
ORDER BY parent_value_code, display_order, child_value_label
`;
2025-12-18 15:16:34 +09:00
const optionsResult = await pool.query(optionsQuery, [
group.group_id,
...parentValueArray,
]);
2025-12-18 14:12:48 +09:00
logger.info("카테고리 값 연쇄 옵션 조회", {
relationCode: code,
parentValues: parentValueArray,
optionsCount: optionsResult.rowCount,
});
return res.json({
success: true,
data: optionsResult.rows,
2025-12-18 15:16:34 +09:00
showGroupLabel: group.show_group_label === "Y",
2025-12-18 14:12:48 +09:00
});
} catch (error: any) {
logger.error("카테고리 값 연쇄 옵션 조회 실패", { error: error.message });
return res.status(500).json({
success: false,
message: "카테고리 값 연쇄 옵션 조회에 실패했습니다.",
error: error.message,
});
}
};
/**
*
*/
export const getCategoryValueCascadingParentOptions = async (
req: AuthenticatedRequest,
res: Response
) => {
try {
const { code } = req.params;
const companyCode = req.user?.companyCode || "*";
// 관계 정보 조회
let groupQuery = `
SELECT
group_id,
parent_table_name,
parent_column_name,
parent_menu_objid
FROM category_value_cascading_group
WHERE relation_code = $1 AND is_active = 'Y'
`;
const groupParams: any[] = [code];
if (companyCode !== "*") {
groupQuery += ` AND (company_code = $2 OR company_code = '*')`;
groupParams.push(companyCode);
}
groupQuery += ` LIMIT 1`;
const groupResult = await pool.query(groupQuery, groupParams);
if (groupResult.rowCount === 0) {
return res.status(404).json({
success: false,
message: "카테고리 값 연쇄관계를 찾을 수 없습니다.",
});
}
const group = groupResult.rows[0];
// 부모 카테고리 값 조회 (table_column_category_values에서)
let optionsQuery = `
SELECT
value_code as value,
value_label as label,
value_order as display_order
FROM table_column_category_values
WHERE table_name = $1
AND column_name = $2
AND is_active = true
`;
2025-12-18 15:16:34 +09:00
const optionsParams: any[] = [
group.parent_table_name,
group.parent_column_name,
];
2025-12-18 14:12:48 +09:00
let paramIndex = 3;
// 메뉴 스코프 적용
if (group.parent_menu_objid) {
optionsQuery += ` AND menu_objid = $${paramIndex}`;
optionsParams.push(group.parent_menu_objid);
paramIndex++;
}
// 멀티테넌시 적용
if (companyCode !== "*") {
optionsQuery += ` AND (company_code = $${paramIndex} OR company_code = '*')`;
optionsParams.push(companyCode);
}
optionsQuery += ` ORDER BY value_order, value_label`;
const optionsResult = await pool.query(optionsQuery, optionsParams);
logger.info("부모 카테고리 값 조회", {
relationCode: code,
tableName: group.parent_table_name,
columnName: group.parent_column_name,
optionsCount: optionsResult.rowCount,
});
return res.json({
success: true,
data: optionsResult.rows,
});
} catch (error: any) {
logger.error("부모 카테고리 값 조회 실패", { error: error.message });
return res.status(500).json({
success: false,
message: "부모 카테고리 값 조회에 실패했습니다.",
error: error.message,
});
}
};
/**
* ( UI용)
*/
export const getCategoryValueCascadingChildOptions = async (
req: AuthenticatedRequest,
res: Response
) => {
try {
const { code } = req.params;
const companyCode = req.user?.companyCode || "*";
// 관계 정보 조회
let groupQuery = `
SELECT
group_id,
child_table_name,
child_column_name,
child_menu_objid
FROM category_value_cascading_group
WHERE relation_code = $1 AND is_active = 'Y'
`;
const groupParams: any[] = [code];
if (companyCode !== "*") {
groupQuery += ` AND (company_code = $2 OR company_code = '*')`;
groupParams.push(companyCode);
}
groupQuery += ` LIMIT 1`;
const groupResult = await pool.query(groupQuery, groupParams);
if (groupResult.rowCount === 0) {
return res.status(404).json({
success: false,
message: "카테고리 값 연쇄관계를 찾을 수 없습니다.",
});
}
const group = groupResult.rows[0];
// 자식 카테고리 값 조회 (table_column_category_values에서)
let optionsQuery = `
SELECT
value_code as value,
value_label as label,
value_order as display_order
FROM table_column_category_values
WHERE table_name = $1
AND column_name = $2
AND is_active = true
`;
2025-12-18 15:16:34 +09:00
const optionsParams: any[] = [
group.child_table_name,
group.child_column_name,
];
2025-12-18 14:12:48 +09:00
let paramIndex = 3;
// 메뉴 스코프 적용
if (group.child_menu_objid) {
optionsQuery += ` AND menu_objid = $${paramIndex}`;
optionsParams.push(group.child_menu_objid);
paramIndex++;
}
// 멀티테넌시 적용
if (companyCode !== "*") {
optionsQuery += ` AND (company_code = $${paramIndex} OR company_code = '*')`;
optionsParams.push(companyCode);
}
optionsQuery += ` ORDER BY value_order, value_label`;
const optionsResult = await pool.query(optionsQuery, optionsParams);
logger.info("자식 카테고리 값 조회", {
relationCode: code,
tableName: group.child_table_name,
columnName: group.child_column_name,
optionsCount: optionsResult.rowCount,
});
return res.json({
success: true,
data: optionsResult.rows,
});
} catch (error: any) {
logger.error("자식 카테고리 값 조회 실패", { error: error.message });
return res.status(500).json({
success: false,
message: "자식 카테고리 값 조회에 실패했습니다.",
error: error.message,
});
}
};
2025-12-18 15:16:34 +09:00
/**
*
* ( )
*/
export const getCategoryValueCascadingMappingsByTable = async (
req: AuthenticatedRequest,
res: Response
) => {
try {
const { tableName } = req.params;
const companyCode = req.user?.companyCode || "*";
if (!tableName) {
return res.status(400).json({
success: false,
message: "테이블명이 필요합니다.",
});
}
// 해당 테이블이 자식 테이블인 연쇄관계 그룹 찾기
let groupQuery = `
SELECT
group_id,
relation_code,
child_column_name
FROM category_value_cascading_group
WHERE child_table_name = $1
AND is_active = 'Y'
`;
const groupParams: any[] = [tableName];
let paramIndex = 2;
// 멀티테넌시 적용
if (companyCode !== "*") {
groupQuery += ` AND (company_code = $${paramIndex} OR company_code = '*')`;
groupParams.push(companyCode);
}
const groupResult = await pool.query(groupQuery, groupParams);
if (groupResult.rowCount === 0) {
// 연쇄관계가 없으면 빈 객체 반환
return res.json({
success: true,
data: {},
});
}
// 각 그룹의 매핑 조회
const mappings: Record<string, Array<{ code: string; label: string }>> = {};
for (const group of groupResult.rows) {
const mappingQuery = `
SELECT DISTINCT
child_value_code as code,
child_value_label as label
FROM category_value_cascading_mapping
WHERE group_id = $1
AND is_active = 'Y'
ORDER BY child_value_label
`;
const mappingResult = await pool.query(mappingQuery, [group.group_id]);
if (mappingResult.rowCount && mappingResult.rowCount > 0) {
mappings[group.child_column_name] = mappingResult.rows;
}
}
logger.info("테이블별 연쇄관계 매핑 조회", {
tableName,
groupCount: groupResult.rowCount,
columnMappings: Object.keys(mappings),
});
return res.json({
success: true,
data: mappings,
});
} catch (error: any) {
logger.error("테이블별 연쇄관계 매핑 조회 실패", { error: error.message });
return res.status(500).json({
success: false,
message: "연쇄관계 매핑 조회에 실패했습니다.",
error: error.message,
});
}
};