범용 폼모달 제어로직 연동 #320

Merged
kjs merged 2 commits from feature/screen-management into main 2025-12-31 10:54:29 +09:00
3 changed files with 183 additions and 0 deletions

View File

@ -214,6 +214,73 @@ router.delete("/:flowId", async (req: Request, res: Response) => {
}
});
/**
*
* GET /api/dataflow/node-flows/:flowId/source-table
* (tableSource, externalDBSource)
*/
router.get("/:flowId/source-table", async (req: Request, res: Response) => {
try {
const { flowId } = req.params;
const flow = await queryOne<{ flow_data: any }>(
`SELECT flow_data FROM node_flows WHERE flow_id = $1`,
[flowId]
);
if (!flow) {
return res.status(404).json({
success: false,
message: "플로우를 찾을 수 없습니다.",
});
}
const flowData =
typeof flow.flow_data === "string"
? JSON.parse(flow.flow_data)
: flow.flow_data;
const nodes = flowData.nodes || [];
// 소스 노드 찾기 (tableSource, externalDBSource 타입)
const sourceNode = nodes.find(
(node: any) =>
node.type === "tableSource" || node.type === "externalDBSource"
);
if (!sourceNode || !sourceNode.data?.tableName) {
return res.json({
success: true,
data: {
sourceTable: null,
sourceNodeType: null,
message: "소스 노드가 없거나 테이블명이 설정되지 않았습니다.",
},
});
}
logger.info(
`플로우 소스 테이블 조회: flowId=${flowId}, table=${sourceNode.data.tableName}`
);
return res.json({
success: true,
data: {
sourceTable: sourceNode.data.tableName,
sourceNodeType: sourceNode.type,
sourceNodeId: sourceNode.id,
displayName: sourceNode.data.displayName,
},
});
} catch (error) {
logger.error("플로우 소스 테이블 조회 실패:", error);
return res.status(500).json({
success: false,
message: "플로우 소스 테이블을 조회하지 못했습니다.",
});
}
});
/**
*
* POST /api/dataflow/node-flows/:flowId/execute

View File

@ -120,3 +120,41 @@ export interface NodeExecutionSummary {
duration?: number;
error?: string;
}
/**
*
*/
export interface FlowSourceTableInfo {
sourceTable: string | null;
sourceNodeType: string | null;
sourceNodeId?: string;
displayName?: string;
message?: string;
}
/**
*
* (tableSource, externalDBSource)
*/
export async function getFlowSourceTable(flowId: number): Promise<FlowSourceTableInfo> {
try {
const response = await apiClient.get<ApiResponse<FlowSourceTableInfo>>(
`/dataflow/node-flows/${flowId}/source-table`,
);
if (response.data.success && response.data.data) {
return response.data.data;
}
return {
sourceTable: null,
sourceNodeType: null,
message: response.data.message || "소스 테이블 정보를 가져올 수 없습니다.",
};
} catch (error) {
console.error("플로우 소스 테이블 조회 실패:", error);
return {
sourceTable: null,
sourceNodeType: null,
message: "API 호출 중 오류가 발생했습니다.",
};
}
}

View File

@ -1864,6 +1864,84 @@ export class ButtonActionExecutor {
console.log(`✅ [handleUniversalFormModalTableSectionSave] 완료: ${resultMessage}`);
toast.success(`저장 완료: ${resultMessage}`);
// 🆕 저장 성공 후 제어 관리 실행 (다중 테이블 저장 시 소스 테이블과 일치하는 섹션만 실행)
if (config.enableDataflowControl && config.dataflowConfig?.flowConfig?.flowId) {
const flowId = config.dataflowConfig.flowConfig.flowId;
console.log("🎯 [handleUniversalFormModalTableSectionSave] 제어 관리 실행 시작:", { flowId });
try {
// 플로우 소스 테이블 조회
const { getFlowSourceTable } = await import("@/lib/api/nodeFlows");
const flowSourceInfo = await getFlowSourceTable(flowId);
console.log("📊 [handleUniversalFormModalTableSectionSave] 플로우 소스 테이블:", flowSourceInfo);
if (flowSourceInfo.sourceTable) {
// 각 섹션 확인하여 소스 테이블과 일치하는 섹션 찾기
let controlExecuted = false;
for (const [sectionId, sectionItems] of Object.entries(tableSectionData)) {
const sectionConfig = sections.find((s: any) => s.id === sectionId);
const sectionTargetTable = sectionConfig?.tableConfig?.saveConfig?.targetTable || tableName;
console.log(`🔍 [handleUniversalFormModalTableSectionSave] 섹션 ${sectionId} 테이블 비교:`, {
sectionTargetTable,
flowSourceTable: flowSourceInfo.sourceTable,
isMatch: sectionTargetTable === flowSourceInfo.sourceTable,
});
// 소스 테이블과 일치하는 섹션만 제어 실행
if (sectionTargetTable === flowSourceInfo.sourceTable && sectionItems.length > 0) {
console.log(
`✅ [handleUniversalFormModalTableSectionSave] 섹션 ${sectionId} → 플로우 소스 테이블 일치! 제어 실행`,
);
// 공통 필드 + 해당 섹션 데이터 병합하여 sourceData 생성
const sourceData = sectionItems.map((item: any) => ({
...commonFieldsData,
...item,
}));
console.log(
`📦 [handleUniversalFormModalTableSectionSave] 제어 전달 데이터: ${sourceData.length}`,
sourceData[0],
);
// 제어 관리용 컨텍스트 생성
const controlContext: ButtonActionContext = {
...context,
selectedRowsData: sourceData,
formData: commonFieldsData,
};
// 제어 관리 실행
await this.executeAfterSaveControl(config, controlContext);
controlExecuted = true;
break; // 첫 번째 매칭 섹션만 실행
}
}
// 매칭되는 섹션이 없으면 메인 테이블 확인
if (!controlExecuted && tableName === flowSourceInfo.sourceTable) {
console.log("✅ [handleUniversalFormModalTableSectionSave] 메인 테이블 일치! 공통 필드로 제어 실행");
const controlContext: ButtonActionContext = {
...context,
selectedRowsData: [commonFieldsData],
formData: commonFieldsData,
};
await this.executeAfterSaveControl(config, controlContext);
}
} else {
console.log("⚠️ [handleUniversalFormModalTableSectionSave] 플로우 소스 테이블 없음 - 제어 스킵");
}
} catch (controlError) {
console.error("❌ [handleUniversalFormModalTableSectionSave] 제어 관리 실행 오류:", controlError);
// 제어 관리 실패는 저장 성공에 영향주지 않음
}
}
// 저장 성공 이벤트 발생
window.dispatchEvent(new CustomEvent("saveSuccess"));
window.dispatchEvent(new CustomEvent("refreshTable"));