feat: 노드 기반 데이터 플로우 시스템 구현
- 노드 에디터 UI 구현 (React Flow 기반)
- TableSource, DataTransform, INSERT, UPDATE, DELETE, UPSERT 노드
- 드래그앤드롭 노드 추가 및 연결
- 속성 패널을 통한 노드 설정
- 실시간 필드 라벨 표시 (column_labels 테이블 연동)
- 데이터 변환 노드 (DataTransform) 기능
- EXPLODE: 구분자로 1개 행 → 여러 행 확장
- UPPERCASE, LOWERCASE, TRIM, CONCAT, SPLIT, REPLACE 등 12가지 변환 타입
- In-place 변환 지원 (타겟 필드 생략 시 소스 필드 덮어쓰기)
- 변환된 필드가 하위 액션 노드에 자동 전달
- 노드 플로우 실행 엔진
- 위상 정렬을 통한 노드 실행 순서 결정
- 레벨별 병렬 실행 (Promise.allSettled)
- 부분 실패 허용 (한 노드 실패 시 연결된 하위 노드만 스킵)
- 트랜잭션 기반 안전한 데이터 처리
- UPSERT 액션 로직 구현
- DB 제약 조건 없이 SELECT → UPDATE or INSERT 방식
- 복합 충돌 키 지원 (예: sales_no + product_name)
- 파라미터 인덱스 정확한 매핑
- 데이터 소스 자동 감지
- 테이블 선택 데이터 (selectedRowsData) 자동 주입
- 폼 입력 데이터 (formData) 자동 주입
- TableSource 노드가 외부 데이터 우선 사용
- 버튼 컴포넌트 통합
- 기존 관계 실행 + 새 노드 플로우 실행 하이브리드 지원
- 노드 플로우 선택 UI 추가
- API 클라이언트 통합 (Axios)
- 개발 문서 작성
- 노드 기반 제어 시스템 개선 계획
- 노드 연결 규칙 설계
- 노드 실행 엔진 설계
- 노드 구조 개선안
- 버튼 통합 분석
2025-10-02 16:22:29 +09:00
|
|
|
/**
|
|
|
|
|
* 노드 플로우 기반 버튼 실행기
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import { executeNodeFlow, ExecutionResult } from "../api/nodeFlows";
|
|
|
|
|
import { logger } from "../utils/logger";
|
|
|
|
|
import { toast } from "sonner";
|
|
|
|
|
import type { ButtonDataflowConfig, ExtendedControlContext } from "@/types/control-management";
|
|
|
|
|
|
|
|
|
|
export interface ButtonExecutionContext {
|
|
|
|
|
buttonId: string;
|
|
|
|
|
screenId?: number;
|
|
|
|
|
companyCode?: string;
|
|
|
|
|
userId?: string;
|
|
|
|
|
formData: Record<string, any>;
|
|
|
|
|
selectedRows?: any[];
|
|
|
|
|
selectedRowsData?: Record<string, any>[];
|
2025-10-24 14:11:12 +09:00
|
|
|
controlDataSource?: "form" | "table-selection" | "table-all" | "flow-selection" | "flow-step-all" | "both" | "all-sources";
|
|
|
|
|
|
|
|
|
|
// 🆕 테이블 전체 데이터 (table-all 모드용)
|
|
|
|
|
tableAllData?: Record<string, any>[];
|
|
|
|
|
|
|
|
|
|
// 🆕 플로우 스텝 전체 데이터 (flow-step-all 모드용)
|
|
|
|
|
flowStepAllData?: Record<string, any>[];
|
|
|
|
|
flowStepId?: number;
|
|
|
|
|
|
|
|
|
|
// 🆕 플로우 선택 데이터 (flow-selection 모드용)
|
|
|
|
|
flowSelectedData?: Record<string, any>[];
|
|
|
|
|
|
feat: 노드 기반 데이터 플로우 시스템 구현
- 노드 에디터 UI 구현 (React Flow 기반)
- TableSource, DataTransform, INSERT, UPDATE, DELETE, UPSERT 노드
- 드래그앤드롭 노드 추가 및 연결
- 속성 패널을 통한 노드 설정
- 실시간 필드 라벨 표시 (column_labels 테이블 연동)
- 데이터 변환 노드 (DataTransform) 기능
- EXPLODE: 구분자로 1개 행 → 여러 행 확장
- UPPERCASE, LOWERCASE, TRIM, CONCAT, SPLIT, REPLACE 등 12가지 변환 타입
- In-place 변환 지원 (타겟 필드 생략 시 소스 필드 덮어쓰기)
- 변환된 필드가 하위 액션 노드에 자동 전달
- 노드 플로우 실행 엔진
- 위상 정렬을 통한 노드 실행 순서 결정
- 레벨별 병렬 실행 (Promise.allSettled)
- 부분 실패 허용 (한 노드 실패 시 연결된 하위 노드만 스킵)
- 트랜잭션 기반 안전한 데이터 처리
- UPSERT 액션 로직 구현
- DB 제약 조건 없이 SELECT → UPDATE or INSERT 방식
- 복합 충돌 키 지원 (예: sales_no + product_name)
- 파라미터 인덱스 정확한 매핑
- 데이터 소스 자동 감지
- 테이블 선택 데이터 (selectedRowsData) 자동 주입
- 폼 입력 데이터 (formData) 자동 주입
- TableSource 노드가 외부 데이터 우선 사용
- 버튼 컴포넌트 통합
- 기존 관계 실행 + 새 노드 플로우 실행 하이브리드 지원
- 노드 플로우 선택 UI 추가
- API 클라이언트 통합 (Axios)
- 개발 문서 작성
- 노드 기반 제어 시스템 개선 계획
- 노드 연결 규칙 설계
- 노드 실행 엔진 설계
- 노드 구조 개선안
- 버튼 통합 분석
2025-10-02 16:22:29 +09:00
|
|
|
onRefresh?: () => void;
|
|
|
|
|
onClose?: () => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface FlowExecutionResult {
|
|
|
|
|
success: boolean;
|
|
|
|
|
message: string;
|
|
|
|
|
executionTime?: number;
|
|
|
|
|
data?: ExecutionResult;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 노드 플로우 실행 함수
|
|
|
|
|
*/
|
|
|
|
|
export async function executeButtonWithFlow(
|
|
|
|
|
flowConfig: ButtonDataflowConfig["flowConfig"],
|
|
|
|
|
context: ButtonExecutionContext,
|
|
|
|
|
originalAction?: () => Promise<void>,
|
|
|
|
|
): Promise<FlowExecutionResult> {
|
|
|
|
|
if (!flowConfig) {
|
|
|
|
|
throw new Error("플로우 설정이 없습니다.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { flowId, flowName, executionTiming = "before" } = flowConfig;
|
|
|
|
|
|
|
|
|
|
logger.info(`🚀 노드 플로우 실행 시작:`, {
|
|
|
|
|
flowId,
|
|
|
|
|
flowName,
|
|
|
|
|
timing: executionTiming,
|
|
|
|
|
contextKeys: Object.keys(context),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// 컨텍스트 데이터 준비
|
|
|
|
|
const contextData = prepareContextData(context);
|
|
|
|
|
|
|
|
|
|
// 타이밍에 따라 실행
|
|
|
|
|
switch (executionTiming) {
|
|
|
|
|
case "before":
|
|
|
|
|
// 1. 플로우 먼저 실행
|
|
|
|
|
const beforeResult = await executeNodeFlow(flowId, contextData);
|
|
|
|
|
|
|
|
|
|
if (!beforeResult.success) {
|
|
|
|
|
toast.error(`플로우 실행 실패: ${beforeResult.message}`);
|
|
|
|
|
return {
|
|
|
|
|
success: false,
|
|
|
|
|
message: beforeResult.message,
|
|
|
|
|
data: beforeResult,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
toast.success(`플로우 실행 완료: ${flowName}`);
|
|
|
|
|
|
|
|
|
|
// 2. 원래 버튼 액션 실행
|
|
|
|
|
if (originalAction) {
|
|
|
|
|
await originalAction();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
success: true,
|
|
|
|
|
message: "플로우 및 버튼 액션이 성공적으로 실행되었습니다.",
|
|
|
|
|
executionTime: beforeResult.executionTime,
|
|
|
|
|
data: beforeResult,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
case "after":
|
|
|
|
|
// 1. 원래 버튼 액션 먼저 실행
|
|
|
|
|
if (originalAction) {
|
|
|
|
|
await originalAction();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. 플로우 실행
|
|
|
|
|
const afterResult = await executeNodeFlow(flowId, contextData);
|
|
|
|
|
|
|
|
|
|
if (!afterResult.success) {
|
|
|
|
|
toast.warning(`버튼 액션은 성공했으나 플로우 실행 실패: ${afterResult.message}`);
|
|
|
|
|
} else {
|
|
|
|
|
toast.success(`플로우 실행 완료: ${flowName}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
success: afterResult.success,
|
|
|
|
|
message: afterResult.message,
|
|
|
|
|
executionTime: afterResult.executionTime,
|
|
|
|
|
data: afterResult,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
case "replace":
|
|
|
|
|
// 플로우만 실행 (원래 액션 대체)
|
|
|
|
|
const replaceResult = await executeNodeFlow(flowId, contextData);
|
|
|
|
|
|
|
|
|
|
if (!replaceResult.success) {
|
|
|
|
|
toast.error(`플로우 실행 실패: ${replaceResult.message}`);
|
|
|
|
|
} else {
|
|
|
|
|
toast.success(`플로우 실행 완료: ${flowName}`, {
|
|
|
|
|
description: `${replaceResult.summary.success}/${replaceResult.summary.total} 노드 성공`,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
success: replaceResult.success,
|
|
|
|
|
message: replaceResult.message,
|
|
|
|
|
executionTime: replaceResult.executionTime,
|
|
|
|
|
data: replaceResult,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
throw new Error(`지원하지 않는 실행 타이밍: ${executionTiming}`);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error("플로우 실행 오류:", error);
|
|
|
|
|
const errorMessage = error instanceof Error ? error.message : "알 수 없는 오류가 발생했습니다.";
|
|
|
|
|
toast.error(`플로우 실행 오류: ${errorMessage}`);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
success: false,
|
|
|
|
|
message: errorMessage,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 컨텍스트 데이터 준비
|
|
|
|
|
*/
|
|
|
|
|
function prepareContextData(context: ButtonExecutionContext): Record<string, any> {
|
2025-10-24 14:11:12 +09:00
|
|
|
// 🔥 controlDataSource 자동 감지 (명시적으로 설정되지 않은 경우)
|
|
|
|
|
let dataSource = context.controlDataSource;
|
|
|
|
|
|
|
|
|
|
if (!dataSource) {
|
|
|
|
|
// 1. 플로우 선택 데이터가 있으면 flow-selection
|
|
|
|
|
if (context.flowSelectedData && context.flowSelectedData.length > 0) {
|
|
|
|
|
dataSource = "flow-selection";
|
|
|
|
|
logger.info("🔄 자동 판단: flow-selection 모드 사용", {
|
|
|
|
|
flowSelectedDataLength: context.flowSelectedData.length,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// 2. 플로우 스텝 전체 데이터가 있으면 flow-step-all
|
|
|
|
|
else if (context.flowStepAllData && context.flowStepAllData.length > 0) {
|
|
|
|
|
dataSource = "flow-step-all";
|
|
|
|
|
logger.info("🔄 자동 판단: flow-step-all 모드 사용", {
|
|
|
|
|
flowStepAllDataLength: context.flowStepAllData.length,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// 3. 테이블 선택 데이터가 있으면 table-selection
|
|
|
|
|
else if (context.selectedRowsData && context.selectedRowsData.length > 0) {
|
|
|
|
|
dataSource = "table-selection";
|
|
|
|
|
logger.info("🔄 자동 판단: table-selection 모드 사용", {
|
|
|
|
|
selectedRowsDataLength: context.selectedRowsData.length,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// 4. 테이블 전체 데이터가 있으면 table-all
|
|
|
|
|
else if (context.tableAllData && context.tableAllData.length > 0) {
|
|
|
|
|
dataSource = "table-all";
|
|
|
|
|
logger.info("🔄 자동 판단: table-all 모드 사용", {
|
|
|
|
|
tableAllDataLength: context.tableAllData.length,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// 5. 폼 데이터만 있으면 form
|
|
|
|
|
else {
|
|
|
|
|
dataSource = "form";
|
|
|
|
|
logger.info("🔄 자동 판단: form 모드 사용");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const baseContext = {
|
feat: 노드 기반 데이터 플로우 시스템 구현
- 노드 에디터 UI 구현 (React Flow 기반)
- TableSource, DataTransform, INSERT, UPDATE, DELETE, UPSERT 노드
- 드래그앤드롭 노드 추가 및 연결
- 속성 패널을 통한 노드 설정
- 실시간 필드 라벨 표시 (column_labels 테이블 연동)
- 데이터 변환 노드 (DataTransform) 기능
- EXPLODE: 구분자로 1개 행 → 여러 행 확장
- UPPERCASE, LOWERCASE, TRIM, CONCAT, SPLIT, REPLACE 등 12가지 변환 타입
- In-place 변환 지원 (타겟 필드 생략 시 소스 필드 덮어쓰기)
- 변환된 필드가 하위 액션 노드에 자동 전달
- 노드 플로우 실행 엔진
- 위상 정렬을 통한 노드 실행 순서 결정
- 레벨별 병렬 실행 (Promise.allSettled)
- 부분 실패 허용 (한 노드 실패 시 연결된 하위 노드만 스킵)
- 트랜잭션 기반 안전한 데이터 처리
- UPSERT 액션 로직 구현
- DB 제약 조건 없이 SELECT → UPDATE or INSERT 방식
- 복합 충돌 키 지원 (예: sales_no + product_name)
- 파라미터 인덱스 정확한 매핑
- 데이터 소스 자동 감지
- 테이블 선택 데이터 (selectedRowsData) 자동 주입
- 폼 입력 데이터 (formData) 자동 주입
- TableSource 노드가 외부 데이터 우선 사용
- 버튼 컴포넌트 통합
- 기존 관계 실행 + 새 노드 플로우 실행 하이브리드 지원
- 노드 플로우 선택 UI 추가
- API 클라이언트 통합 (Axios)
- 개발 문서 작성
- 노드 기반 제어 시스템 개선 계획
- 노드 연결 규칙 설계
- 노드 실행 엔진 설계
- 노드 구조 개선안
- 버튼 통합 분석
2025-10-02 16:22:29 +09:00
|
|
|
buttonId: context.buttonId,
|
|
|
|
|
screenId: context.screenId,
|
|
|
|
|
companyCode: context.companyCode,
|
|
|
|
|
userId: context.userId,
|
2025-10-24 14:11:12 +09:00
|
|
|
controlDataSource: dataSource,
|
feat: 노드 기반 데이터 플로우 시스템 구현
- 노드 에디터 UI 구현 (React Flow 기반)
- TableSource, DataTransform, INSERT, UPDATE, DELETE, UPSERT 노드
- 드래그앤드롭 노드 추가 및 연결
- 속성 패널을 통한 노드 설정
- 실시간 필드 라벨 표시 (column_labels 테이블 연동)
- 데이터 변환 노드 (DataTransform) 기능
- EXPLODE: 구분자로 1개 행 → 여러 행 확장
- UPPERCASE, LOWERCASE, TRIM, CONCAT, SPLIT, REPLACE 등 12가지 변환 타입
- In-place 변환 지원 (타겟 필드 생략 시 소스 필드 덮어쓰기)
- 변환된 필드가 하위 액션 노드에 자동 전달
- 노드 플로우 실행 엔진
- 위상 정렬을 통한 노드 실행 순서 결정
- 레벨별 병렬 실행 (Promise.allSettled)
- 부분 실패 허용 (한 노드 실패 시 연결된 하위 노드만 스킵)
- 트랜잭션 기반 안전한 데이터 처리
- UPSERT 액션 로직 구현
- DB 제약 조건 없이 SELECT → UPDATE or INSERT 방식
- 복합 충돌 키 지원 (예: sales_no + product_name)
- 파라미터 인덱스 정확한 매핑
- 데이터 소스 자동 감지
- 테이블 선택 데이터 (selectedRowsData) 자동 주입
- 폼 입력 데이터 (formData) 자동 주입
- TableSource 노드가 외부 데이터 우선 사용
- 버튼 컴포넌트 통합
- 기존 관계 실행 + 새 노드 플로우 실행 하이브리드 지원
- 노드 플로우 선택 UI 추가
- API 클라이언트 통합 (Axios)
- 개발 문서 작성
- 노드 기반 제어 시스템 개선 계획
- 노드 연결 규칙 설계
- 노드 실행 엔진 설계
- 노드 구조 개선안
- 버튼 통합 분석
2025-10-02 16:22:29 +09:00
|
|
|
};
|
2025-10-24 14:11:12 +09:00
|
|
|
|
|
|
|
|
// 데이터 소스에 따라 데이터 준비
|
|
|
|
|
|
|
|
|
|
switch (dataSource) {
|
|
|
|
|
case "form":
|
|
|
|
|
return {
|
|
|
|
|
...baseContext,
|
|
|
|
|
formData: context.formData || {},
|
|
|
|
|
sourceData: [context.formData || {}], // 배열로 통일
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
case "table-selection":
|
|
|
|
|
return {
|
|
|
|
|
...baseContext,
|
|
|
|
|
formData: context.formData || {},
|
|
|
|
|
selectedRowsData: context.selectedRowsData || [],
|
|
|
|
|
sourceData: context.selectedRowsData || [],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
case "table-all":
|
|
|
|
|
return {
|
|
|
|
|
...baseContext,
|
|
|
|
|
formData: context.formData || {},
|
|
|
|
|
tableAllData: context.tableAllData || [],
|
|
|
|
|
sourceData: context.tableAllData || [],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
case "flow-selection":
|
|
|
|
|
return {
|
|
|
|
|
...baseContext,
|
|
|
|
|
formData: context.formData || {},
|
|
|
|
|
flowSelectedData: context.flowSelectedData || [],
|
|
|
|
|
sourceData: context.flowSelectedData || [],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
case "flow-step-all":
|
|
|
|
|
return {
|
|
|
|
|
...baseContext,
|
|
|
|
|
formData: context.formData || {},
|
|
|
|
|
flowStepAllData: context.flowStepAllData || [],
|
|
|
|
|
flowStepId: context.flowStepId,
|
|
|
|
|
sourceData: context.flowStepAllData || [],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
case "both":
|
|
|
|
|
// 폼 + 테이블 선택
|
|
|
|
|
return {
|
|
|
|
|
...baseContext,
|
|
|
|
|
formData: context.formData || {},
|
|
|
|
|
selectedRowsData: context.selectedRowsData || [],
|
|
|
|
|
sourceData: [
|
|
|
|
|
context.formData || {},
|
|
|
|
|
...(context.selectedRowsData || []),
|
|
|
|
|
],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
case "all-sources":
|
|
|
|
|
// 모든 소스 결합
|
|
|
|
|
return {
|
|
|
|
|
...baseContext,
|
|
|
|
|
formData: context.formData || {},
|
|
|
|
|
selectedRowsData: context.selectedRowsData || [],
|
|
|
|
|
tableAllData: context.tableAllData || [],
|
|
|
|
|
flowSelectedData: context.flowSelectedData || [],
|
|
|
|
|
flowStepAllData: context.flowStepAllData || [],
|
|
|
|
|
sourceData: [
|
|
|
|
|
context.formData || {},
|
|
|
|
|
...(context.selectedRowsData || []),
|
|
|
|
|
...(context.tableAllData || []),
|
|
|
|
|
...(context.flowSelectedData || []),
|
|
|
|
|
...(context.flowStepAllData || []),
|
|
|
|
|
].filter(item => Object.keys(item).length > 0), // 빈 객체 제거
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
logger.warn(`알 수 없는 데이터 소스: ${dataSource}, 기본값(form) 사용`);
|
|
|
|
|
return {
|
|
|
|
|
...baseContext,
|
|
|
|
|
formData: context.formData || {},
|
|
|
|
|
sourceData: [context.formData || {}],
|
|
|
|
|
};
|
|
|
|
|
}
|
feat: 노드 기반 데이터 플로우 시스템 구현
- 노드 에디터 UI 구현 (React Flow 기반)
- TableSource, DataTransform, INSERT, UPDATE, DELETE, UPSERT 노드
- 드래그앤드롭 노드 추가 및 연결
- 속성 패널을 통한 노드 설정
- 실시간 필드 라벨 표시 (column_labels 테이블 연동)
- 데이터 변환 노드 (DataTransform) 기능
- EXPLODE: 구분자로 1개 행 → 여러 행 확장
- UPPERCASE, LOWERCASE, TRIM, CONCAT, SPLIT, REPLACE 등 12가지 변환 타입
- In-place 변환 지원 (타겟 필드 생략 시 소스 필드 덮어쓰기)
- 변환된 필드가 하위 액션 노드에 자동 전달
- 노드 플로우 실행 엔진
- 위상 정렬을 통한 노드 실행 순서 결정
- 레벨별 병렬 실행 (Promise.allSettled)
- 부분 실패 허용 (한 노드 실패 시 연결된 하위 노드만 스킵)
- 트랜잭션 기반 안전한 데이터 처리
- UPSERT 액션 로직 구현
- DB 제약 조건 없이 SELECT → UPDATE or INSERT 방식
- 복합 충돌 키 지원 (예: sales_no + product_name)
- 파라미터 인덱스 정확한 매핑
- 데이터 소스 자동 감지
- 테이블 선택 데이터 (selectedRowsData) 자동 주입
- 폼 입력 데이터 (formData) 자동 주입
- TableSource 노드가 외부 데이터 우선 사용
- 버튼 컴포넌트 통합
- 기존 관계 실행 + 새 노드 플로우 실행 하이브리드 지원
- 노드 플로우 선택 UI 추가
- API 클라이언트 통합 (Axios)
- 개발 문서 작성
- 노드 기반 제어 시스템 개선 계획
- 노드 연결 규칙 설계
- 노드 실행 엔진 설계
- 노드 구조 개선안
- 버튼 통합 분석
2025-10-02 16:22:29 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 플로우 실행 결과 처리
|
|
|
|
|
*/
|
|
|
|
|
export function handleFlowExecutionResult(result: FlowExecutionResult, context: ButtonExecutionContext): void {
|
|
|
|
|
if (result.success) {
|
|
|
|
|
logger.info("✅ 플로우 실행 성공:", result);
|
|
|
|
|
|
|
|
|
|
// 성공 시 데이터 새로고침
|
|
|
|
|
if (context.onRefresh) {
|
|
|
|
|
context.onRefresh();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 실행 결과 요약 표시
|
|
|
|
|
if (result.data) {
|
|
|
|
|
const { summary } = result.data;
|
|
|
|
|
console.log("📊 플로우 실행 요약:", {
|
|
|
|
|
전체: summary.total,
|
|
|
|
|
성공: summary.success,
|
|
|
|
|
실패: summary.failed,
|
|
|
|
|
스킵: summary.skipped,
|
|
|
|
|
실행시간: `${result.executionTime}ms`,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
logger.error("❌ 플로우 실행 실패:", result);
|
|
|
|
|
|
|
|
|
|
// 실패한 노드 정보 표시
|
|
|
|
|
if (result.data) {
|
|
|
|
|
const failedNodes = result.data.nodes.filter((n) => n.status === "failed");
|
|
|
|
|
if (failedNodes.length > 0) {
|
|
|
|
|
console.error("❌ 실패한 노드들:", failedNodes);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|