반응형 및 테이블 리스트 컴포넌트 오류 수정
This commit is contained in:
@@ -295,6 +295,54 @@ export class DynamicFormService {
|
||||
}
|
||||
});
|
||||
|
||||
// 📝 RepeaterInput 데이터 처리 (JSON 배열을 개별 레코드로 분해)
|
||||
const repeaterData: Array<{
|
||||
data: Record<string, any>[];
|
||||
targetTable?: string;
|
||||
componentId: string;
|
||||
}> = [];
|
||||
Object.keys(dataToInsert).forEach((key) => {
|
||||
const value = dataToInsert[key];
|
||||
|
||||
// RepeaterInput 데이터인지 확인 (JSON 배열 문자열)
|
||||
if (
|
||||
typeof value === "string" &&
|
||||
value.trim().startsWith("[") &&
|
||||
value.trim().endsWith("]")
|
||||
) {
|
||||
try {
|
||||
const parsedArray = JSON.parse(value);
|
||||
if (Array.isArray(parsedArray) && parsedArray.length > 0) {
|
||||
console.log(
|
||||
`🔄 RepeaterInput 데이터 감지: ${key}, ${parsedArray.length}개 항목`
|
||||
);
|
||||
|
||||
// 컴포넌트 설정에서 targetTable 추출 (컴포넌트 ID를 통해)
|
||||
// 프론트엔드에서 { data: [...], targetTable: "..." } 형식으로 전달될 수 있음
|
||||
let targetTable: string | undefined;
|
||||
let actualData = parsedArray;
|
||||
|
||||
// 첫 번째 항목에 _targetTable이 있는지 확인 (프론트엔드에서 메타데이터 전달)
|
||||
if (parsedArray[0] && parsedArray[0]._targetTable) {
|
||||
targetTable = parsedArray[0]._targetTable;
|
||||
actualData = parsedArray.map(
|
||||
({ _targetTable, ...item }) => item
|
||||
);
|
||||
}
|
||||
|
||||
repeaterData.push({
|
||||
data: actualData,
|
||||
targetTable,
|
||||
componentId: key,
|
||||
});
|
||||
delete dataToInsert[key]; // 원본 배열 데이터는 제거
|
||||
}
|
||||
} catch (parseError) {
|
||||
console.log(`⚠️ JSON 파싱 실패: ${key}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 존재하지 않는 컬럼 제거
|
||||
Object.keys(dataToInsert).forEach((key) => {
|
||||
if (!tableColumns.includes(key)) {
|
||||
@@ -305,6 +353,9 @@ export class DynamicFormService {
|
||||
}
|
||||
});
|
||||
|
||||
// RepeaterInput 데이터 처리 로직은 메인 저장 후에 처리
|
||||
// (각 Repeater가 다른 테이블에 저장될 수 있으므로)
|
||||
|
||||
console.log("🎯 실제 테이블에 삽입할 데이터:", {
|
||||
tableName,
|
||||
dataToInsert,
|
||||
@@ -388,6 +439,111 @@ export class DynamicFormService {
|
||||
// 결과를 표준 형식으로 변환
|
||||
const insertedRecord = Array.isArray(result) ? result[0] : result;
|
||||
|
||||
// 📝 RepeaterInput 데이터 저장 (각 Repeater를 해당 테이블에 저장)
|
||||
if (repeaterData.length > 0) {
|
||||
console.log(
|
||||
`🔄 RepeaterInput 데이터 저장 시작: ${repeaterData.length}개 Repeater`
|
||||
);
|
||||
|
||||
for (const repeater of repeaterData) {
|
||||
const targetTableName = repeater.targetTable || tableName;
|
||||
console.log(
|
||||
`📝 Repeater "${repeater.componentId}" → 테이블 "${targetTableName}"에 ${repeater.data.length}개 항목 저장`
|
||||
);
|
||||
|
||||
// 대상 테이블의 컬럼 및 기본키 정보 조회
|
||||
const targetTableColumns =
|
||||
await this.getTableColumns(targetTableName);
|
||||
const targetPrimaryKeys = await this.getPrimaryKeys(targetTableName);
|
||||
|
||||
// 컬럼명만 추출
|
||||
const targetColumnNames = targetTableColumns.map(
|
||||
(col) => col.columnName
|
||||
);
|
||||
|
||||
// 각 항목을 저장
|
||||
for (let i = 0; i < repeater.data.length; i++) {
|
||||
const item = repeater.data[i];
|
||||
const itemData: Record<string, any> = {
|
||||
...item,
|
||||
created_by,
|
||||
updated_by,
|
||||
regdate: new Date(),
|
||||
};
|
||||
|
||||
// 대상 테이블에 존재하는 컬럼만 필터링
|
||||
Object.keys(itemData).forEach((key) => {
|
||||
if (!targetColumnNames.includes(key)) {
|
||||
delete itemData[key];
|
||||
}
|
||||
});
|
||||
|
||||
// 타입 변환 적용
|
||||
Object.keys(itemData).forEach((columnName) => {
|
||||
const column = targetTableColumns.find(
|
||||
(col) => col.columnName === columnName
|
||||
);
|
||||
if (column) {
|
||||
itemData[columnName] = this.convertValueForPostgreSQL(
|
||||
itemData[columnName],
|
||||
column.dataType
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// UPSERT 쿼리 생성
|
||||
const itemColumns = Object.keys(itemData);
|
||||
const itemValues: any[] = Object.values(itemData);
|
||||
const itemPlaceholders = itemValues
|
||||
.map((_, index) => `$${index + 1}`)
|
||||
.join(", ");
|
||||
|
||||
let itemUpsertQuery: string;
|
||||
if (targetPrimaryKeys.length > 0) {
|
||||
const conflictColumns = targetPrimaryKeys.join(", ");
|
||||
const updateSet = itemColumns
|
||||
.filter((col) => !targetPrimaryKeys.includes(col))
|
||||
.map((col) => `${col} = EXCLUDED.${col}`)
|
||||
.join(", ");
|
||||
|
||||
if (updateSet) {
|
||||
itemUpsertQuery = `
|
||||
INSERT INTO ${targetTableName} (${itemColumns.join(", ")})
|
||||
VALUES (${itemPlaceholders})
|
||||
ON CONFLICT (${conflictColumns})
|
||||
DO UPDATE SET ${updateSet}
|
||||
RETURNING *
|
||||
`;
|
||||
} else {
|
||||
itemUpsertQuery = `
|
||||
INSERT INTO ${targetTableName} (${itemColumns.join(", ")})
|
||||
VALUES (${itemPlaceholders})
|
||||
ON CONFLICT (${conflictColumns})
|
||||
DO NOTHING
|
||||
RETURNING *
|
||||
`;
|
||||
}
|
||||
} else {
|
||||
itemUpsertQuery = `
|
||||
INSERT INTO ${targetTableName} (${itemColumns.join(", ")})
|
||||
VALUES (${itemPlaceholders})
|
||||
RETURNING *
|
||||
`;
|
||||
}
|
||||
|
||||
console.log(
|
||||
` 📝 항목 ${i + 1}/${repeater.data.length} 저장:`,
|
||||
itemData
|
||||
);
|
||||
await query<any>(itemUpsertQuery, itemValues);
|
||||
}
|
||||
|
||||
console.log(` ✅ Repeater "${repeater.componentId}" 저장 완료`);
|
||||
}
|
||||
|
||||
console.log(`✅ 모든 RepeaterInput 데이터 저장 완료`);
|
||||
}
|
||||
|
||||
// 🔥 조건부 연결 실행 (INSERT 트리거)
|
||||
try {
|
||||
if (company_code) {
|
||||
@@ -1114,6 +1270,31 @@ export class DynamicFormService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 테이블의 기본키 컬럼명 목록 조회
|
||||
*/
|
||||
async getPrimaryKeys(tableName: string): Promise<string[]> {
|
||||
try {
|
||||
console.log("🔑 서비스: 테이블 기본키 조회 시작:", { tableName });
|
||||
|
||||
const result = await query<{ column_name: string }>(
|
||||
`SELECT a.attname AS column_name
|
||||
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]
|
||||
);
|
||||
|
||||
const primaryKeys = result.map((row) => row.column_name);
|
||||
console.log("✅ 서비스: 테이블 기본키 조회 성공:", primaryKeys);
|
||||
|
||||
return primaryKeys;
|
||||
} catch (error) {
|
||||
console.error("❌ 서비스: 테이블 기본키 조회 실패:", error);
|
||||
throw new Error(`테이블 기본키 조회 실패: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 제어관리 실행 (화면에 설정된 경우)
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user