@flowlab/all
Version:
A cool library focusing on handling various flows
176 lines (162 loc) • 6.8 kB
text/typescript
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, // 失败时重试1次
retryDelay: 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);
});