refactor: 重连功能迁移重构,画布数据更新迁移

refactor
钟良源 1 month ago
parent 3396cf3d62
commit 05ed81fa5a

@ -0,0 +1,59 @@
const hasObjectValues = (value: any) => {
return Boolean(value && typeof value === 'object' && Object.keys(value).length > 0);
};
const hasInitialCanvasData = (initialData: any, useDefault: boolean) => {
if (!initialData) {
return false;
}
if (Array.isArray(initialData)) {
return initialData.length > 0;
}
if (useDefault) {
return (
hasObjectValues(initialData?.main?.components) ||
hasObjectValues(initialData?.components)
);
}
return hasObjectValues(initialData);
};
export const hasCanvasContent = (canvas: any) => {
return Boolean(
canvas &&
((Array.isArray(canvas.nodes) && canvas.nodes.length > 0) ||
(Array.isArray(canvas.edges) && canvas.edges.length > 0))
);
};
export const shouldUseCachedCanvas = (params: {
cachedCanvas: any;
initialData: any;
useDefault: boolean;
}) => {
const { cachedCanvas, initialData, useDefault } = params;
if (!cachedCanvas) {
return false;
}
if (hasCanvasContent(cachedCanvas)) {
return true;
}
return !hasInitialCanvasData(initialData, useDefault);
};
export const shouldPersistCanvas = (params: {
nodes: any[];
edges: any[];
isRunning?: boolean;
}) => {
if (params.isRunning) {
return false;
}
return hasCanvasContent(params);
};

@ -5,6 +5,7 @@ import { Dispatch } from 'redux';
import { import {
pauseApp, pauseApp,
reRunApp, reRunApp,
reconnectRun,
resumeApp, resumeApp,
runMainFlow, runMainFlow,
runSubFlow, runSubFlow,
@ -181,3 +182,21 @@ export const rerunWorkflow = async ({
Message.error('重跑失败'); Message.error('重跑失败');
} }
}; };
export const reconnectWorkflowRuntime = async (params: {
instanceId: string;
newSocketId: string;
dispatch: Dispatch<any>;
}) => {
const { instanceId, newSocketId, dispatch } = params;
dispatch(updateRuntimeId(instanceId));
dispatch(updateIsRunning(true));
const res: any = await reconnectRun({
instanceId,
newSocketId,
});
return res?.data || res;
};

@ -0,0 +1,43 @@
interface RuntimeReconnectApp {
id?: string | number;
key?: string | number;
instanceId?: string | number;
scheduled?: number | string | boolean;
}
interface RuntimeReconnectRequestParams {
app: RuntimeReconnectApp | null | undefined;
socketId: string | null | undefined;
lastReconnectKey: string;
}
export const isScheduledRunning = (app: RuntimeReconnectApp | null | undefined) => {
return Boolean(
app && (app.scheduled === 1 || app.scheduled === '1' || app.scheduled === true)
);
};
export const buildRuntimeReconnectRequest = ({
app,
socketId,
lastReconnectKey,
}: RuntimeReconnectRequestParams) => {
const instanceId = app?.instanceId ? String(app.instanceId) : '';
const newSocketId = socketId ? String(socketId) : '';
const appKey = app?.id || app?.key ? String(app.id || app.key) : '';
if (!isScheduledRunning(app) || !instanceId || !newSocketId || !appKey) {
return null;
}
const reconnectKey = `${appKey}:${instanceId}:${newSocketId}`;
if (reconnectKey === lastReconnectKey) {
return null;
}
return {
instanceId,
newSocketId,
reconnectKey,
};
};

@ -46,6 +46,10 @@ import {
removeNodesAndConnectedEdges, removeNodesAndConnectedEdges,
resolveDeletedNodesWithLoopPairs, resolveDeletedNodesWithLoopPairs,
} from '@/features/workflow/operations/deleteOperations'; } from '@/features/workflow/operations/deleteOperations';
import {
shouldPersistCanvas,
shouldUseCachedCanvas,
} from '@/features/workflow/operations/canvasCache';
import { import {
buildRuntimeNode, buildRuntimeNode,
resolveNodeDefinition, resolveNodeDefinition,
@ -323,8 +327,9 @@ export const useFlowCallbacks = (
// 初始化画布数据 // 初始化画布数据
const initializeCanvasData = useCallback(() => { const initializeCanvasData = useCallback(() => {
const appKey = getCurrentFlowAppKey(); const appKey = getCurrentFlowAppKey();
if (appKey && canvasDataMap[appKey]) { const cachedCanvas = appKey ? canvasDataMap[appKey] : null;
const { edges, nodes } = canvasDataMap[appKey]; if (shouldUseCachedCanvas({ cachedCanvas, initialData, useDefault })) {
const { edges, nodes } = cachedCanvas;
setNodes(nodes); setNodes(nodes);
setEdges(edges); setEdges(edges);
} else { } else {
@ -343,11 +348,19 @@ export const useFlowCallbacks = (
// 标记历史记录已初始化 // 标记历史记录已初始化
setHistoryInitialized(true); setHistoryInitialized(true);
}, [initialData, canvasDataMap, getCurrentFlowAppKey]); }, [initialData, useDefault, canvasDataMap, getCurrentFlowAppKey]);
// 实时更新 canvasDataMap // 实时更新 canvasDataMap
const updateCanvasDataMapEffect = useCallback(() => { const updateCanvasDataMapEffect = useCallback(() => {
const appKey = getCurrentFlowAppKey(); const appKey = getCurrentFlowAppKey();
if (appKey) { const { appRuntimeData } = store.getState().ideContainer;
const isCurrentAppRunning = Boolean(
appKey && appRuntimeData[appKey]?.isRunning
);
if (
appKey &&
shouldPersistCanvas({ nodes, edges, isRunning: isCurrentAppRunning })
) {
updateCanvasDataMapDebounced( updateCanvasDataMapDebounced(
dispatch, dispatch,
canvasDataMap, canvasDataMap,

@ -61,7 +61,9 @@ export const useFlowEditorState = (initialData?: any, readOnly?: boolean) => {
Object.values(initialData.components).some((comp: any) => comp.status); Object.values(initialData.components).some((comp: any) => comp.status);
setNodes((prevNodes) => { setNodes((prevNodes) => {
return prevNodes.map((node) => { let hasChanges = false;
const nextNodes = prevNodes.map((node) => {
// 如果是只读模式(历史实例查看),优先使用节点自身的历史状态 // 如果是只读模式(历史实例查看),优先使用节点自身的历史状态
// 如果是正常运行模式,只使用运行时状态 // 如果是正常运行模式,只使用运行时状态
let nodeStatus = 'waiting'; let nodeStatus = 'waiting';
@ -80,6 +82,14 @@ export const useFlowEditorState = (initialData?: any, readOnly?: boolean) => {
showStatus = currentAppIsRunning; showStatus = currentAppIsRunning;
} }
if (
node.data.status === nodeStatus &&
node.data.isStatusVisible === showStatus
) {
return node;
}
hasChanges = true;
return { return {
...node, ...node,
data: { data: {
@ -89,6 +99,8 @@ export const useFlowEditorState = (initialData?: any, readOnly?: boolean) => {
}, },
}; };
}); });
return hasChanges ? nextNodes : prevNodes;
}); });
}, [ }, [
appRuntimeData, appRuntimeData,

@ -1,8 +1,14 @@
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import { useSelector, useDispatch } from 'react-redux'; import { useSelector, useDispatch } from 'react-redux';
import { updateSocketId, updateNodeStatus, updateEventListOld } from '@/store/ideContainer'; import {
updateSocketId,
updateNodeStatus,
updateEventListOld
} from '@/store/ideContainer';
import useWebSocket from '@/hooks/useWebSocket'; import useWebSocket from '@/hooks/useWebSocket';
import { isJSON, getUrlParams } from '@/utils/common'; import { isJSON, getUrlParams } from '@/utils/common';
import { buildRuntimeReconnectRequest } from '@/features/workflow/runtime/runtimeReconnect';
import { reconnectWorkflowRuntime } from '@/features/workflow/runtime/runtimeActions';
import styles from './style/index.module.less'; import styles from './style/index.module.less';
import SideBar from './sideBar'; import SideBar from './sideBar';
@ -64,9 +70,10 @@ function IDEContainer() {
// 用于跟踪已打开的tab保持组件状态 // 用于跟踪已打开的tab保持组件状态
const [openedTabs, setOpenedTabs] = useState<Set<string>>(new Set()); const [openedTabs, setOpenedTabs] = useState<Set<string>>(new Set());
const [subMenuData, setSubMenuData] = useState<any>({}); const [subMenuData, setSubMenuData] = useState<any>({});
const { menuData, flowData, currentAppData } = useSelector((state) => state.ideContainer); const { menuData, flowData, currentAppData, socketId } = useSelector((state) => state.ideContainer);
const dispatch = useDispatch(); const dispatch = useDispatch();
const navBarRef = useRef<NavBarRef>(null); const navBarRef = useRef<NavBarRef>(null);
const lastReconnectKeyRef = useRef('');
// 初始化WebSocket hook // 初始化WebSocket hook
const ws = useWebSocket({ const ws = useWebSocket({
@ -122,6 +129,48 @@ function IDEContainer() {
} }
}); });
useEffect(() => {
const reconnectRequest = buildRuntimeReconnectRequest({
app: currentAppData,
socketId,
lastReconnectKey: lastReconnectKeyRef.current
});
if (!reconnectRequest) {
return;
}
let canceled = false;
lastReconnectKeyRef.current = reconnectRequest.reconnectKey;
const reconnectRuntime = async () => {
try {
const reconnectResult = await reconnectWorkflowRuntime({
instanceId: reconnectRequest.instanceId,
newSocketId: reconnectRequest.newSocketId,
dispatch
});
if (!canceled && reconnectResult?.success === false) {
lastReconnectKeyRef.current = '';
Message.error(reconnectResult.message || '运行实例重连失败');
}
} catch (error) {
if (!canceled) {
lastReconnectKeyRef.current = '';
console.error('运行实例重连失败:', error);
Message.error('运行实例重连失败');
}
}
};
reconnectRuntime();
return () => {
canceled = true;
};
}, [currentAppData, socketId, dispatch]);
// 监听自定义事件,处理打开子节点标签页的逻辑 // 监听自定义事件,处理打开子节点标签页的逻辑
useEffect(() => { useEffect(() => {
const handleOpenSubNodeTab = async (event: CustomEvent) => { const handleOpenSubNodeTab = async (event: CustomEvent) => {
@ -500,4 +549,4 @@ function IDEContainer() {
); );
} }
export default IDEContainer; export default IDEContainer;

Loading…
Cancel
Save