2. 状态机基础:有限状态机(FSM)核心概念

有限状态机(Finite State Machine, FSM)是审批流系统的数学建模基础。在审批流中,一个工单从“草稿”到“已归档”的完整生命周期,本质上就是状态机在事件驱动下的状态迁移过程。理解FSM的四个核心要素——状态、事件、转移、动作——是构建可恢复、可审计的异常处理骨架的前提。

2.1 核心概念定义

概念 符号 定义 审批流示例
状态 (State) S 系统在某一时刻所处的稳定模式,是有限且可枚举的 草稿、审批中、已通过、已驳回
事件 (Event) E 触发状态转移的外部输入或内部信号 提交申请、审批通过、驳回操作
转移 (Transition) δ 状态在事件驱动下从当前状态到下一状态的映射关系 草稿 + 提交 → 审批中
动作 (Action) A 转移发生时执行的副作用操作(非状态变更) 发送通知、写入审计日志、释放资源锁
关键原则: 状态机在任意时刻只能处于 一个 当前状态。状态总数是有限的,且所有可能的状态转移必须在设计时显式定义。未定义的事件-状态组合应被系统拒绝或触发异常处理流程。

2.2 形式化定义

一个确定性有限状态机(DFS)可以用六元组形式化描述:

FSM = (S, E, δ, s₀, F, A)

其中:
- S  = {s₁, s₂, ..., sₙ}  有限状态集合
- E  = {e₁, e₂, ..., eₘ}  有限事件集合
- δ  : S × E → S          转移函数(部分函数)
- s₀ ∈ S                  初始状态
- F  ⊆ S                  终态集合(可空)
- A  : S × E → Action     动作函数(可选)
工程提示: 在实现审批流状态机时,δ 通常以状态转移矩阵或决策表的形式存储。对于未定义的转移(如“已归档”状态下收到“提交”事件),应返回错误码或抛出特定异常,而不是静默忽略。

2.3 状态转移矩阵

状态转移矩阵是FSM最直观的工程表达方式。行代表当前状态,列代表事件,单元格代表目标状态。空白单元格表示非法转移。

当前状态 \ 事件 提交 审批通过 驳回 撤回 归档
草稿 审批中 — — — —
审批中 — 已通过 已驳回 草稿 —
已通过 — — — — 已归档
已驳回 审批中 — — — —
已归档 — — — — —
常见陷阱: 转移矩阵中的“—”不能简单视为“不做任何事”。在分布式系统中,非法转移应触发明确的异常事件,并进入错误恢复状态(如“待人工干预”),而不是静默失败。

2.4 动作的分类与执行时机

动作是状态机区别于纯状态图的关键。动作可以附加在转移上,也可以附加在进入/离开状态时执行。在审批流异常恢复场景中,动作的幂等性和可重入性至关重要。

动作类型 触发时机 审批流示例 异常恢复要求
进入动作 进入某个状态时执行 进入“审批中”时锁定工单 幂等:重复进入不重复加锁
退出动作 离开某个状态时执行 离开“审批中”时释放审批人锁 可重入:失败后重试不破坏状态
转移动作 转移发生时执行 “提交”事件触发时写入审计日志 原子性:动作失败则转移回滚
定时动作 在状态内停留超时后触发 “审批中”超过48小时自动升级 幂等:超时事件只处理一次

2.5 状态机图示:审批流核心循环

下图展示了一个典型审批流状态机的核心循环,包含5个状态和6条合法转移路径。注意“已驳回”状态允许重新提交,形成状态回环,这是业务系统中常见的恢复模式。

审批流有限状态机核心循环 审批流有限状态机核心循环 草稿 审批中 已通过 已驳回 已归档 提交 审批通过 驳回 重新提交 归档 撤回 图示说明: • 蓝色节点为初始状态(草稿),紫色节点为终态(已归档) • 红色节点(已驳回)支持回环转移,是异常恢复的关键设计点 • 所有转移均附带动作(审计日志、通知、锁操作),动作失败将触发异常恢复流程

2.6 动作执行与异常恢复的关联

在分布式审批流系统中,动作的执行可能失败(如数据库宕机、消息队列积压)。状态机必须定义动作失败时的行为策略:

  • 回滚策略: 动作失败后,状态回退到转移前的状态,并记录失败原因。适用于关键业务操作(如扣款、锁资源)。
  • 重试策略: 动作失败后,状态停留在当前状态,定时重试动作。适用于可恢复的临时故障(如网络超时)。
  • 补偿策略: 动作部分成功时,执行补偿动作回滚已生效的副作用。适用于跨服务调用场景。
设计准则: 状态机中的动作应设计为 幂等 且 可重入。幂等保证重复执行不产生副作用;可重入保证动作可以在任意中断点恢复执行。这是后续章节“异常恢复策略”的基石。

2.7 状态机与工作流引擎的对比

维度 有限状态机 (FSM) 工作流引擎 (如 Activiti)
状态粒度 扁平、有限、显式枚举 可嵌套、可并行、可动态创建
转移定义 矩阵或表驱动,确定性 BPMN 图驱动,支持条件分支
异常恢复 状态回滚 + 动作重试,强一致性 补偿事务 + 人工干预,最终一致性
适用场景 单对象生命周期管理(审批单、订单) 多参与者协作流程(跨部门审批)
状态数上限 通常 < 20 个 理论上无上限
选型建议: 对于企业审批流中的核心单据(如采购审批、报销单),推荐使用 FSM 作为底层状态管理模型,上层再叠加工作流引擎处理人工任务分配。FSM 提供严格的数学保证,是异常恢复的可靠基础。

2.8 状态机实现的最小代码骨架

以下是一个面向审批流的状态机核心实现骨架,使用枚举定义状态和事件,通过转移矩阵驱动状态变更:

// 状态枚举
public enum ApprovalState {
    DRAFT,          // 草稿
    IN_REVIEW,      // 审批中
    APPROVED,       // 已通过
    REJECTED,       // 已驳回
    ARCHIVED        // 已归档
}

// 事件枚举
public enum ApprovalEvent {
    SUBMIT,         // 提交
    APPROVE,        // 审批通过
    REJECT,         // 驳回
    WITHDRAW,       // 撤回
    ARCHIVE         // 归档
}

// 状态转移矩阵(二维数组)
// 行:当前状态;列:事件;值:目标状态(-1表示非法转移)
private static final int[][] TRANSITION_MATRIX = {
    // DRAFT    IN_REVIEW   APPROVED   REJECTED   ARCHIVED
    {  1,       -1,         -1,        -1,        -1  },  // DRAFT
    {  0,        2,          3,        -1,        -1  },  // IN_REVIEW
    { -1,       -1,         -1,        -1,         4  },  // APPROVED
    {  1,       -1,         -1,        -1,        -1  },  // REJECTED
    { -1,       -1,         -1,        -1,        -1  }   // ARCHIVED
};

// 状态转移执行方法
public ApprovalState transition(ApprovalState current, ApprovalEvent event) {
    int nextIndex = TRANSITION_MATRIX[current.ordinal()][event.ordinal()];
    if (nextIndex == -1) {
        throw new IllegalTransitionException(
            "非法转移: " + current + " -> " + event);
    }
    // 执行动作(审计日志、通知等)
    executeAction(current, event, ApprovalState.values()[nextIndex]);
    return ApprovalState.values()[nextIndex];
}
生产环境注意: 上述骨架仅用于演示核心逻辑。生产级实现需要加入:分布式锁(防止并发转移)、持久化(状态变更写入数据库)、幂等校验(重复事件不重复处理)、以及异常恢复钩子(动作失败时的补偿逻辑)。

2.9 本章小结