UNPKG

@flowlab/all

Version:

A cool library focusing on handling various flows

176 lines (162 loc) 6.8 kB
import { WorkflowDefinition, WorkflowExecutor, createWorkflowContext, nodeRegistry, BaseNode, } from './index.js'; import { INodeContext, INodeOutput, NodeStatus, StepType } from './types/index.js'; import { InMemoryEventManager } from './event/event-manager.js'; import { ConsoleLogger } from './monitoring/logger.js'; import { LocalScheduler } from './scheduler/index.js'; // MARK: 定义简单节点 class LogNode extends BaseNode { id = 'LogNode'; async execute(context: INodeContext): Promise<INodeOutput> { const message = context.input.message || '默认日志消息'; console.log(`[LogNode][${context.stepId}] ${message}`, context.workflowContext.variables); context.logs.push(`消息已记录: ${message}`); return { status: NodeStatus.COMPLETED, output: { loggedAt: new Date() } }; } } // MARK: 定义加法节点 class AddDataNode extends BaseNode { id = 'AddDataNode'; async execute(context: INodeContext): Promise<INodeOutput> { const a = context.input.a ?? 0; const b = context.input.b ?? 0; const sum = Number(a) + Number(b); console.log(`[AddDataNode][${context.stepId}] 计算 ${a} + ${b} = ${sum}`); context.logs.push(`计算完成: ${sum}`); // 模拟可能失败的情况 if (isNaN(sum)) { context.logs.push(`输入无效,计算失败`); return { status: NodeStatus.FAILED, error: new Error("输入不是有效的数字") }; } return { status: NodeStatus.COMPLETED, output: { result: sum } }; } // 添加补偿逻辑示例 async compensate(context: INodeContext): Promise<void> { console.warn(`[AddDataNode][${context.stepId}] 补偿逻辑执行:尝试撤销数据添加操作 (模拟)`); context.logs.push("补偿操作已执行 (模拟)"); // 在实际应用中,这里可能需要执行数据库回滚、API 调用等 } } // MARK: 用户角色检查节点 class CheckUserRoleNode extends BaseNode { id = 'CheckUserRoleNode'; requiredRoles = ['admin']; // 只有 admin 角色可以执行 async execute(context: INodeContext): Promise<INodeOutput> { console.log(`[CheckUserRoleNode][${context.stepId}] 用户角色检查通过 (需要 admin)`); context.logs.push("管理员操作已执行"); return { status: NodeStatus.COMPLETED, output: { adminActionDone: true } }; } } // MARK: 注册节点 nodeRegistry.register(new LogNode()); nodeRegistry.register(new AddDataNode()); nodeRegistry.register(new CheckUserRoleNode()); // MARK: 定义工作流 const mainWorkflow = new WorkflowDefinition('main-workflow-example', '主工作流示例'); mainWorkflow .addStep({ id: 'start_log', nodeId: 'LogNode', input: { message: '工作流开始' }, nextStepId: 'calculate_sum', }) .addStep({ id: 'calculate_sum', nodeId: 'AddDataNode', input: { a: 'workflow.input.valueA', // 从工作流输入映射 b: 5, // 静态值 }, outputMapping: { 'workflow.variables.calculationResult': 'nodeOutput.result', // 将节点输出映射到工作流变量 }, nextStepId: 'conditional_branch', timeout: 5000, // 5秒超时 maxRetries: 1, // 失败时重试1retryDelay: 100, // 重试间隔100ms compensateOnFailure: true, // 失败时需要补偿 sla: 1000, // SLA 为 1 秒 }) .addConditionStep({ id: 'conditional_branch', condition: (ctx) => (ctx.variables.calculationResult ?? 0) > 10, branches: { 'true': 'log_gt_10', // 条件为 true 时跳转到 log_gt_10 // 如果不提供 'false' 分支,且 condition 返回 false,则会尝试走 nextStepId (如果配置了) }, nextStepId: 'log_le_10', // 如果条件为 false 或无匹配分支,则走到这里 }) .addStep({ id: 'log_gt_10', nodeId: 'LogNode', input: { message: '计算结果大于 10' }, nextStepId: 'parallel_tasks', // 分支结束后汇合 }) .addStep({ id: 'log_le_10', nodeId: 'LogNode', input: { message: '计算结果小于或等于 10' }, nextStepId: 'parallel_tasks', // 分支结束后汇合 }) .addParallelStep({ id: 'parallel_tasks', parallelSteps: [ { id: 'parallel_log_1', type: StepType.TASK, nodeId: 'LogNode', input: { message: '并行任务 1' } }, { id: 'parallel_log_2', type: StepType.TASK, nodeId: 'LogNode', input: { message: '并行任务 2' } }, // 可以嵌套子工作流等 // { id: 'parallel_sub', type: StepType.SUB_WORKFLOW, subWorkflowId: 'sub_process_example' } ], nextStepId: 'admin_check', }) .addStep({ id: 'admin_check', nodeId: 'CheckUserRoleNode', // 需要 admin 角色 nextStepId: 'final_log', }) .addStep({ id: 'final_log', nodeId: 'LogNode', input: { message: '工作流结束' }, // 没有 nextStepId,工作流在此结束 }); // MARK: 准备执行器选项和上下文 const executorOptions = { nodeRegistry: nodeRegistry, logger: new ConsoleLogger(), // 使用自定义 Logger eventManager: new InMemoryEventManager(), // 使用事件管理器 scheduler: new LocalScheduler(), // 使用本地调度器 // 使用分布式调度器占位符 // scheduler: new DistributedSchedulerPlaceholder('rabbitmq'), }; const initialInput = { valueA: 7 }; // 7 + 5 = 12 > 10 // const initialInput = { valueA: 3 }; // 3 + 5 = 8 <= 10 // const initialInput = { valueA: 'abc' }; // 导致 AddDataNode 失败 const workflowContext = createWorkflowContext( mainWorkflow.id, initialInput, 'tenant-123', // 租户 ID 'user-456', // 用户 ID 'admin' // 用户角色 ('admin''user') // 'user' // 如果是 'user',admin_check 会失败 ); // 5. 创建并运行执行器 const executor = new WorkflowExecutor(mainWorkflow, workflowContext, executorOptions); executor.run() .then(finalContext => { console.log("\n--- 工作流执行完成 ---"); console.log("最终状态:", finalContext.status); console.log("最终变量:", finalContext.variables); if (finalContext.error) { console.error("最终错误:", finalContext.error); } // console.log("执行历史:", JSON.stringify(finalContext.history, null, 2)); // 打印详细历史 }) .catch(error => { console.error("\n--- 工作流执行过程中发生未捕获的严重错误 ---"); console.error(error); });