분할패널 설정변경
This commit is contained in:
@@ -2185,3 +2185,67 @@ export async function multiTableSave(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 두 테이블 간의 엔티티 관계 자동 감지
|
||||
* GET /api/table-management/tables/entity-relations?leftTable=xxx&rightTable=yyy
|
||||
*
|
||||
* column_labels에서 정의된 엔티티/카테고리 타입 설정을 기반으로
|
||||
* 두 테이블 간의 외래키 관계를 자동으로 감지합니다.
|
||||
*/
|
||||
export async function getTableEntityRelations(
|
||||
req: AuthenticatedRequest,
|
||||
res: Response
|
||||
): Promise<void> {
|
||||
try {
|
||||
const { leftTable, rightTable } = req.query;
|
||||
|
||||
logger.info(`=== 테이블 엔티티 관계 조회 시작: ${leftTable} <-> ${rightTable} ===`);
|
||||
|
||||
if (!leftTable || !rightTable) {
|
||||
const response: ApiResponse<null> = {
|
||||
success: false,
|
||||
message: "leftTable과 rightTable 파라미터가 필요합니다.",
|
||||
error: {
|
||||
code: "MISSING_PARAMETERS",
|
||||
details: "leftTable과 rightTable 쿼리 파라미터가 필요합니다.",
|
||||
},
|
||||
};
|
||||
res.status(400).json(response);
|
||||
return;
|
||||
}
|
||||
|
||||
const tableManagementService = new TableManagementService();
|
||||
const relations = await tableManagementService.detectTableEntityRelations(
|
||||
String(leftTable),
|
||||
String(rightTable)
|
||||
);
|
||||
|
||||
logger.info(`테이블 엔티티 관계 조회 완료: ${relations.length}개 발견`);
|
||||
|
||||
const response: ApiResponse<any> = {
|
||||
success: true,
|
||||
message: `${relations.length}개의 엔티티 관계를 발견했습니다.`,
|
||||
data: {
|
||||
leftTable: String(leftTable),
|
||||
rightTable: String(rightTable),
|
||||
relations,
|
||||
},
|
||||
};
|
||||
|
||||
res.status(200).json(response);
|
||||
} catch (error) {
|
||||
logger.error("테이블 엔티티 관계 조회 중 오류 발생:", error);
|
||||
|
||||
const response: ApiResponse<null> = {
|
||||
success: false,
|
||||
message: "테이블 엔티티 관계 조회 중 오류가 발생했습니다.",
|
||||
error: {
|
||||
code: "ENTITY_RELATIONS_ERROR",
|
||||
details: error instanceof Error ? error.message : "Unknown error",
|
||||
},
|
||||
};
|
||||
|
||||
res.status(500).json(response);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
toggleLogTable,
|
||||
getCategoryColumnsByMenu, // 🆕 메뉴별 카테고리 컬럼 조회
|
||||
multiTableSave, // 🆕 범용 다중 테이블 저장
|
||||
getTableEntityRelations, // 🆕 두 테이블 간 엔티티 관계 조회
|
||||
} from "../controllers/tableManagementController";
|
||||
|
||||
const router = express.Router();
|
||||
@@ -38,6 +39,15 @@ router.use(authenticateToken);
|
||||
*/
|
||||
router.get("/tables", getTableList);
|
||||
|
||||
/**
|
||||
* 두 테이블 간 엔티티 관계 조회
|
||||
* GET /api/table-management/tables/entity-relations?leftTable=xxx&rightTable=yyy
|
||||
*
|
||||
* column_labels에서 정의된 엔티티/카테고리 타입 설정을 기반으로
|
||||
* 두 테이블 간의 외래키 관계를 자동으로 감지합니다.
|
||||
*/
|
||||
router.get("/tables/entity-relations", getTableEntityRelations);
|
||||
|
||||
/**
|
||||
* 테이블 컬럼 정보 조회
|
||||
* GET /api/table-management/tables/:tableName/columns
|
||||
|
||||
@@ -1306,6 +1306,41 @@ export class TableManagementService {
|
||||
paramCount: number;
|
||||
} | null> {
|
||||
try {
|
||||
// 🆕 배열 값 처리 (다중 값 검색 - 분할패널 엔티티 타입에서 "2,3" 형태 지원)
|
||||
// 좌측에서 "2"를 선택해도, 우측에서 "2,3"을 가진 행이 표시되도록 함
|
||||
if (Array.isArray(value) && value.length > 0) {
|
||||
// 배열의 각 값에 대해 OR 조건으로 검색
|
||||
// 우측 컬럼에 "2,3" 같은 다중 값이 있을 수 있으므로
|
||||
// 각 값을 LIKE 또는 = 조건으로 처리
|
||||
const conditions: string[] = [];
|
||||
const values: any[] = [];
|
||||
|
||||
value.forEach((v: any, idx: number) => {
|
||||
const safeValue = String(v).trim();
|
||||
// 정확히 일치하거나, 콤마로 구분된 값 중 하나로 포함
|
||||
// 예: "2,3" 컬럼에서 "2"를 찾으려면:
|
||||
// - 정확히 "2"
|
||||
// - "2," 로 시작
|
||||
// - ",2" 로 끝남
|
||||
// - ",2," 중간에 포함
|
||||
const paramBase = paramIndex + (idx * 4);
|
||||
conditions.push(`(
|
||||
${columnName}::text = $${paramBase} OR
|
||||
${columnName}::text LIKE $${paramBase + 1} OR
|
||||
${columnName}::text LIKE $${paramBase + 2} OR
|
||||
${columnName}::text LIKE $${paramBase + 3}
|
||||
)`);
|
||||
values.push(safeValue, `${safeValue},%`, `%,${safeValue}`, `%,${safeValue},%`);
|
||||
});
|
||||
|
||||
logger.info(`🔍 다중 값 배열 검색: ${columnName} IN [${value.join(", ")}]`);
|
||||
return {
|
||||
whereClause: `(${conditions.join(" OR ")})`,
|
||||
values,
|
||||
paramCount: values.length,
|
||||
};
|
||||
}
|
||||
|
||||
// 🔧 파이프로 구분된 문자열 처리 (다중선택 또는 날짜 범위)
|
||||
if (typeof value === "string" && value.includes("|")) {
|
||||
const columnInfo = await this.getColumnWebTypeInfo(
|
||||
@@ -4630,4 +4665,101 @@ export class TableManagementService {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 두 테이블 간의 엔티티 관계 자동 감지
|
||||
* column_labels에서 엔티티 타입 설정을 기반으로 테이블 간 관계를 찾습니다.
|
||||
*
|
||||
* @param leftTable 좌측 테이블명
|
||||
* @param rightTable 우측 테이블명
|
||||
* @returns 감지된 엔티티 관계 배열
|
||||
*/
|
||||
async detectTableEntityRelations(
|
||||
leftTable: string,
|
||||
rightTable: string
|
||||
): Promise<Array<{
|
||||
leftColumn: string;
|
||||
rightColumn: string;
|
||||
direction: "left_to_right" | "right_to_left";
|
||||
inputType: string;
|
||||
displayColumn?: string;
|
||||
}>> {
|
||||
try {
|
||||
logger.info(`두 테이블 간 엔티티 관계 감지 시작: ${leftTable} <-> ${rightTable}`);
|
||||
|
||||
const relations: Array<{
|
||||
leftColumn: string;
|
||||
rightColumn: string;
|
||||
direction: "left_to_right" | "right_to_left";
|
||||
inputType: string;
|
||||
displayColumn?: string;
|
||||
}> = [];
|
||||
|
||||
// 1. 우측 테이블에서 좌측 테이블을 참조하는 엔티티 컬럼 찾기
|
||||
// 예: right_table의 customer_id -> left_table(customer_mng)의 customer_code
|
||||
const rightToLeftRels = await query<{
|
||||
column_name: string;
|
||||
reference_column: string;
|
||||
input_type: string;
|
||||
display_column: string | null;
|
||||
}>(
|
||||
`SELECT column_name, reference_column, input_type, display_column
|
||||
FROM column_labels
|
||||
WHERE table_name = $1
|
||||
AND input_type IN ('entity', 'category')
|
||||
AND reference_table = $2
|
||||
AND reference_column IS NOT NULL
|
||||
AND reference_column != ''`,
|
||||
[rightTable, leftTable]
|
||||
);
|
||||
|
||||
for (const rel of rightToLeftRels) {
|
||||
relations.push({
|
||||
leftColumn: rel.reference_column,
|
||||
rightColumn: rel.column_name,
|
||||
direction: "right_to_left",
|
||||
inputType: rel.input_type,
|
||||
displayColumn: rel.display_column || undefined,
|
||||
});
|
||||
}
|
||||
|
||||
// 2. 좌측 테이블에서 우측 테이블을 참조하는 엔티티 컬럼 찾기
|
||||
// 예: left_table의 item_id -> right_table(item_info)의 item_number
|
||||
const leftToRightRels = await query<{
|
||||
column_name: string;
|
||||
reference_column: string;
|
||||
input_type: string;
|
||||
display_column: string | null;
|
||||
}>(
|
||||
`SELECT column_name, reference_column, input_type, display_column
|
||||
FROM column_labels
|
||||
WHERE table_name = $1
|
||||
AND input_type IN ('entity', 'category')
|
||||
AND reference_table = $2
|
||||
AND reference_column IS NOT NULL
|
||||
AND reference_column != ''`,
|
||||
[leftTable, rightTable]
|
||||
);
|
||||
|
||||
for (const rel of leftToRightRels) {
|
||||
relations.push({
|
||||
leftColumn: rel.column_name,
|
||||
rightColumn: rel.reference_column,
|
||||
direction: "left_to_right",
|
||||
inputType: rel.input_type,
|
||||
displayColumn: rel.display_column || undefined,
|
||||
});
|
||||
}
|
||||
|
||||
logger.info(`엔티티 관계 감지 완료: ${relations.length}개 발견`);
|
||||
relations.forEach((rel, idx) => {
|
||||
logger.info(` ${idx + 1}. ${leftTable}.${rel.leftColumn} <-> ${rightTable}.${rel.rightColumn} (${rel.direction})`);
|
||||
});
|
||||
|
||||
return relations;
|
||||
} catch (error) {
|
||||
logger.error(`엔티티 관계 감지 실패: ${leftTable} <-> ${rightTable}`, error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user