技术

工作流引擎实操笔记:节点、流转与幂等

从状态机到可配置流程:一次工作流引擎落地中关于节点模型、流转条件、幂等与补偿的实操记录。

目录
  1. 先分清状态机与工作流
  2. 节点模型
  3. 流转与条件
  4. 幂等:工作流逃不掉的一课
  5. 在途单据怎么办
  6. 可观测性
  7. 小结

工作流引擎的复杂度不在「跑通一条流程」,而在「流程改了之后,在途单据怎么办」。

先分清状态机与工作流

  • 状态机:状态与迁移在代码里写死,改动需要发版,适合稳定的审批路径。
  • 工作流:节点与连线可配置,改动只需改配置,适合审批路径经常调整的场景。

多数业务系统的正确姿势是:用状态机守住业务语义,用工作流驱动流转顺序。

节点模型

一个够用的节点模型只需要四个要素:

要素说明
nodeKey节点唯一标识,配置与运行数据都靠它关联
type审批 / 抄送 / 条件 / 开始 / 结束
assignee处理人规则(指定人、角色、发起人上级)
listeners进入或离开节点时触发的动作
interface FlowNode {
  nodeKey: string
  type: 'start' | 'approve' | 'cc' | 'condition' | 'end'
  assignee?: { mode: 'user' | 'role' | 'manager', value: string }
  onEnter?: string[]   // 事件名,交给监听器注册表处理
}

流转与条件

条件节点的判断逻辑建议抽成纯函数 + 表达式白名单,不要让配置里塞任意脚本。

// 只允许有限的操作符,避免配置被当成代码执行
const OPS = {
  '>': (a: number, b: number) => a > b,
  '===': (a: unknown, b: unknown) => a === b,
  in: (a: string, b: string[]) => b.includes(a),
}

幂等:工作流逃不掉的一课

审批按钮被点两次、消息重投、定时补偿重跑 —— 这些都会导致同一个节点被处理多次。三条防线:

  1. 业务唯一键:流程实例 + 节点 + 处理人 组成唯一索引,重复操作直接冲突失败;
  2. 状态前置校验:处理前检查实例状态是否仍处于该节点;
  3. 到期补偿任务幂等:补偿任务本身也要能重复执行而不产生副作用。
-- 唯一索引挡住重复提交
CREATE UNIQUE INDEX uk_task_node_handler
  ON wf_task (instance_id, node_key, handler_id);

在途单据怎么办

流程改版是常态,处理方式有三种,按风险从低到高:

  • 版本隔离:新流程新版本号,在途单据继续跑老版本(推荐,改动最安全)。
  • 节点映射:把老节点映射到新节点,需要人工维护映射表。
  • 强制重算:高风险,仅适用于流程很短、在途单据极少的场景。

可观测性

工作流出问题时,最需要的是一条时间线,而不是日志海洋:

[2026-08-30 10:12:03] 实例 I-1001 创建,发起人 u_23
[2026-08-30 10:12:04] 进入节点 approve_1,处理人 u_07
[2026-08-30 10:30:11] 节点 approve_1 通过,意见:同意
[2026-08-30 10:30:11] 进入节点 approve_2(条件命中 amount > 5000)

把每次流转都记为一条事件,界面上就能直接渲染出流程图上的轨迹。

小结

工作流引擎的选型不是重点,幂等、版本、可观测性这三件事才是长期成本所在。前两个决定系统稳不稳,最后一个决定排查问题要花多久。