2. 审批流骨架:企业业务系统中审批流的通用骨架模型、节点与边的基本抽象

在构建企业级审批流引擎时,第一步不是编写代码,而是建立一套通用的骨架模型。这个骨架需要抽象出所有审批流程的共性:无论是一个简单的请假申请,还是一个复杂的采购合同审批,其底层结构都可以被归纳为节点(Node)与边(Edge)的拓扑图。

2.1 通用骨架模型:有向图与状态机

审批流本质上是一个有向图(Directed Graph),结合了有限状态机(FSM)的约束。每个审批节点代表一个状态,而边则代表状态之间的转移条件。

核心抽象公式:
审批流 = (N, E, S0, Sf, T)
其中 N 为节点集合,E 为边集合,S0 为初始状态,Sf 为终态集合,T 为转移函数。

这种抽象带来的直接好处是:流程定义与业务逻辑解耦。开发人员只需维护节点和边的配置,而无需关心具体的业务代码。

2.2 节点(Node)的基本抽象

节点是流程中的“停靠点”。在企业系统中,节点通常被抽象为以下几种类型,每种类型对应不同的行为模式:

节点类型 别名 核心行为 典型场景
开始节点 Start 流程实例化,无前驱 提交申请
审批节点 Approval 人工决策(通过/驳回/转办) 部门经理审批
条件节点 Gateway 根据表达式路由到不同分支 金额 > 10000 走总监审批
执行节点 Task 自动执行脚本或调用API 发送通知、更新库存
结束节点 End 流程终止,无后继 审批完成或驳回终止
设计避坑: 不要将“驳回”设计为节点属性,而应将其视为一种边(Edge)的转移动作。驳回意味着从当前审批节点通过一条“驳回边”回到上一个节点或开始节点。

2.3 边(Edge)的基本抽象

边是连接节点的“管道”,定义了流程的流转方向与触发条件。边的抽象决定了流程的灵活性与复杂度。

  • 无条件边(Sequence Flow): 默认流转,节点处理完成后自动触发。
  • 条件边(Conditional Flow): 携带一个表达式(如 SpEL、Aviator),当表达式结果为 true 时触发。
  • 异常边(Exception Flow): 当节点执行抛出特定异常时触发,用于错误处理与补偿。
  • 超时边(Timeout Flow): 节点停留超过预设时间后自动触发,用于催办或自动转交。

2.4 骨架模型的可视化:节点与边的拓扑结构

下图展示了一个典型的企业采购审批骨架模型。该模型包含了开始、条件分支、并行审批、以及异常处理路径,体现了节点与边的完整抽象。

审批流骨架模型:节点与边拓扑图 审批流骨架模型:节点与边拓扑图 开始节点 条件 审批节点A 部门经理 审批节点B 总监加签 并行 执行节点 更新库存 结束节点 异常边示例 超时/异常 → 自动转交 或触发补偿流程

2.5 节点与边的数据模型定义

在代码层面,骨架模型通常映射为以下核心数据结构。这是状态机引擎的“元数据”。

// 节点定义
public class Node {
    private String id;              // 唯一标识
    private String name;            // 节点名称
    private NodeType type;          // 枚举:START, APPROVAL, GATEWAY, TASK, END
    private Map<String, Object> props; // 扩展属性(如审批人策略、超时时间)
}

// 边定义
public class Edge {
    private String id;              // 唯一标识
    private String sourceNodeId;    // 源节点ID
    private String targetNodeId;    // 目标节点ID
    private EdgeType type;          // 枚举:SEQUENCE, CONDITIONAL, EXCEPTION, TIMEOUT
    private String conditionExpr;   // 条件表达式(仅 CONDITIONAL 类型使用)
    private int priority;           // 优先级,用于多条件边时的排序
}

// 流程定义
public class ProcessDefinition {
    private String processKey;      // 流程唯一标识
    private List<Node> nodes;       // 节点列表
    private List<Edge> edges;       // 边列表
    private String startNodeId;     // 开始节点ID
    private List<String> endNodeIds; // 结束节点ID集合
}
关键设计原则: 节点和边的定义必须支持版本化。生产环境中的流程定义一旦发布,应视为不可变对象。任何修改都应生成新版本,避免正在运行的流程实例出现状态漂移。

2.6 骨架模型的扩展性:从通用到具体

通用骨架模型提供了“骨架”,而企业业务系统的复杂性体现在“血肉”上。通过节点和边的扩展属性(props),我们可以将通用模型适配到具体业务:

  • 审批节点扩展: 增加 assigneeStrategy(审批人策略:固定人、角色、上级、表单变量)、counterSignType(会签类型:一票通过、一票否决、百分比通过)。
  • 条件节点扩展: 增加 expressionLanguage(表达式语言类型:SpEL、Groovy、Aviator)、fallbackEdgeId(默认分支,当所有条件不满足时使用)。
  • 执行节点扩展: 增加 retryCount(失败重试次数)、timeout(执行超时时间)、compensationHandler(补偿处理器,用于Saga模式)。

这种设计使得同一个引擎可以驱动“请假审批”、“采购合同审批”、“报销审批”等完全不同的业务流程,而无需修改引擎核心代码。

2.7 本章小结

审批流的骨架模型是状态机设计的基础。通过将流程抽象为节点(状态)与边(转移),我们获得了以下核心能力:

  1. 流程与业务解耦: 业务变化只需调整节点/边配置,无需修改代码。
  2. 可视化与可分析: 有向图结构天然支持流程图的渲染与路径分析。
  3. 错误处理的基础: 异常边、超时边为后续章节的“错误处理”提供了结构化的接入点。
  4. 版本化管理: 不可变的流程定义保障了运行时的一致性。