feat: enhance data mapping and entity join handling in components
- Updated ButtonPrimaryComponent to utilize entity join metadata for improved data mapping. - Introduced getEntityJoinColumns method in TableListComponent to retrieve entity join column metadata. - Enhanced applyMappingRules function to support optional entity join columns, allowing for more flexible data resolution. - Added utility functions to build join alias maps and resolve values from entity joins, improving data handling capabilities. These enhancements aim to provide a more robust and dynamic data mapping experience, facilitating better integration of entity relationships in the application.
This commit is contained in:
parent
2772c2296c
commit
a6aa57fece
|
|
@ -938,8 +938,14 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
|||
effectiveMappingRules = multiTableMappings[0]?.mappingRules || [];
|
||||
}
|
||||
|
||||
// 소스 DataProvider에서 엔티티 조인 메타데이터 가져오기
|
||||
const entityJoinColumns = sourceProvider?.getEntityJoinColumns?.() || [];
|
||||
if (entityJoinColumns.length > 0) {
|
||||
console.log(`🔗 [ButtonPrimary] 엔티티 조인 메타데이터 ${entityJoinColumns.length}개 감지`, entityJoinColumns);
|
||||
}
|
||||
|
||||
const mappedData = sourceData.map((row) => {
|
||||
const mappedRow = applyMappingRules(row, effectiveMappingRules);
|
||||
const mappedRow = applyMappingRules(row, effectiveMappingRules, entityJoinColumns);
|
||||
|
||||
return {
|
||||
...mappedRow,
|
||||
|
|
|
|||
|
|
@ -1164,6 +1164,17 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
setSelectedRows(new Set());
|
||||
setIsAllSelected(false);
|
||||
},
|
||||
|
||||
getEntityJoinColumns: () => {
|
||||
return (tableConfig.columns || [])
|
||||
.filter((col) => col.additionalJoinInfo)
|
||||
.map((col) => ({
|
||||
sourceTable: col.additionalJoinInfo!.sourceTable || tableConfig.selectedTable,
|
||||
sourceColumn: col.additionalJoinInfo!.sourceColumn,
|
||||
joinAlias: col.additionalJoinInfo!.joinAlias,
|
||||
referenceTable: col.additionalJoinInfo!.referenceTable,
|
||||
}));
|
||||
},
|
||||
};
|
||||
|
||||
// DataReceivable 인터페이스 구현
|
||||
|
|
|
|||
|
|
@ -8,21 +8,27 @@ import type {
|
|||
Condition,
|
||||
TransformFunction,
|
||||
} from "@/types/screen-embedding";
|
||||
import type { EntityJoinColumnMeta } from "@/types/data-transfer";
|
||||
import { logger } from "./logger";
|
||||
|
||||
/**
|
||||
* 매핑 규칙 적용
|
||||
* @param data 배열 또는 단일 객체
|
||||
* @param rules 매핑 규칙 배열
|
||||
* @param entityJoinColumns 엔티티 조인 메타데이터 (선택적) - sourceField 값이 비었을 때 조인 alias에서 해결
|
||||
* @returns 매핑된 배열
|
||||
*/
|
||||
export function applyMappingRules(data: any[] | any, rules: MappingRule[]): any[] {
|
||||
export function applyMappingRules(
|
||||
data: any[] | any,
|
||||
rules: MappingRule[],
|
||||
entityJoinColumns?: EntityJoinColumnMeta[],
|
||||
): any[] {
|
||||
// 빈 데이터 처리
|
||||
if (!data) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 🆕 배열이 아닌 경우 배열로 변환
|
||||
// 배열이 아닌 경우 배열로 변환
|
||||
const dataArray = Array.isArray(data) ? data : [data];
|
||||
|
||||
if (dataArray.length === 0) {
|
||||
|
|
@ -42,22 +48,34 @@ export function applyMappingRules(data: any[] | any, rules: MappingRule[]): any[
|
|||
return [applyTransformRules(dataArray, rules)];
|
||||
}
|
||||
|
||||
// 엔티티 조인 alias 역방향 맵 구성: { referenceColumn → joinAlias }
|
||||
// ex) joinAlias "part_code_item_name" → sourceColumn "part_code", referenceColumn "item_name"
|
||||
const joinAliasMap = buildJoinAliasMap(entityJoinColumns);
|
||||
|
||||
// 일반 매핑 (각 행에 대해 매핑)
|
||||
// 🆕 원본 데이터를 복사한 후 매핑 규칙 적용 (매핑되지 않은 필드도 유지)
|
||||
// 원본 데이터를 복사한 후 매핑 규칙 적용 (매핑되지 않은 필드도 유지)
|
||||
return dataArray.map((row) => {
|
||||
// 원본 데이터 복사
|
||||
const mappedRow: any = { ...row };
|
||||
|
||||
for (const rule of rules) {
|
||||
// sourceField와 targetField가 모두 있어야 매핑 적용
|
||||
if (!rule.sourceField || !rule.targetField) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const sourceValue = getNestedValue(row, rule.sourceField);
|
||||
let sourceValue = getNestedValue(row, rule.sourceField);
|
||||
|
||||
// sourceField 값이 비어있으면 엔티티 조인 alias에서 해결 시도
|
||||
if (isEmptyValue(sourceValue) && joinAliasMap.size > 0) {
|
||||
sourceValue = resolveFromEntityJoin(row, rule.targetField, joinAliasMap);
|
||||
if (sourceValue !== undefined) {
|
||||
logger.info(
|
||||
`[dataMapping] 엔티티 조인 해결: ${rule.sourceField}(비어있음) → targetField "${rule.targetField}" → alias에서 값 획득`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const targetValue = sourceValue ?? rule.defaultValue;
|
||||
|
||||
// 소스 필드와 타겟 필드가 다르면 소스 필드 제거 후 타겟 필드에 설정
|
||||
if (rule.sourceField !== rule.targetField) {
|
||||
delete mappedRow[rule.sourceField];
|
||||
}
|
||||
|
|
@ -69,6 +87,50 @@ export function applyMappingRules(data: any[] | any, rules: MappingRule[]): any[
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 엔티티 조인 alias에서 역방향 참조 맵 구성
|
||||
* joinAlias 네이밍 규칙: {sourceColumn}_{referenceColumn}
|
||||
* 예: "part_code_item_name" → sourceColumn="part_code", referenceColumn="item_name"
|
||||
*
|
||||
* 반환 Map: referenceColumn → joinAlias
|
||||
* 예: "item_name" → "part_code_item_name"
|
||||
*/
|
||||
function buildJoinAliasMap(
|
||||
entityJoinColumns?: EntityJoinColumnMeta[],
|
||||
): Map<string, string> {
|
||||
const map = new Map<string, string>();
|
||||
if (!entityJoinColumns || entityJoinColumns.length === 0) return map;
|
||||
|
||||
for (const meta of entityJoinColumns) {
|
||||
const prefix = `${meta.sourceColumn}_`;
|
||||
if (meta.joinAlias.startsWith(prefix)) {
|
||||
const referenceColumn = meta.joinAlias.slice(prefix.length);
|
||||
map.set(referenceColumn, meta.joinAlias);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 엔티티 조인 alias에서 targetField에 해당하는 값 해결
|
||||
* targetField 이름으로 조인 alias를 찾아 row에서 값을 가져옴
|
||||
*/
|
||||
function resolveFromEntityJoin(
|
||||
row: any,
|
||||
targetField: string,
|
||||
joinAliasMap: Map<string, string>,
|
||||
): any {
|
||||
const joinAlias = joinAliasMap.get(targetField);
|
||||
if (!joinAlias) return undefined;
|
||||
|
||||
const value = row[joinAlias];
|
||||
return isEmptyValue(value) ? undefined : value;
|
||||
}
|
||||
|
||||
function isEmptyValue(value: any): boolean {
|
||||
return value === null || value === undefined || value === "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 변환 함수 적용
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -157,6 +157,17 @@ export interface DataReceivable {
|
|||
getData(): any;
|
||||
}
|
||||
|
||||
/**
|
||||
* 엔티티 조인 컬럼 메타데이터
|
||||
* 소스 테이블의 FK가 참조 테이블과 어떻게 조인되었는지 정보
|
||||
*/
|
||||
export interface EntityJoinColumnMeta {
|
||||
sourceColumn: string;
|
||||
joinAlias: string;
|
||||
referenceTable: string;
|
||||
sourceTable?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 데이터 제공 가능한 컴포넌트 인터페이스
|
||||
* 데이터를 제공할 수 있는 컴포넌트가 구현해야 하는 인터페이스
|
||||
|
|
@ -180,5 +191,11 @@ export interface DataProvidable {
|
|||
* 선택 초기화 메서드
|
||||
*/
|
||||
clearSelection(): void;
|
||||
|
||||
/**
|
||||
* 엔티티 조인 컬럼 메타데이터 반환 (선택적)
|
||||
* 전달 매핑 시 조인 alias 해결에 사용
|
||||
*/
|
||||
getEntityJoinColumns?(): EntityJoinColumnMeta[];
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue