Merge remote-tracking branch 'upstream/main'
All checks were successful
Build and Push Images / build-and-push (push) Successful in 13m31s
All checks were successful
Build and Push Images / build-and-push (push) Successful in 13m31s
This commit is contained in:
@@ -606,7 +606,7 @@ router.get(
|
||||
});
|
||||
}
|
||||
|
||||
const { enableEntityJoin, groupByColumns } = req.query;
|
||||
const { enableEntityJoin, groupByColumns, primaryKeyColumn } = req.query;
|
||||
const enableEntityJoinFlag =
|
||||
enableEntityJoin === "true" ||
|
||||
(typeof enableEntityJoin === "boolean" && enableEntityJoin);
|
||||
@@ -626,17 +626,22 @@ router.get(
|
||||
}
|
||||
}
|
||||
|
||||
// 🆕 primaryKeyColumn 파싱
|
||||
const primaryKeyColumnStr = typeof primaryKeyColumn === "string" ? primaryKeyColumn : undefined;
|
||||
|
||||
console.log(`🔍 레코드 상세 조회: ${tableName}/${id}`, {
|
||||
enableEntityJoin: enableEntityJoinFlag,
|
||||
groupByColumns: groupByColumnsArray,
|
||||
primaryKeyColumn: primaryKeyColumnStr,
|
||||
});
|
||||
|
||||
// 레코드 상세 조회 (Entity Join 옵션 + 그룹핑 옵션 포함)
|
||||
// 레코드 상세 조회 (Entity Join 옵션 + 그룹핑 옵션 + Primary Key 컬럼 포함)
|
||||
const result = await dataService.getRecordDetail(
|
||||
tableName,
|
||||
id,
|
||||
enableEntityJoinFlag,
|
||||
groupByColumnsArray
|
||||
groupByColumnsArray,
|
||||
primaryKeyColumnStr // 🆕 Primary Key 컬럼명 전달
|
||||
);
|
||||
|
||||
if (!result.success) {
|
||||
|
||||
@@ -490,7 +490,8 @@ class DataService {
|
||||
tableName: string,
|
||||
id: string | number,
|
||||
enableEntityJoin: boolean = false,
|
||||
groupByColumns: string[] = []
|
||||
groupByColumns: string[] = [],
|
||||
primaryKeyColumn?: string // 🆕 클라이언트에서 전달한 Primary Key 컬럼명
|
||||
): Promise<ServiceResponse<any>> {
|
||||
try {
|
||||
// 테이블 접근 검증
|
||||
@@ -499,20 +500,30 @@ class DataService {
|
||||
return validation.error!;
|
||||
}
|
||||
|
||||
// Primary Key 컬럼 찾기
|
||||
const pkResult = await query<{ attname: string }>(
|
||||
`SELECT a.attname
|
||||
FROM pg_index i
|
||||
JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey)
|
||||
WHERE i.indrelid = $1::regclass AND i.indisprimary`,
|
||||
[tableName]
|
||||
);
|
||||
// 🆕 클라이언트에서 전달한 Primary Key 컬럼이 있으면 우선 사용
|
||||
let pkColumn = primaryKeyColumn || "";
|
||||
|
||||
// Primary Key 컬럼이 없으면 자동 감지
|
||||
if (!pkColumn) {
|
||||
const pkResult = await query<{ attname: string }>(
|
||||
`SELECT a.attname
|
||||
FROM pg_index i
|
||||
JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey)
|
||||
WHERE i.indrelid = $1::regclass AND i.indisprimary`,
|
||||
[tableName]
|
||||
);
|
||||
|
||||
let pkColumn = "id"; // 기본값
|
||||
if (pkResult.length > 0) {
|
||||
pkColumn = pkResult[0].attname;
|
||||
pkColumn = "id"; // 기본값
|
||||
if (pkResult.length > 0) {
|
||||
pkColumn = pkResult[0].attname;
|
||||
}
|
||||
console.log(`🔑 [getRecordDetail] 자동 감지된 Primary Key:`, pkResult);
|
||||
} else {
|
||||
console.log(`🔑 [getRecordDetail] 클라이언트 제공 Primary Key: ${pkColumn}`);
|
||||
}
|
||||
|
||||
console.log(`🔑 [getRecordDetail] 테이블: ${tableName}, Primary Key 컬럼: ${pkColumn}, 조회 ID: ${id}`);
|
||||
|
||||
// 🆕 Entity Join이 활성화된 경우
|
||||
if (enableEntityJoin) {
|
||||
const { EntityJoinService } = await import("./entityJoinService");
|
||||
|
||||
@@ -334,6 +334,10 @@ export class EntityJoinService {
|
||||
);
|
||||
});
|
||||
|
||||
// 🔧 _label 별칭 중복 방지를 위한 Set
|
||||
// 같은 sourceColumn에서 여러 조인 설정이 있을 때 _label은 첫 번째만 생성
|
||||
const generatedLabelAliases = new Set<string>();
|
||||
|
||||
const joinColumns = joinConfigs
|
||||
.map((config) => {
|
||||
const aliasKey = `${config.referenceTable}:${config.sourceColumn}`;
|
||||
@@ -368,16 +372,26 @@ export class EntityJoinService {
|
||||
|
||||
// _label 필드도 함께 SELECT (프론트엔드 getColumnUniqueValues용)
|
||||
// sourceColumn_label 형식으로 추가
|
||||
resultColumns.push(
|
||||
`COALESCE(${alias}.${col}::TEXT, '') AS ${config.sourceColumn}_label`
|
||||
);
|
||||
// 🔧 중복 방지: 같은 sourceColumn에서 _label은 첫 번째만 생성
|
||||
const labelAlias = `${config.sourceColumn}_label`;
|
||||
if (!generatedLabelAliases.has(labelAlias)) {
|
||||
resultColumns.push(
|
||||
`COALESCE(${alias}.${col}::TEXT, '') AS ${labelAlias}`
|
||||
);
|
||||
generatedLabelAliases.add(labelAlias);
|
||||
}
|
||||
|
||||
// 🆕 referenceColumn (PK)도 항상 SELECT (parentDataMapping용)
|
||||
// 예: customer_code, item_number 등
|
||||
// col과 동일해도 별도의 alias로 추가 (customer_code as customer_code)
|
||||
resultColumns.push(
|
||||
`COALESCE(${alias}.${config.referenceColumn}::TEXT, '') AS ${config.referenceColumn}`
|
||||
);
|
||||
// 🔧 중복 방지: referenceColumn도 한 번만 추가
|
||||
const refColAlias = config.referenceColumn;
|
||||
if (!generatedLabelAliases.has(refColAlias)) {
|
||||
resultColumns.push(
|
||||
`COALESCE(${alias}.${config.referenceColumn}::TEXT, '') AS ${refColAlias}`
|
||||
);
|
||||
generatedLabelAliases.add(refColAlias);
|
||||
}
|
||||
} else {
|
||||
resultColumns.push(
|
||||
`COALESCE(main.${col}::TEXT, '') AS ${config.aliasColumn}`
|
||||
@@ -392,6 +406,11 @@ export class EntityJoinService {
|
||||
|
||||
const individualAlias = `${config.sourceColumn}_${col}`;
|
||||
|
||||
// 🔧 중복 방지: 같은 alias가 이미 생성되었으면 스킵
|
||||
if (generatedLabelAliases.has(individualAlias)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isJoinTableColumn) {
|
||||
// 조인 테이블 컬럼은 조인 별칭 사용
|
||||
resultColumns.push(
|
||||
@@ -403,6 +422,7 @@ export class EntityJoinService {
|
||||
`COALESCE(main.${col}::TEXT, '') AS ${individualAlias}`
|
||||
);
|
||||
}
|
||||
generatedLabelAliases.add(individualAlias);
|
||||
});
|
||||
|
||||
// 🆕 referenceColumn (PK)도 함께 SELECT (parentDataMapping용)
|
||||
@@ -410,11 +430,13 @@ export class EntityJoinService {
|
||||
config.referenceTable && config.referenceTable !== tableName;
|
||||
if (
|
||||
isJoinTableColumn &&
|
||||
!displayColumns.includes(config.referenceColumn)
|
||||
!displayColumns.includes(config.referenceColumn) &&
|
||||
!generatedLabelAliases.has(config.referenceColumn) // 🔧 중복 방지
|
||||
) {
|
||||
resultColumns.push(
|
||||
`COALESCE(${alias}.${config.referenceColumn}::TEXT, '') AS ${config.referenceColumn}`
|
||||
);
|
||||
generatedLabelAliases.add(config.referenceColumn);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -921,11 +921,12 @@ export class TableManagementService {
|
||||
...layout.properties,
|
||||
widgetType: inputType,
|
||||
inputType: inputType,
|
||||
// componentConfig 내부의 type도 업데이트
|
||||
// componentConfig 내부의 type, inputType, webType 모두 업데이트
|
||||
componentConfig: {
|
||||
...layout.properties?.componentConfig,
|
||||
type: newComponentType,
|
||||
inputType: inputType,
|
||||
webType: inputType, // 프론트엔드 SelectBasicComponent에서 카테고리 로딩 여부 판단에 사용
|
||||
},
|
||||
};
|
||||
|
||||
@@ -941,7 +942,7 @@ export class TableManagementService {
|
||||
);
|
||||
|
||||
logger.info(
|
||||
`화면 레이아웃 업데이트: screen_id=${layout.screen_id}, component_id=${layout.component_id}, widgetType=${inputType}, componentType=${newComponentType}`
|
||||
`화면 레이아웃 업데이트: screen_id=${layout.screen_id}, component_id=${layout.component_id}, widgetType=${inputType}, webType=${inputType}, componentType=${newComponentType}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user