Files
autumn-recruitment/07.模式/fsm/有限状态机在业务中的应用.md

5.6 KiB

tags, create time, update time
tags create time update time
pattern/fsm
workflow-engine
order-lifecycle
state-machine-pattern
2026-08-08 15:00 2026-08-08 15:00

有限状态机在业务中的应用

概述

FSM 在工程中最大的价值是将"散落的状态判断"收拢到"显式的状态定义"。本文讨论三种典型的业务场景:审批流、订单生命周期和资源交付,每种场景展示其状态图和关键注意事项。

审批流

典型流程

任务从提交到最终结果需经过多级审批,每个审批人可以批准或驳回。

stateDiagram-v2
    [*] --> Draft
    Draft --> Submitted: 提交申请
    Submitted --> ManagerReview: 主管审核
    ManagerReview --> Approved: 批准
    ManagerReview --> Rejected: 驳回
    ManagerReview --> ManagerRevise: 修改后重提
    Approved --> FinanceReview: 进入财务审核
    FinanceReview --> FinalApproved: 财务批准
    FinanceReview --> Rejected: 财务驳回
    Rejected --> [*]
    FinalApproved --> [*]
    ManagerRevise --> Draft: 退回草稿

关键注意事项

注意点 说明
权限校验 每个审批节点需要确认审批人是否有该角色的审批权限
驳回的语义 "驳回"可以是终止(回终态)也可以是退回上一步(回到前置状态),需要在模型中明确区分
超期自动处理 审批超过 N 天未操作应触发超时事件,默认通过或升级给上级
审计追踪 每次状态转换记录 operator、timestamp、comment,不可删除

Tip

面试常考点:如果审批链动态变化(比如 A 请假了由 B 代审),这不再是静态 FSM,而是需要引入策略模式 + 规则引擎。硬编码审批人在生产环境中是不可接受的。

订单生命周期

状态图

订单是最经典的 FSM 应用场景。合法转移必须严格受控,防止"跳状态"。

stateDiagram-v2
    [*] --> PendingPayment
    PendingPayment --> Paid: 用户支付
    PendingPayment --> Cancelled: 超时取消
    Paid --> Shipped: 仓库发货
    Paid --> Refunding: 申请退款
    Shipped --> Completed: 用户确认收货
    Shipped --> Refunding: 申请退款
    Completed --> [*]
    Refunding --> Refunded: 退款完成
    Refunding --> Paid: 退款拒绝
    Cancelled --> [*]
    Refunded --> [*]

非法转移示例

Paid -> Cancelled      ❌ 已支付的订单不能直接取消
Completed -> Shipped   ❌ 已完成不能倒退回已发货
PendingPayment -> Shipped ❌ 未付款不能直接发货

代码示例

func (m *OrderManager) Transition(ctx context.Context, orderID string, event string) error {
    // 1. 获取当前状态
    order, _ := m.repo.FindByID(ctx, orderID)
    
    // 2. 查表确认转换是否合法
    handler, ok := m.transitions[order.Status][event]
    if !ok {
        return fmt.Errorf("invalid transition: %s -> [%s]", order.Status, event)
    }
    
    // 3. 执行转换
    if err := handler(ctx, order); err != nil {
        return err
    }
    return m.repo.Save(ctx, order)
}

资源交付八阶段(七牛云存储场景)

这是一个更复杂的 FSM 应用。假设我们正在实现一个对象存储服务,资源从创建到销毁经历以下八个阶段。

stateDiagram-v2
    [*] --> BucketCreated
    BucketCreated --> FileUploaded: 上传文件
    FileUploaded --> CDNPrefetch: CDN预热
    CDNPrefetch --> AccessAuthorized: 设置访问策略
    AccessAuthorized --> TrafficTracked: 采集流量统计
    TrafficTracked --> LifecycleConfigured: 配置生命周期
    LifecycleConfigured --> CleanupPending: 满足清理条件
    CleanupPending --> [*]: 物理删除

各阶段职责

阶段 核心操作 失败回退策略
BucketCreated 创建存储空间,分配地域和冗余策略 幂等重试,已有 bucket 视为成功
FileUploaded PUT 对象请求,校验 ETag 一致性 断点续传 + 分片合并
CDNPrefetch 向 CDN 节点下发缓存指令 异步队列,不阻塞主链路
AccessAuthorized 签发临时签名 URL 或配置 ACL 与文件上传绑定,原子操作
TrafficTracked 持续指标上报至时序数据库 本地 buffer,批量异步发送
LifecycleConfigured 设置过期规则(如 90 天自动删除) API 级别幂等更新
CleanupPending 定时任务扫描到期对象,标记为删除中 二次确认后执行,可撤销
物理删除 实际调用存储 API 删除对象 事务性删除,失败告警

关键设计决策

Warning

资源删除必须是两阶段的:先标记 CleanupPending,再执行 物理删除。这避免误删且支持回滚。跳过这个阶段是数据丢失的头号原因。

Note

为什么"流量统计"被建模为状态而不是事件?因为流量统计是一个持续的监控行为,不是一个瞬时动作。在实际工程中,它可能通过侧车(sidecar)或事件总线持续运行,不受单用户的控制流影响。FSM 在这里只记录"是否已进入此阶段"。

对比总结

维度 审批流 订单生命周期 资源交付
状态数量 5-8 6-8 8
循环转移 有(退回修改) 部分(退款→重新支付) 无(线性流程)
并发安全要求 中等(单人审批) 极高(多人同时操作) 高(异步任务冲突)
持久化策略 每步写 DB 每步写 DB + 事件日志 最终一致 + 补偿机制

关联笔记