UNPKG

jbxl-workflow

Version:

流程图

364 lines (349 loc) 15.2 kB
import {ElMessage} from "element-plus"; class NodePool { constructor() { this.nodes = new Map(); // 存储所有节点实例 this.relationships = new Map(); // 存储节点之间的关系 this.idRelationships = new Map(); // 存储节点间的关系信息,例如条件、执行顺序等 this.portConnectionCache = new Map(); // 存储节点间的连接信息,防止重复连接 this.startNode = null; // 入口节点 this.endNode = null; // 出口节点 this.startNodeOrigin = null; // 入口节点没有被劫持的实例对象 } // 添加节点到节点池 addNode(node) { if (globalThis.ISRUNNING) { ElMessage.closeAll() return ElMessage.warning('工作流正在运行中,无法添加节点') } if (!node.id) { throw new Error('节点必须有唯一标识符'); } this.idRelationships.set(node.id, node.nodeId) // 检查节点类型并设置起始/结束节点 if (node.shapeType === 'start-node-shape') { if (this.startNode) { throw new Error('工作流中只能有一个起始节点'); } this.startNode = node; } else if (node.shapeType === 'end-node-shape') { if (this.endNode) { throw new Error('工作流中只能有一个结束节点'); } this.endNode = node; } globalThis._graph?.eventBus?.emit('end-node-shape:result', false) // 输出结果 this.nodes.set(node.id, node); this.relationships.set(node.id, { upstream: new Set(), // 上游节点ID集合 downstream: new Set() // 下游节点ID集合 }); return this; } // 从节点池中删除节点 removeNode(node) { if (!node.id) { throw new Error('节点必须有唯一标识符') } if (['start-node-shape', 'end-node-shape'].includes(node.shapeType)) { throw new Error('不能删除开始节点和结束节点') } globalThis._graph?.eventBus?.emit('end-node-shape:result', false) // 输出结果 this.nodes.delete(node.id) this.relationships.delete(node.id) this.idRelationships.delete(node.id) return this } // 建立节点之间的连接 connect(upstreamNodeId, downstreamNodeId) { const upstreamNode = this.nodes.get(upstreamNodeId); const downstreamNode = this.nodes.get(downstreamNodeId); if (!upstreamNode || !downstreamNode) { throw new Error('上游或下游节点不存在'); } // 检查是否形成循环 if (this.hasCircularDependency(upstreamNodeId, downstreamNodeId)) { throw new Error('检测到循环依赖,无法建立连接'); } // 更新关系映射 const upstreamRelations = this.relationships.get(upstreamNodeId); const downstreamRelations = this.relationships.get(downstreamNodeId); upstreamRelations.downstream.add(downstreamNodeId); downstreamRelations.upstream.add(upstreamNodeId); // 更新节点实例的关系 // upstreamNode?.setNext(downstreamNode); // downstreamNode?.setPrev(upstreamNode); return this; } // 断开节点之间的连接 disconnect(upstreamNodeId, downstreamNodeId) { const upstreamNode = this.nodes.get(upstreamNodeId) const downstreamNode = this.nodes.get(downstreamNodeId) if (!upstreamNode || !downstreamNode) { throw new Error('上游或下游节点不存在') } globalThis._graph?.eventBus?.emit('end-node-shape:result', false) // 输出结果 // 更新关系映射 const upstreamRelations = this.relationships.get(upstreamNodeId) const downstreamRelations = this.relationships.get(downstreamNodeId) upstreamRelations.downstream.delete(downstreamNodeId) downstreamRelations.upstream.delete(upstreamNodeId) return this } // 获取下游节点 getDownstreamNodes(nodeId) { const relations = this.relationships.get(nodeId); if (!relations) return []; return Array.from(relations.downstream).map(id => ({ id, nodeId: this.idRelationships.get(id) })); } // 检查是否存在循环依赖 hasCircularDependency(sourceId, targetId, visited = new Set()) { if (sourceId === targetId) return true; visited.add(sourceId); const downstreamNodes = this.getDownstreamNodes(targetId); for (const node of downstreamNodes) { if (visited.has(node.id) || this.hasCircularDependency(sourceId, node.id, visited)) { return true; } } visited.delete(sourceId); return false; } // 获取所有直接上游节点 getDirectUpstreamNodes(nodeId) { const relations = this.relationships.get(nodeId); if (!relations) return []; return Array.from(relations.upstream).map(id => this.nodes.get(id)); } // 获取所有直接下游节点 getDirectDownstreamNodes(nodeId) { const relations = this.relationships.get(nodeId); if (!relations) return []; return Array.from(relations.downstream).map(id => this.nodes.get(id)); } // 获取所有间接下游节点(包括直接下游节点) getAllDownstreamNodes(nodeId, visited = new Set()) { const result = new Set(); const directDownstream = this.getDirectDownstreamNodes(nodeId); for (const node of directDownstream) { if (!visited.has(node.id)) { result.add(node); visited.add(node.id); const indirectDownstream = this.getAllDownstreamNodes(node.id, visited); indirectDownstream.forEach(n => result.add(n)); } } return Array.from(result); } // 获取所有上游节点 getAllUpstreamNodes(nodeId, visited = new Set()) { const result = new Set(); const directUpstream = this.getDirectUpstreamNodes(nodeId); for (const node of directUpstream) { if (!visited.has(node.id)) { result.add(node); visited.add(node.id); const indirectUpstream = this.getAllUpstreamNodes(node.id, visited); indirectUpstream.forEach(n => result.add(n)); } } return Array.from(result); } // 验证工作流的完整性 async validate() { if (!this.startNode) { throw new Error('工作流缺少起始节点'); } if (!this.endNode) { throw new Error('工作流缺少结束节点'); } // 采用BFS 遍历 const validationQueue = [this.startNode]; // 开始节点入队,为当前队头 const validated = new Set() // 检查是否所有节点都能从起始节点到达 const reachableNodes = new Set(); while (validationQueue.length > 0) { const currentNode = validationQueue.shift();// 取出队头 try { const validateRes = await currentNode?.validate?.(); if (!validateRes) { ElMessage({ type: 'error', message: `${currentNode.title}节点 验证未通过`, customClass: 'top-full-width-message' }); return false; } } catch (err) { // ElMessage.error(`${currentNode.title}节点 验证出错`); ElMessage({ type: 'error', message: `${currentNode.title}节点 验证出错`, customClass: 'top-full-width-message' }); return false; } reachableNodes.add(currentNode.id); validated.add(currentNode.id); // 验证通过,标记为已验证 const downstreamNodes = this.getDirectDownstreamNodes(currentNode.id); // 找出当前节点的直属下游节点 for (const node of downstreamNodes) { if (!validated.has(node.id)) { validationQueue.push(node); } } } // const allDownstreamNodes = this.getAllDownstreamNodes(this.startNode.id); // allDownstreamNodes.forEach(node => reachableNodes.add(node.id)); // 只有当前节点验证通过后,才添加下游节点到验证队列中 // 检查是否所有节点都能到达结束节点 if (!reachableNodes.has(this.endNode.id)) { // ElMessage.error(`请检查是否正确连线`); ElMessage({ type: 'error', message: `请检查是否正确连线`, customClass: 'top-full-width-message' }); return false } // 检查是否存在孤立节点 for (const [nodeId, node] of this.nodes.entries()) { if (nodeId !== this.startNode.id && !reachableNodes.has(nodeId)) { // throw new Error(`检测到孤立节点: ${node.title || nodeId}`); // return ElMessage.error(`检测到孤立节点: ${node.title || nodeId}`); // ElMessage.error(`检测到孤立节点: ${node.title || nodeId}`); ElMessage({ type: 'error', message: `检测到孤立节点: ${node.title || nodeId}`, customClass: 'top-full-width-message' }); return false } } return true; } handlePrevParamsData (currentNode) { const upstreamParamsDataList = [] this.getAllUpstreamNodes(currentNode.id).forEach((node) => { const {paramsData, outputParamsData} = node || {} const paramsDataList = paramsData?.list || [] const outputParamsDataList = outputParamsData?.list || [] upstreamParamsDataList.push(...paramsDataList, ...outputParamsDataList) }) return upstreamParamsDataList } getPrevParam(list, id) { let prevParam for (let i = 0; i < list.length; i++) { const item = list[i] if (item.id === id) { prevParam = item } if (item.children?.length) { prevParam = this.getPrevParam(item.children, id) } if (prevParam) return prevParam } } async *runDebug () { if (!this.startNode) { throw new Error('工作流缺少起始节点'); } globalThis.ISRUNNING = true; // 首先遍历所有节点并关闭结果显示 for (const node of this.nodes.values()) { node.showResult?.(false); } const execQueue = [this.startNode]; const executed = new Set(); const nodeResults = new Map(); while (execQueue.length > 0) { const currentNode = execQueue.shift(); currentNode.showResult(false) // 有可能再次运行,所以需要把状态关闭 const prevParamsDataList = this.handlePrevParamsData(currentNode); // 拿到前面节点的所有参数数据 if (prevParamsDataList?.length) { // 处理输入参数 currentNode?.paramsData?.list?.forEach(item => { // const prevParam = prevParamsDataList.find(prev => prev.id === item.value); const prevParam = this.getPrevParam(prevParamsDataList, item.value) if (prevParam?.result !== undefined || prevParam?.value !== undefined) { item.result = prevParam.result || prevParam.value; } }); // 处理输出参数 currentNode?.outputParamsData?.list?.forEach(item => { // const prevParam = prevParamsDataList.find(prev => prev.id === item.value); const prevParam = this.getPrevParam(prevParamsDataList, item.value) if (prevParam?.result !== undefined || prevParam?.value !== undefined) { item.result = prevParam.result || prevParam.value; } }); } try { currentNode.showResult(true) // 显示运行状态(运行中、运行成功、运行结束) yield { status: 'running', node: currentNode, nodeId: currentNode.id, message: `正在执行${currentNode.title}节点` }; await new Promise(resolve => setTimeout(resolve, 0)); const result = await currentNode.runDebug?.({paramsData: currentNode.paramsData || {}, outputParamsData: currentNode.outputParamsData || {}}); const {res, data} = result || {} const status = res ? 'success' : 'error' currentNode.runDebugStatus = status // 输出的状态 yield { status, node: currentNode, nodeId: currentNode.id, data, message: `${currentNode.title}节点执行${res ? '成功' : '失败'}` }; if (res) { nodeResults.set(currentNode.id, result); executed.add(currentNode.id); // console.log('nodePool result', result) const downstreamNodes = this.getDirectDownstreamNodes(currentNode.id); for (const node of downstreamNodes) { if (!executed.has(node.id)) { execQueue.push(node); } } // 给UI一个更新的机会 await new Promise(resolve => setTimeout(resolve, 0)); } } catch (error) { globalThis.ISRUNNING = false yield { status: 'error', node: currentNode, nodeId: currentNode.id, error: error, message: `${currentNode.title}节点执行出错: ${error.message}` }; return; } } } // 获取工作流执行路径 getExecutionPath() { if (!this.validate()) return []; const path = []; let currentNode = this.startNode; while (currentNode) { path.push(currentNode); const downstreamNodes = this.getDirectDownstreamNodes(currentNode.id); currentNode = downstreamNodes[0] || null; } return path; } // 清空节点池 clear() { this.nodes.clear(); this.relationships.clear(); this.startNode = null; this.endNode = null; } } export default NodePool;