feat(flow): 优化事件节点处理逻辑

- 重构事件节点列表处理函数,提升事件节点识别准确性
- 更新事件发送和接收节点的数据结构,支持节点ID与主题映射
- 完善事件节点变动检测机制,精确识别新增、删除和修改的事件节点- 增加事件节点主题变更时的自动删除逻辑
- 优化事件节点数据解析过程,增强对自定义定义的处理能力
- 调整API接口参数结构,统一使用topics字段替代eventIds
- 改进流程数据保存逻辑,确保事件节点变动及时同步到服务端
- 修复事件节点时间戳比较逻辑,避免误判节点版本
- 增强错误处理机制,提升事件节点解析的稳定性
- 更新Redux存储结构,支持更丰富的事件节点信息存储
master
钟良源 3 months ago
parent a4184ea1a8
commit 8b9bf00952

@ -205,7 +205,7 @@ export interface DeleteEventParams {
export interface DeleteEventForAppParams {
appId: string,
eventIds: string[]
topics: string[]
}
// runtime

@ -36,13 +36,14 @@ import {
} from '@/utils/flowCommon';
import { projectFlowHandle } from '@/pages/flowEditor/utils/projectFlowHandle';
import { appFLowHandle } from '@/pages/flowEditor/utils/appFlowhandle';
import { handelEventNodeList, updateEvent, upDatePublish } from '@/pages/flowEditor/utils/common';
import { Dispatch } from 'redux';
import { runMainFlow, stopApp } from '@/api/apps';
import store from '@/store';
import { updateAppEvent, updateAppEventChannel, updateAppFlowData } from '@/api/appEvent';
import { sleep } from '@/utils/common';
import { queryEventItemBySceneIdOld } from '@/api/event';
import { queryEventItemBySceneIdOld, deleteEventSub, deleteEventPub } from '@/api/event';
export const useFlowCallbacks = (
nodes: Node[],
@ -810,7 +811,11 @@ export const useFlowCallbacks = (
target: loopStartNode.id,
sourceHandle: edgeForNodeAdd.sourceHandle,
targetHandle: 'start', // 循环开始节点的输入句柄
type: 'custom'
type: 'custom',
lineType: 'api',
data: {
lineType: 'api'
}
},
{
id: `e${loopEndNode.id}-${edgeForNodeAdd.target}`,
@ -818,7 +823,11 @@ export const useFlowCallbacks = (
target: edgeForNodeAdd.target,
sourceHandle: 'break', // 循环结束节点的输出句柄
targetHandle: edgeForNodeAdd.targetHandle,
type: 'custom'
type: 'custom',
lineType: 'api',
data: {
lineType: 'api'
}
}
];
@ -977,7 +986,11 @@ export const useFlowCallbacks = (
target: newNode.id,
sourceHandle: edgeForNodeAdd.sourceHandle,
targetHandle: newNodeTargetHandle,
type: 'custom'
type: 'custom',
lineType: 'api',
data: {
lineType: 'api'
}
},
{
id: `e${newNode.id}-${edgeForNodeAdd.target}`,
@ -985,7 +998,11 @@ export const useFlowCallbacks = (
target: edgeForNodeAdd.target,
sourceHandle: newNodeSourceHandle,
targetHandle: edgeForNodeAdd.targetHandle,
type: 'custom'
type: 'custom',
lineType: 'api',
data: {
lineType: 'api'
}
}
];
@ -1111,168 +1128,7 @@ export const useFlowCallbacks = (
}, [edgeForNodeAdd, positionForNodeAdd, addNodeOnEdge, addNodeOnPane]);
// endregion
// 保存所有节点和边数据到服务器,更新事件相关数据
const updateEvent = (revertedData, appid) => {
// 初始化参数对象
const params: any = {
eventListenes: [],
eventSends: []
};
// 遍历所有节点,查找事件相关节点
Object.entries(revertedData).forEach(([nodeId, nodeConfig]: [string, any]) => {
// 检查节点是否有component属性和type定义
if (nodeConfig.component?.type) {
const nodeType = nodeConfig.component.type;
const customDef = nodeConfig.component.customDef;
// 解析customDef可能是字符串也可能是对象
let eventConfig;
if (typeof customDef === 'string') {
try {
eventConfig = JSON.parse(customDef);
} catch (e) {
console.error('Failed to parse customDef:', e);
return;
}
}
else {
eventConfig = customDef;
}
// 根据节点类型处理事件节点
if (nodeType === 'EVENTLISTENE') {
// 事件接收节点
params.eventListenes.push({
nodeName: nodeConfig.nodeName || '',
eventId: eventConfig?.eventId || null,
topic: eventConfig?.topic || '',
eventName: eventConfig?.name || '',
dataOuts: nodeConfig.dataOuts || [],
nodeId: nodeConfig.nodeId
});
}
else if (nodeType === 'EVENTSEND') {
// 事件发送节点
params.eventSends.push({
nodeName: nodeConfig.nodeName || '',
eventId: eventConfig?.eventId || null,
topic: eventConfig?.topic || '',
eventName: eventConfig?.name || '',
dataIns: nodeConfig.dataIns || [],
nodeId: nodeConfig.nodeId
});
}
}
});
// 调用更新事件的API
if (params.eventListenes.length > 0 || params.eventSends.length > 0) {
return params;
}
else return null;
};
const upDatePublish = async (revertedData) => {
const { currentAppData } = store.getState().ideContainer;
const params = {
appId: currentAppData.id,
publishAppId: []
};
revertedData.forEach(item => {
if (item?.component && item.component.type === 'SUB' && !item.component.customDef) params.publishAppId.push(item.component.compId); // 复合组件的这个compId实际上就是flowHousVO里面的id
});
if (params.publishAppId.length > 0) {
const res: any = await refPublish(params);
if (res.code === 200) return res.data;
else return {};
}
else return {};
};
const handelEventNodeList = (newRevertedData) => {
const { appRuntimeData, currentAppData } = store.getState().ideContainer;
const current = appRuntimeData[currentAppData.id];
const deleteEventSendNodeList = [];
const deleteEventlisteneList = [];
// 处理流程接口后的数据
const currentEventSendNodeList = current.eventSendNodeList;
const currentEventlisteneList = current.eventlisteneList;
// 画布数据
const nodeEntries: [string, any][] = Object.entries(newRevertedData);
// 分类事件节点
const eventSendNodes = [];
const eventListenerNodes = [];
// 从nodeEntries中提取事件节点
nodeEntries.forEach(([nodeId, nodeConfig]) => {
if (nodeConfig.component?.type === 'EVENTSEND') {
eventSendNodes.push({ nodeId, ...nodeConfig });
}
else if (nodeConfig.component?.type === 'EVENTLISTENE') {
eventListenerNodes.push({ nodeId, ...nodeConfig });
}
});
// 处理EVENTSEND节点
// 如果currentEventSendNodeList中有而nodeEntries中没有则加入删除列表
currentEventSendNodeList.forEach((eventNode) => {
const nodeId = eventNode;
const nodeInCanvas = eventSendNodes.find(node => node.nodeId === nodeId);
if (!nodeInCanvas) {
// 画布数据中没有该节点,加入删除列表
deleteEventSendNodeList.push(eventNode);
}
else {
// 节点存在于画布中,比较时间戳
// nodeId格式为"节点类型-时间戳"
const canvasNode = nodeInCanvas.nodeId.split('-');
const canvasTimestamp = canvasNode[canvasNode.length - 1];
const interfaceNode = nodeId.split('-');
const interfaceTimestamp = interfaceNode[interfaceNode.length - 1];
// 如果画布数据的时间戳是最新的那就是新增的事件节点不需要添加到deleteList中
// 反之需要进一步判断
if (canvasTimestamp < interfaceTimestamp) {
// 画布节点时间戳较旧,需要进一步判断
// 这里留空,等待后续实现
}
}
});
// 处理EVENTLISTENE节点
// 如果currentEventlisteneList中有而nodeEntries中没有则加入删除列表
currentEventlisteneList.forEach((eventNode) => {
const nodeId = eventNode;
const nodeInCanvas = eventListenerNodes.find(node => node.nodeId === nodeId);
if (!nodeInCanvas) {
// 画布数据中没有该节点,加入删除列表
deleteEventlisteneList.push(eventNode);
}
else {
// 节点存在于画布中,比较时间戳
// nodeId格式为"节点类型-时间戳"
const canvasNode = nodeInCanvas.nodeId.split('-');
const canvasTimestamp = canvasNode[canvasNode.length - 1];
const interfaceNode = nodeId.split('-');
const interfaceTimestamp = interfaceNode[interfaceNode.length - 1];
// 如果画布数据的时间戳是最新的那就是新增的事件节点不需要添加到deleteList中
// 反之需要进一步判断
if (canvasTimestamp < interfaceTimestamp) {
// 画布节点时间戳较旧,需要进一步判断
// 这里留空,等待后续实现
}
}
});
return {
deleteEventSendNodeList,
deleteEventlisteneList
};
};
const saveFlowDataToServer = useCallback(async () => {
if (useDefault) {
try {
@ -1295,7 +1151,7 @@ export const useFlowCallbacks = (
const newRevertedData = reverseConvertFlowData(nodes, edges, upDatePublishCB);
const { flowData, currentAppData, info } = store.getState().ideContainer;
const { deleteEventSendNodeList, deleteEventlisteneList } = handelEventNodeList(newRevertedData);
// console.log(deleteEventSendNodeList, deleteEventlisteneList);
console.log('事件处理数据', deleteEventSendNodeList, deleteEventlisteneList);
// return;
let params = {};
// 更新复合组件/子流程
@ -1344,6 +1200,18 @@ export const useFlowCallbacks = (
Message.error(res.message);
}
}
// 事件节点变动数据有长度就通知后端,主流程和子流程(复合节点)通用
if (deleteEventSendNodeList.length > 0 || deleteEventlisteneList.length > 0) {
deleteEventSendNodeList.length > 0 && deleteEventPub({
appId: currentAppData.id,
topics: deleteEventSendNodeList
});
deleteEventlisteneList.length > 0 && deleteEventSub({
appId: currentAppData.id,
topics: deleteEventlisteneList
});
}
} catch (error) {
console.error('Error saving flow data:', error);
Message.error('保存失败');
@ -1391,7 +1259,7 @@ export const useFlowCallbacks = (
eventId: Array.from(new Set(item.eventId))
}));
try {
updateAppFlowData(appFlowParams);
updateAppFlowData(appEventParams);
if (appEventParams.length > 0) {
for (const item of appEventParams) {
await sleep(500);

@ -0,0 +1,255 @@
import store from '@/store';
import { refPublish } from '@/api/appRes';
export const handelEventNodeList = (newRevertedData) => {
const { appRuntimeData, currentAppData } = store.getState().ideContainer;
const current = appRuntimeData[currentAppData.id];
const deleteEventSendNodeList = [];
const deleteEventlisteneList = [];
// 处理流程接口后的数据,这里是原始数据的依据
const currentEventSendNodeList = current.eventSendNodeList;
const currentEventlisteneList = current.eventlisteneList;
// 画布数据
const nodeEntries: [string, any][] = Object.entries(newRevertedData);
// 分类事件节点 [{nodeId:topic}]
const eventSendNodes = [];
const eventListenerNodes = [];
// 从nodeEntries中提取事件节点
nodeEntries.forEach(([nodeId, nodeConfig]) => {
if (nodeConfig.component?.type === 'EVENTSEND') {
try {
const customDef = JSON.parse(nodeConfig.component.customDef);
eventSendNodes.push({ nodeId, topic: customDef.topic });
} catch (e) {
eventSendNodes.push({ nodeId, topic: nodeConfig.component.customDef.topic });
}
}
else if (nodeConfig.component?.type === 'EVENTLISTENE') {
try {
const customDef = JSON.parse(nodeConfig.component.customDef);
eventListenerNodes.push({ nodeId, topic: customDef.topic });
} catch (e) {
eventListenerNodes.push({ nodeId, topic: nodeConfig.component.customDef.topic });
}
}
});
// 处理EVENTSEND节点
// 1. 原始数据中有但新数据中没有的节点,直接加入删除列表(节点被删除)
currentEventSendNodeList.forEach((eventNode) => {
const nodeId = Object.keys(eventNode)[0];
const nodeInCanvas = eventSendNodes.find(node => node.nodeId === nodeId);
if (!nodeInCanvas) {
// 画布数据中没有该节点,说明节点被删除了,直接加入删除列表
deleteEventSendNodeList.push(eventNode[nodeId]);
}
});
// 2. 遍历新数据中的每个事件节点
eventSendNodes.forEach((newNode) => {
const newNodeId = newNode.nodeId;
// nodeId格式为"节点类型-时间戳"
const newNodeParts = newNodeId.split('-');
const newNodeTimestamp = parseInt(newNodeParts[newNodeParts.length - 1]) || 0;
// 在原始数据中查找相同类型的节点
const sameTypeNodes = currentEventSendNodeList.filter(node => {
const nodeIdKey = Object.keys(node)[0];
const nodeIdParts = nodeIdKey.split('-');
// 比较节点类型部分(除了时间戳)
return nodeIdParts.slice(0, -1).join('-') === newNodeParts.slice(0, -1).join('-');
});
if (sameTypeNodes.length > 0) {
// 获取这些同类型节点中的最大时间戳
const maxTimestamp = Math.max(...sameTypeNodes.map(node => {
const nodeIdKey = Object.keys(node)[0];
return parseInt(nodeIdKey.split('-')[nodeIdKey.split('-').length - 1]) || 0;
}));
// 查找新节点在原始数据中的对应项
const correspondingNode = currentEventSendNodeList.find(node => {
const nodeIdKey = Object.keys(node)[0];
return nodeIdKey === newNodeId;
});
// 如果找到了对应的节点
if (correspondingNode) {
const correspondingNodeId = Object.keys(correspondingNode)[0];
const correspondingTopic = correspondingNode[correspondingNodeId];
// 比较topic是否一致
if (newNode.topic !== correspondingTopic) {
// topic不一致将原始数据的topic加入删除列表
deleteEventSendNodeList.push(correspondingTopic);
}
}
// 如果新节点的时间戳小于最大时间戳,则说明它是一个旧版本,应该被删除
else if (newNodeTimestamp < maxTimestamp) {
// 查找该节点在currentEventSendNodeList中的完整对象
const nodeToDelete = currentEventSendNodeList.find(node => {
const nodeIdKey = Object.keys(node)[0];
return nodeIdKey === newNodeId;
});
if (nodeToDelete) {
const nodeIdKey = Object.keys(nodeToDelete)[0];
deleteEventSendNodeList.push(nodeToDelete[nodeIdKey]);
}
}
}
// 如果sameTypeNodes为空说明这是一个全新的节点类型不需要处理
});
// 处理EVENTLISTENE节点
// 1. 原始数据中有但新数据中没有的节点,直接加入删除列表(节点被删除)
currentEventlisteneList.forEach((eventNode) => {
const nodeId = Object.keys(eventNode)[0];
const nodeInCanvas = eventListenerNodes.find(node => node.nodeId === nodeId);
if (!nodeInCanvas) {
// 画布数据中没有该节点,说明节点被删除了,直接加入删除列表
deleteEventlisteneList.push(eventNode[nodeId]);
}
});
// 2. 遍历新数据中的每个事件节点
eventListenerNodes.forEach((newNode) => {
const newNodeId = newNode.nodeId;
// nodeId格式为"节点类型-时间戳"
const newNodeParts = newNodeId.split('-');
const newNodeTimestamp = parseInt(newNodeParts[newNodeParts.length - 1]) || 0;
// 在原始数据中查找相同类型的节点
const sameTypeNodes = currentEventlisteneList.filter(node => {
const nodeIdKey = Object.keys(node)[0];
const nodeIdParts = nodeIdKey.split('-');
// 比较节点类型部分(除了时间戳)
return nodeIdParts.slice(0, -1).join('-') === newNodeParts.slice(0, -1).join('-');
});
if (sameTypeNodes.length > 0) {
// 获取这些同类型节点中的最大时间戳
const maxTimestamp = Math.max(...sameTypeNodes.map(node => {
const nodeIdKey = Object.keys(node)[0];
return parseInt(nodeIdKey.split('-')[nodeIdKey.split('-').length - 1]) || 0;
}));
// 查找新节点在原始数据中的对应项
const correspondingNode = currentEventlisteneList.find(node => {
const nodeIdKey = Object.keys(node)[0];
return nodeIdKey === newNodeId;
});
// 如果找到了对应的节点
if (correspondingNode) {
const correspondingNodeId = Object.keys(correspondingNode)[0];
const correspondingTopic = correspondingNode[correspondingNodeId];
// 比较topic是否一致
if (newNode.topic !== correspondingTopic) {
// topic不一致将原始数据的topic加入删除列表
deleteEventlisteneList.push(correspondingTopic);
}
}
// 如果新节点的时间戳小于最大时间戳,则说明它是一个旧版本,应该被删除
else if (newNodeTimestamp < maxTimestamp) {
// 查找该节点在currentEventlisteneList中的完整对象
const nodeToDelete = currentEventlisteneList.find(node => {
const nodeIdKey = Object.keys(node)[0];
return nodeIdKey === newNodeId;
});
if (nodeToDelete) {
const nodeIdKey = Object.keys(nodeToDelete)[0];
deleteEventlisteneList.push(nodeToDelete[nodeIdKey]);
}
}
}
// 如果sameTypeNodes为空说明这是一个全新的节点类型不需要处理
});
return {
deleteEventSendNodeList,
deleteEventlisteneList
};
};
export const updateEvent = (revertedData, appid) => {
// 初始化参数对象
const params: any = {
eventListenes: [],
eventSends: []
};
// 遍历所有节点,查找事件相关节点
Object.entries(revertedData).forEach(([nodeId, nodeConfig]: [string, any]) => {
// 检查节点是否有component属性和type定义
if (nodeConfig.component?.type) {
const nodeType = nodeConfig.component.type;
const customDef = nodeConfig.component.customDef;
// 解析customDef可能是字符串也可能是对象
let eventConfig;
if (typeof customDef === 'string') {
try {
eventConfig = JSON.parse(customDef);
} catch (e) {
console.error('Failed to parse customDef:', e);
return;
}
}
else {
eventConfig = customDef;
}
// 根据节点类型处理事件节点
if (nodeType === 'EVENTLISTENE') {
// 事件接收节点
params.eventListenes.push({
nodeName: nodeConfig.nodeName || '',
eventId: eventConfig?.eventId || null,
topic: eventConfig?.topic || '',
eventName: eventConfig?.name || '',
dataOuts: nodeConfig.dataOuts || [],
nodeId: nodeConfig.nodeId
});
}
else if (nodeType === 'EVENTSEND') {
// 事件发送节点
params.eventSends.push({
nodeName: nodeConfig.nodeName || '',
eventId: eventConfig?.eventId || null,
topic: eventConfig?.topic || '',
eventName: eventConfig?.name || '',
dataIns: nodeConfig.dataIns || [],
nodeId: nodeConfig.nodeId
});
}
}
});
// 调用更新事件的API
if (params.eventListenes.length > 0 || params.eventSends.length > 0) {
return params;
}
else return null;
};
export const upDatePublish = async (revertedData) => {
const { currentAppData } = store.getState().ideContainer;
const params = {
appId: currentAppData.id,
publishAppId: []
};
revertedData.forEach(item => {
if (item?.component && item.component.type === 'SUB' && !item.component.customDef) params.publishAppId.push(item.component.compId); // 复合组件的这个compId实际上就是flowHousVO里面的id
});
if (params.publishAppId.length > 0) {
const res: any = await refPublish(params);
if (res.code === 200) return res.data;
else return {};
}
else return {};
};

@ -21,8 +21,8 @@ interface IDEContainerState {
isRunning: boolean;
logs: any[];
runId: string;
eventSendNodeList: string[],
eventlisteneList: string[]
eventSendNodeList: any[], // [{nodeID:topic}]
eventlisteneList: any[] // [{nodeID:topic}]
}>;
}

@ -75,12 +75,28 @@ export const convertFlowData = (flowData: any, useDefault = true) => {
const nodeConfig: any = entry[1];
// 更新应用中的事件节点列表
if (nodeId.includes('EVENTLISTENE')) eventlisteneList.push(nodeId);
else if (nodeId.includes('EVENTSEND')) eventSendNodeList.push(nodeId);
if (nodeId.includes('EVENTLISTENE')) {
try {
const customDef = JSON.parse(nodeConfig.component.customDef);
// 使用展开运算符创建新数组,避免修改冻结对象
eventlisteneList.splice(eventlisteneList.length, 0, { [nodeId]: customDef.topic });
} catch (error) {
console.log(error);
}
}
else if (nodeId.includes('EVENTSEND')) {
try {
const customDef = JSON.parse(nodeConfig.component.customDef);
// 使用展开运算符创建新数组,避免修改冻结对象
eventSendNodeList.splice(eventSendNodeList.length, 0, { [nodeId]: customDef.topic });
} catch (error) {
console.log(error);
}
}
if (eventlisteneList.length > 0 || eventSendNodeList.length > 0) store.dispatch(updateEventNodeList({
eventSendNodeList,
eventlisteneList
eventSendNodeList: [...eventSendNodeList],
eventlisteneList: [...eventlisteneList]
}));
// 确定节点类型

Loading…
Cancel
Save