jbxl-workflow
Version:
流程图
364 lines (349 loc) • 15.2 kB
JavaScript
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;