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];
}
生产环境注意: 上述骨架仅用于演示核心逻辑。生产级实现需要加入:分布式锁(防止并发转移)、持久化(状态变更写入数据库)、幂等校验(重复事件不重复处理)、以及异常恢复钩子(动作失败时的补偿逻辑)。