搜 索

《如何写好一个状态机》—— 从入门到放弃系列

  • 35阅读
  • 2023年10月13日
  • 0评论
首页 / 编程 / 正文

前言

以前在做支付系统的入金模块时,遇到了一个经典问题:交易状态流转

你以为状态机就是 if-else 一把梭?年轻人,图样图森破。当你的状态从 3 个变成 10 个,事件从 5 个变成 20 个,你就会发现自己写的代码比《百年孤独》里的家谱还难理清。

今天,我们就来聊聊如何优雅地实现一个状态机,让你的代码不至于成为同事口中的"祖传屎山"。

什么是状态机?

概念定义

状态机(State Machine),全称是有限状态机(Finite State Machine,简称 FSM),是一种数学计算模型。它由以下几个核心要素组成:

graph LR subgraph 状态机核心要素 S[状态 State] --> E[事件 Event] E --> T[转换 Transition] T --> A[动作 Action] A --> S end
要素说明支付场景示例
状态(State)系统在某一时刻的状况待验证、处理中、成功、失败
事件(Event)触发状态变化的外部输入用户提交、审核通过、支付回调
转换(Transition)从一个状态到另一个状态的变化待验证 → 已验证
动作(Action)状态转换时执行的操作发送通知、更新余额、记录日志
守卫(Guard)转换前的条件检查余额是否充足、账户是否激活

用人话说:状态机就是一套规则,定义了"在什么状态下,发生什么事件,会转换到什么状态,并且执行什么动作"。

为什么状态机如此重要?(尤其对于支付系统)

1. 业务逻辑的"契约化"

没有状态机的代码通常长这样:

// 💀 地狱级代码示例
public void processPayment(Payment payment, String action) {
    if (payment.getStatus().equals("PENDING") && action.equals("approve")) {
        if (payment.getAmount() > 10000) {
            if (payment.getUserLevel().equals("VIP")) {
                payment.setStatus("APPROVED");
            } else {
                payment.setStatus("NEED_REVIEW");
            }
        } else {
            payment.setStatus("APPROVED");
        }
    } else if (payment.getStatus().equals("APPROVED") && action.equals("pay")) {
        // 又是一堆 if-else...
    } else if (...) {
        // 无限嵌套...
    }
}

这种代码的问题在于:状态流转规则散落在各处,没有人能说清楚系统到底有多少种状态、多少种流转路径。

而有了状态机,业务规则变成了"契约":

状态A + 事件X → 状态B(必须满足条件C)

新人接手项目,看一眼状态流转图就能理解业务;产品经理提需求,先画状态图再讨论;出了 bug,对照状态机排查一目了然。

2. 防止"非法状态转换"

在支付系统中,状态的正确性直接关系到钱。想象一下这些恐怖场景:

  • 一笔已经退款成功的交易,又被标记为支付成功(用户白嫖)
  • 一笔处理中的交易,直接跳到已完成(跳过了风控检查)
  • 一笔已取消的订单,又被重新激活(幽灵订单)

状态机通过白名单机制杜绝这些问题:只有明确定义的流转路径才是合法的,其他一律拒绝。

// 状态机会直接拒绝非法操作
stateMachine.fire(DepositEvent.DEPOSIT_SUCCESS); 
// IllegalStateException: 状态[REFUND_SUCCESS]不支持事件[DEPOSIT_SUCCESS]

3. 天然的审计追踪能力

金融系统对审计的要求非常高。有了状态机,每一次状态变更都是一个明确的"事件":

2024-01-15 10:23:45 | TXN_001 | WAIT_VERIFY → VERIFIED | EVENT: VERIFY_PASS | OPERATOR: SYSTEM
2024-01-15 10:23:46 | TXN_001 | VERIFIED → PENDING | EVENT: DEPOSIT_PENDING | OPERATOR: SYSTEM  
2024-01-15 10:25:12 | TXN_001 | PENDING → REFUND_PROCESSING | EVENT: DEPOSIT_FAILED | OPERATOR: SYSTEM
2024-01-15 14:30:00 | TXN_001 | REFUND_PROCESSING → REFUND_SUCCESS | EVENT: REFUND_SUCCESS | OPERATOR: SYSTEM

完整的状态流转链路,出了问题可以精确定位到是哪个环节、什么时间、由谁触发的。

4. 支持复杂的业务编排

实际的支付系统远比想象的复杂:

  • 并行状态:一笔交易可能同时处于"支付处理中"和"风控审核中"
  • 嵌套状态:退款流程本身也是一个子状态机
  • 超时处理:处理中状态超过 30 分钟自动触发人工审核
  • 补偿机制:某个步骤失败后自动触发回滚

这些复杂场景,用 if-else 写出来会是什么样子?我不敢想。而状态机天然支持这些高级特性。

5. 代码即文档

一个设计良好的状态机,本身就是最好的业务文档:

// 看这段代码,不需要任何注释,你就知道业务规则是什么
.transition()
    .from(DepositStatus.PENDING)
    .to(DepositStatus.REFUND_PROCESSING)
    .on(DepositEvent.DEPOSIT_FAILED)
    .guard(ctx -> ctx.getTransaction().getRetryCount() >= 3) // 重试3次才退款
    .action(ctx -> refundService.initiateRefund(ctx.getPayload()))

状态机在支付系统中的典型应用

支付系统可以说是状态机的"最佳实践场",几乎每个核心流程都离不开它:

graph TB subgraph 支付系统状态机应用 A[订单状态机] --> A1[待支付→支付中→已支付→已完成] B[支付状态机] --> B1[创建→处理中→成功/失败] C[退款状态机] --> C1[申请→审核→处理→完成] D[对账状态机] --> D1[待对账→对账中→已平账/差异] E[结算状态机] --> E1[待结算→结算中→已结算] end

在我们的入金场景中,一笔交易从收到客户的转账,到最终入账成功或退款完成,要经历多个状态的流转。每个状态代表着交易在某个特定阶段,每次状态变更都意味着业务的推进。

好了,理论知识铺垫完毕。接下来,让我们看看这个真实的入金状态流转图,然后用五种不同的方式来实现它。

问题场景

先看一下我们入金系统的状态流转图:

stateDiagram-v2 [*] --> WV: 正常入金 [*] --> MC: IBAN未找到 WV --> V: 验证通过 WV --> MC: 交易验证不通过 V --> P: 入金处理中 V --> RP: 账户状态校验失败 P --> P: 仍在处理中 P --> S: 入金成功 P --> RP: 入金失败 MC --> RP: 人工确认退款 MC --> RS: 人工确认退款成功 MC --> MF: 人工确认失败 RP --> RS: 退款成功 RP --> RF: 退款失败 RF --> RS: 人工确认退款成功 RS --> RT: 收到退票通知 RS --> [*]: 流程结束 RT --> RS: 人工确认退款成功 MF --> [*]: 流程结束 S --> [*]: 流程结束 note right of MC: 🧑‍💼 需人工处理 note right of RF: 🧑‍💼 需人工处理 note right of RT: 🧑‍💼 需人工处理

状态说明:

状态缩写全称说明处理方式
WVWait_Verify等待验证系统自动
VVerified已验证系统自动
PPending处理中系统自动
SSuccess成功系统自动
MCManual_Confirm人工确认人工处理
RPRefund_Processing退款处理中系统自动
RSRefund_Success退款成功系统自动
RFRefund_Failed退款失败人工处理
MFManual_Failed人工确认失败系统自动
RTReturn_Ticket退票人工处理

看到这张图,是不是有种想转行的冲动?别慌,我们一步步来拆解。

方案一:Switch/If-Else 硬刚(适合勇士)

这是最原始、最直接、最容易被后人骂的方式。但作为入门,还是要了解一下。

状态与事件定义

/**
 * 入金交易状态枚举
 */
public enum DepositStatus {
    WAIT_VERIFY("WV", "等待验证"),
    VERIFIED("V", "已验证"),
    PENDING("P", "处理中"),
    SUCCESS("S", "成功"),
    MANUAL_CONFIRM("MC", "人工确认"),
    REFUND_PROCESSING("RP", "退款处理中"),
    REFUND_SUCCESS("RS", "退款成功"),
    REFUND_FAILED("RF", "退款失败"),
    MANUAL_FAILED("MF", "人工确认失败"),
    RETURN_TICKET("RT", "退票");

    private final String code;
    private final String desc;

    DepositStatus(String code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    public String getCode() { return code; }
    public String getDesc() { return desc; }
}

/**
 * 状态流转事件枚举
 */
public enum DepositEvent {
    IBAN_NOT_FOUND("IBAN未找到"),
    VERIFY_PASS("验证通过"),
    VERIFY_FAIL("验证失败"),
    DEPOSIT_PENDING("入金处理中"),
    DEPOSIT_SUCCESS("入金成功"),
    DEPOSIT_FAILED("入金失败"),
    ACCOUNT_VALIDATE_FAIL("账户校验失败"),
    MANUAL_CONFIRM_REFUND("人工确认退款"),
    MANUAL_CONFIRM_SUCCESS("人工确认成功"),
    MANUAL_CONFIRM_FAILED("人工确认失败"),
    REFUND_SUCCESS("退款成功"),
    REFUND_FAILED("退款失败"),
    RECEIVE_RETURN_TICKET("收到退票通知"),
    STILL_PENDING("仍在处理中");

    private final String desc;

    DepositEvent(String desc) {
        this.desc = desc;
    }

    public String getDesc() { return desc; }
}

Switch 实现

/**
 * 状态机服务 - Switch版本
 * 
 * 优点:简单直接,一眼能看懂
 * 缺点:当状态和事件增多时,这个方法会膨胀到让你怀疑人生
 */
@Service
@Slf4j
public class DepositStateMachineSwitch {

    public DepositStatus transition(DepositStatus currentStatus, DepositEvent event) {
        log.info("状态流转: {} + {} -> ?", currentStatus, event);
        
        switch (currentStatus) {
            case WAIT_VERIFY:
                return handleWaitVerify(event);
            case VERIFIED:
                return handleVerified(event);
            case PENDING:
                return handlePending(event);
            case MANUAL_CONFIRM:
                return handleManualConfirm(event);
            case REFUND_PROCESSING:
                return handleRefundProcessing(event);
            case REFUND_FAILED:
                return handleRefundFailed(event);
            case REFUND_SUCCESS:
                return handleRefundSuccess(event);
            case RETURN_TICKET:
                return handleReturnTicket(event);
            case SUCCESS:
            case MANUAL_FAILED:
                // 终态,不再流转
                throw new IllegalStateException("终态不允许继续流转: " + currentStatus);
            default:
                throw new IllegalArgumentException("未知状态: " + currentStatus);
        }
    }

    private DepositStatus handleWaitVerify(DepositEvent event) {
        switch (event) {
            case VERIFY_PASS:
                return DepositStatus.VERIFIED;
            case VERIFY_FAIL:
                return DepositStatus.MANUAL_CONFIRM;
            default:
                throw new IllegalStateException(
                    String.format("状态[%s]不支持事件[%s]", DepositStatus.WAIT_VERIFY, event));
        }
    }

    private DepositStatus handleVerified(DepositEvent event) {
        switch (event) {
            case DEPOSIT_PENDING:
                return DepositStatus.PENDING;
            case ACCOUNT_VALIDATE_FAIL:
                return DepositStatus.REFUND_PROCESSING;
            default:
                throw new IllegalStateException(
                    String.format("状态[%s]不支持事件[%s]", DepositStatus.VERIFIED, event));
        }
    }

    private DepositStatus handlePending(DepositEvent event) {
        switch (event) {
            case STILL_PENDING:
                return DepositStatus.PENDING; // 自循环
            case DEPOSIT_SUCCESS:
                return DepositStatus.SUCCESS;
            case DEPOSIT_FAILED:
                return DepositStatus.REFUND_PROCESSING;
            default:
                throw new IllegalStateException(
                    String.format("状态[%s]不支持事件[%s]", DepositStatus.PENDING, event));
        }
    }

    private DepositStatus handleManualConfirm(DepositEvent event) {
        switch (event) {
            case MANUAL_CONFIRM_REFUND:
                return DepositStatus.REFUND_PROCESSING;
            case MANUAL_CONFIRM_SUCCESS:
                return DepositStatus.REFUND_SUCCESS;
            case MANUAL_CONFIRM_FAILED:
                return DepositStatus.MANUAL_FAILED;
            default:
                throw new IllegalStateException(
                    String.format("状态[%s]不支持事件[%s]", DepositStatus.MANUAL_CONFIRM, event));
        }
    }

    private DepositStatus handleRefundProcessing(DepositEvent event) {
        switch (event) {
            case REFUND_SUCCESS:
                return DepositStatus.REFUND_SUCCESS;
            case REFUND_FAILED:
                return DepositStatus.REFUND_FAILED;
            default:
                throw new IllegalStateException(
                    String.format("状态[%s]不支持事件[%s]", DepositStatus.REFUND_PROCESSING, event));
        }
    }

    private DepositStatus handleRefundFailed(DepositEvent event) {
        if (event == DepositEvent.MANUAL_CONFIRM_SUCCESS) {
            return DepositStatus.REFUND_SUCCESS;
        }
        throw new IllegalStateException(
            String.format("状态[%s]不支持事件[%s]", DepositStatus.REFUND_FAILED, event));
    }

    private DepositStatus handleRefundSuccess(DepositEvent event) {
        if (event == DepositEvent.RECEIVE_RETURN_TICKET) {
            return DepositStatus.RETURN_TICKET;
        }
        throw new IllegalStateException(
            String.format("状态[%s]不支持事件[%s]", DepositStatus.REFUND_SUCCESS, event));
    }

    private DepositStatus handleReturnTicket(DepositEvent event) {
        if (event == DepositEvent.MANUAL_CONFIRM_SUCCESS) {
            return DepositStatus.REFUND_SUCCESS;
        }
        throw new IllegalStateException(
            String.format("状态[%s]不支持事件[%s]", DepositStatus.RETURN_TICKET, event));
    }
}

点评: 这种写法就像你家的电线,刚装修完还能看,过两年再看就是一团乱麻。不过它胜在简单,适合状态少、改动少的场景。


方案二:状态模式(设计模式爱好者的最爱)

状态模式是 GoF 23 种设计模式之一,核心思想是把每个状态的行为封装到独立的类中

类图设计

classDiagram class DepositState["«interface» DepositState"] { +handle(context, event) +getStatus() } class DepositContext { -currentState -transaction +fireEvent(event) +setState(state) +getCurrentStatus() } class WaitVerifyState { +handle(context, event) +getStatus() } class VerifiedState { +handle(context, event) +getStatus() } class PendingState { +handle(context, event) +getStatus() } class ManualConfirmState { +handle(context, event) +getStatus() } DepositState <|.. WaitVerifyState DepositState <|.. VerifiedState DepositState <|.. PendingState DepositState <|.. ManualConfirmState DepositContext --> DepositState

代码实现

/**
 * 状态接口
 */
public interface DepositState {
    
    /**
     * 处理事件,返回新状态
     */
    DepositState handle(DepositContext context, DepositEvent event);
    
    /**
     * 获取当前状态枚举
     */
    DepositStatus getStatus();
    
    /**
     * 是否为终态
     */
    default boolean isFinal() {
        return false;
    }
}

/**
 * 上下文类 - 持有当前状态并管理状态流转
 */
@Slf4j
public class DepositContext {
    
    private DepositState currentState;
    private final DepositTransaction transaction;
    private final List<StateChangeListener> listeners = new ArrayList<>();
    
    public DepositContext(DepositTransaction transaction) {
        this.transaction = transaction;
        // 根据交易初始化状态
        this.currentState = StateFactory.createState(transaction.getStatus());
    }
    
    public void fireEvent(DepositEvent event) {
        if (currentState.isFinal()) {
            throw new IllegalStateException("当前状态为终态,无法继续流转");
        }
        
        DepositStatus oldStatus = currentState.getStatus();
        DepositState newState = currentState.handle(this, event);
        
        if (newState != currentState) {
            this.currentState = newState;
            this.transaction.setStatus(newState.getStatus());
            
            log.info("状态流转: {} --[{}]--> {}", oldStatus, event, newState.getStatus());
            
            // 通知监听器
            listeners.forEach(l -> l.onStateChange(oldStatus, newState.getStatus(), event));
        }
    }
    
    public DepositStatus getCurrentStatus() {
        return currentState.getStatus();
    }
    
    public DepositTransaction getTransaction() {
        return transaction;
    }
    
    public void addListener(StateChangeListener listener) {
        listeners.add(listener);
    }
}

/**
 * 等待验证状态
 */
public class WaitVerifyState implements DepositState {
    
    @Override
    public DepositState handle(DepositContext context, DepositEvent event) {
        switch (event) {
            case VERIFY_PASS:
                return new VerifiedState();
            case VERIFY_FAIL:
                return new ManualConfirmState();
            default:
                throw new IllegalStateException("WaitVerify状态不支持事件: " + event);
        }
    }
    
    @Override
    public DepositStatus getStatus() {
        return DepositStatus.WAIT_VERIFY;
    }
}

/**
 * 已验证状态
 */
public class VerifiedState implements DepositState {
    
    @Override
    public DepositState handle(DepositContext context, DepositEvent event) {
        switch (event) {
            case DEPOSIT_PENDING:
                return new PendingState();
            case ACCOUNT_VALIDATE_FAIL:
                return new RefundProcessingState();
            default:
                throw new IllegalStateException("Verified状态不支持事件: " + event);
        }
    }
    
    @Override
    public DepositStatus getStatus() {
        return DepositStatus.VERIFIED;
    }
}

/**
 * 处理中状态
 */
public class PendingState implements DepositState {
    
    @Override
    public DepositState handle(DepositContext context, DepositEvent event) {
        switch (event) {
            case STILL_PENDING:
                return this; // 自循环,返回自身
            case DEPOSIT_SUCCESS:
                return new SuccessState();
            case DEPOSIT_FAILED:
                return new RefundProcessingState();
            default:
                throw new IllegalStateException("Pending状态不支持事件: " + event);
        }
    }
    
    @Override
    public DepositStatus getStatus() {
        return DepositStatus.PENDING;
    }
}

/**
 * 人工确认状态
 */
public class ManualConfirmState implements DepositState {
    
    @Override
    public DepositState handle(DepositContext context, DepositEvent event) {
        switch (event) {
            case MANUAL_CONFIRM_REFUND:
                return new RefundProcessingState();
            case MANUAL_CONFIRM_SUCCESS:
                return new RefundSuccessState();
            case MANUAL_CONFIRM_FAILED:
                return new ManualFailedState();
            default:
                throw new IllegalStateException("ManualConfirm状态不支持事件: " + event);
        }
    }
    
    @Override
    public DepositStatus getStatus() {
        return DepositStatus.MANUAL_CONFIRM;
    }
}

/**
 * 退款处理中状态
 */
public class RefundProcessingState implements DepositState {
    
    @Override
    public DepositState handle(DepositContext context, DepositEvent event) {
        switch (event) {
            case REFUND_SUCCESS:
                return new RefundSuccessState();
            case REFUND_FAILED:
                return new RefundFailedState();
            default:
                throw new IllegalStateException("RefundProcessing状态不支持事件: " + event);
        }
    }
    
    @Override
    public DepositStatus getStatus() {
        return DepositStatus.REFUND_PROCESSING;
    }
}

/**
 * 退款失败状态 - 需要人工处理
 */
public class RefundFailedState implements DepositState {
    
    @Override
    public DepositState handle(DepositContext context, DepositEvent event) {
        if (event == DepositEvent.MANUAL_CONFIRM_SUCCESS) {
            return new RefundSuccessState();
        }
        throw new IllegalStateException("RefundFailed状态不支持事件: " + event);
    }
    
    @Override
    public DepositStatus getStatus() {
        return DepositStatus.REFUND_FAILED;
    }
}

/**
 * 退款成功状态
 */
public class RefundSuccessState implements DepositState {
    
    @Override
    public DepositState handle(DepositContext context, DepositEvent event) {
        if (event == DepositEvent.RECEIVE_RETURN_TICKET) {
            return new ReturnTicketState();
        }
        throw new IllegalStateException("RefundSuccess状态不支持事件: " + event);
    }
    
    @Override
    public DepositStatus getStatus() {
        return DepositStatus.REFUND_SUCCESS;
    }
    
    @Override
    public boolean isFinal() {
        // 这里有点特殊,RS可能收到退票通知,所以不是严格终态
        return false;
    }
}

/**
 * 退票状态 - 需要人工处理
 */
public class ReturnTicketState implements DepositState {
    
    @Override
    public DepositState handle(DepositContext context, DepositEvent event) {
        if (event == DepositEvent.MANUAL_CONFIRM_SUCCESS) {
            return new RefundSuccessState();
        }
        throw new IllegalStateException("ReturnTicket状态不支持事件: " + event);
    }
    
    @Override
    public DepositStatus getStatus() {
        return DepositStatus.RETURN_TICKET;
    }
}

/**
 * 成功状态 - 终态
 */
public class SuccessState implements DepositState {
    
    @Override
    public DepositState handle(DepositContext context, DepositEvent event) {
        throw new IllegalStateException("Success是终态,不支持任何事件");
    }
    
    @Override
    public DepositStatus getStatus() {
        return DepositStatus.SUCCESS;
    }
    
    @Override
    public boolean isFinal() {
        return true;
    }
}

/**
 * 人工确认失败状态 - 终态
 */
public class ManualFailedState implements DepositState {
    
    @Override
    public DepositState handle(DepositContext context, DepositEvent event) {
        throw new IllegalStateException("ManualFailed是终态,不支持任何事件");
    }
    
    @Override
    public DepositStatus getStatus() {
        return DepositStatus.MANUAL_FAILED;
    }
    
    @Override
    public boolean isFinal() {
        return true;
    }
}

/**
 * 状态工厂
 */
public class StateFactory {
    
    private static final Map<DepositStatus, Supplier<DepositState>> STATE_MAP = new EnumMap<>(DepositStatus.class);
    
    static {
        STATE_MAP.put(DepositStatus.WAIT_VERIFY, WaitVerifyState::new);
        STATE_MAP.put(DepositStatus.VERIFIED, VerifiedState::new);
        STATE_MAP.put(DepositStatus.PENDING, PendingState::new);
        STATE_MAP.put(DepositStatus.SUCCESS, SuccessState::new);
        STATE_MAP.put(DepositStatus.MANUAL_CONFIRM, ManualConfirmState::new);
        STATE_MAP.put(DepositStatus.REFUND_PROCESSING, RefundProcessingState::new);
        STATE_MAP.put(DepositStatus.REFUND_SUCCESS, RefundSuccessState::new);
        STATE_MAP.put(DepositStatus.REFUND_FAILED, RefundFailedState::new);
        STATE_MAP.put(DepositStatus.MANUAL_FAILED, ManualFailedState::new);
        STATE_MAP.put(DepositStatus.RETURN_TICKET, ReturnTicketState::new);
    }
    
    public static DepositState createState(DepositStatus status) {
        Supplier<DepositState> supplier = STATE_MAP.get(status);
        if (supplier == null) {
            throw new IllegalArgumentException("未知状态: " + status);
        }
        return supplier.get();
    }
}

点评: 状态模式符合开闭原则,新增状态只需要新增类。但代码量确实大,10 个状态就要写 10 个类,这还没算上接口和工厂。适合状态行为复杂、需要独立测试的场景。


方案三:基于状态枚举的流转表(推荐)

这是我个人比较喜欢的方案,把状态流转规则用数据结构来表达,代码量小,可读性高。

核心思想

用一个 Map<当前状态, Map<事件, 目标状态>> 来定义所有合法的状态流转路径。

代码实现

/**
 * 状态流转配置 - 基于枚举的声明式配置
 * 
 * 这种方式的好处是:状态流转规则一目了然,像看配置文件一样
 */
@Component
public class DepositStateTransitionTable {
    
    /**
     * 状态流转表
     * 结构: 当前状态 -> (事件 -> 目标状态)
     */
    private final Map<DepositStatus, Map<DepositEvent, DepositStatus>> transitionTable;
    
    public DepositStateTransitionTable() {
        this.transitionTable = buildTransitionTable();
    }
    
    private Map<DepositStatus, Map<DepositEvent, DepositStatus>> buildTransitionTable() {
        Map<DepositStatus, Map<DepositEvent, DepositStatus>> table = new EnumMap<>(DepositStatus.class);
        
        // WV(等待验证) 的流转规则
        table.put(DepositStatus.WAIT_VERIFY, Map.of(
            DepositEvent.VERIFY_PASS, DepositStatus.VERIFIED,
            DepositEvent.VERIFY_FAIL, DepositStatus.MANUAL_CONFIRM
        ));
        
        // V(已验证) 的流转规则
        table.put(DepositStatus.VERIFIED, Map.of(
            DepositEvent.DEPOSIT_PENDING, DepositStatus.PENDING,
            DepositEvent.ACCOUNT_VALIDATE_FAIL, DepositStatus.REFUND_PROCESSING
        ));
        
        // P(处理中) 的流转规则
        table.put(DepositStatus.PENDING, Map.of(
            DepositEvent.STILL_PENDING, DepositStatus.PENDING,
            DepositEvent.DEPOSIT_SUCCESS, DepositStatus.SUCCESS,
            DepositEvent.DEPOSIT_FAILED, DepositStatus.REFUND_PROCESSING
        ));
        
        // MC(人工确认) 的流转规则
        table.put(DepositStatus.MANUAL_CONFIRM, Map.of(
            DepositEvent.MANUAL_CONFIRM_REFUND, DepositStatus.REFUND_PROCESSING,
            DepositEvent.MANUAL_CONFIRM_SUCCESS, DepositStatus.REFUND_SUCCESS,
            DepositEvent.MANUAL_CONFIRM_FAILED, DepositStatus.MANUAL_FAILED
        ));
        
        // RP(退款处理中) 的流转规则
        table.put(DepositStatus.REFUND_PROCESSING, Map.of(
            DepositEvent.REFUND_SUCCESS, DepositStatus.REFUND_SUCCESS,
            DepositEvent.REFUND_FAILED, DepositStatus.REFUND_FAILED
        ));
        
        // RF(退款失败) 的流转规则 - 需要人工处理
        table.put(DepositStatus.REFUND_FAILED, Map.of(
            DepositEvent.MANUAL_CONFIRM_SUCCESS, DepositStatus.REFUND_SUCCESS
        ));
        
        // RS(退款成功) 的流转规则
        table.put(DepositStatus.REFUND_SUCCESS, Map.of(
            DepositEvent.RECEIVE_RETURN_TICKET, DepositStatus.RETURN_TICKET
        ));
        
        // RT(退票) 的流转规则 - 需要人工处理
        table.put(DepositStatus.RETURN_TICKET, Map.of(
            DepositEvent.MANUAL_CONFIRM_SUCCESS, DepositStatus.REFUND_SUCCESS
        ));
        
        // 终态没有流转规则
        table.put(DepositStatus.SUCCESS, Map.of());
        table.put(DepositStatus.MANUAL_FAILED, Map.of());
        
        return Collections.unmodifiableMap(table);
    }
    
    /**
     * 执行状态流转
     */
    public DepositStatus transition(DepositStatus current, DepositEvent event) {
        Map<DepositEvent, DepositStatus> transitions = transitionTable.get(current);
        
        if (transitions == null || transitions.isEmpty()) {
            throw new IllegalStateException(
                String.format("状态[%s]是终态,无法继续流转", current));
        }
        
        DepositStatus target = transitions.get(event);
        if (target == null) {
            throw new IllegalStateException(
                String.format("非法的状态流转: %s + %s", current, event));
        }
        
        return target;
    }
    
    /**
     * 检查流转是否合法
     */
    public boolean canTransition(DepositStatus current, DepositEvent event) {
        Map<DepositEvent, DepositStatus> transitions = transitionTable.get(current);
        return transitions != null && transitions.containsKey(event);
    }
    
    /**
     * 获取某状态下所有可能的事件
     */
    public Set<DepositEvent> getAvailableEvents(DepositStatus current) {
        Map<DepositEvent, DepositStatus> transitions = transitionTable.get(current);
        return transitions != null ? transitions.keySet() : Collections.emptySet();
    }
    
    /**
     * 判断是否为终态
     */
    public boolean isFinalState(DepositStatus status) {
        Map<DepositEvent, DepositStatus> transitions = transitionTable.get(status);
        return transitions == null || transitions.isEmpty();
    }
}

/**
 * 状态机服务 - 基于流转表
 */
@Service
@Slf4j
public class DepositStateMachineService {
    
    private final DepositStateTransitionTable transitionTable;
    private final DepositTransactionRepository repository;
    private final ApplicationEventPublisher eventPublisher;
    
    public DepositStateMachineService(DepositStateTransitionTable transitionTable,
                                      DepositTransactionRepository repository,
                                      ApplicationEventPublisher eventPublisher) {
        this.transitionTable = transitionTable;
        this.repository = repository;
        this.eventPublisher = eventPublisher;
    }
    
    /**
     * 触发状态流转
     */
    @Transactional
    public DepositTransaction fireEvent(String transactionId, DepositEvent event) {
        DepositTransaction transaction = repository.findById(transactionId)
            .orElseThrow(() -> new IllegalArgumentException("交易不存在: " + transactionId));
        
        DepositStatus currentStatus = transaction.getStatus();
        
        // 执行流转
        DepositStatus newStatus = transitionTable.transition(currentStatus, event);
        
        // 更新交易状态
        transaction.setStatus(newStatus);
        transaction.setUpdatedAt(LocalDateTime.now());
        transaction = repository.save(transaction);
        
        log.info("状态流转成功: {} --[{}]--> {}, transactionId={}", 
            currentStatus, event, newStatus, transactionId);
        
        // 发布状态变更事件
        eventPublisher.publishEvent(new DepositStateChangedEvent(
            this, transactionId, currentStatus, newStatus, event));
        
        return transaction;
    }
    
    /**
     * 批量查询可操作的事件
     */
    public Map<String, Set<DepositEvent>> getAvailableEvents(List<String> transactionIds) {
        return repository.findAllById(transactionIds).stream()
            .collect(Collectors.toMap(
                DepositTransaction::getId,
                t -> transitionTable.getAvailableEvents(t.getStatus())
            ));
    }
}

点评: 这种方式把"状态流转规则"和"流转逻辑"分离,规则以数据的形式呈现,非常直观。扩展新状态只需要在表里加一行配置。强烈推荐中小型项目使用。


方案四:Spring State Machine(官方出品)

如果你是 Spring 生态的忠实粉丝,那 Spring State Machine 是官方钦定的选择。

Maven 依赖

<dependency>
    <groupId>org.springframework.statemachine</groupId>
    <artifactId>spring-statemachine-core</artifactId>
    <version>3.2.0</version>
</dependency>

状态机配置

/**
 * Spring State Machine 配置类
 */
@Configuration
@EnableStateMachine
@Slf4j
public class DepositStateMachineConfig 
    extends EnumStateMachineConfigurerAdapter<DepositStatus, DepositEvent> {

    @Override
    public void configure(StateMachineStateConfigurer<DepositStatus, DepositEvent> states) 
            throws Exception {
        states
            .withStates()
            .initial(DepositStatus.WAIT_VERIFY)
            .end(DepositStatus.SUCCESS)
            .end(DepositStatus.MANUAL_FAILED)
            .states(EnumSet.allOf(DepositStatus.class));
    }

    @Override
    public void configure(StateMachineTransitionConfigurer<DepositStatus, DepositEvent> transitions) 
            throws Exception {
        transitions
            // 初始状态流转
            .withExternal()
                .source(DepositStatus.WAIT_VERIFY)
                .target(DepositStatus.VERIFIED)
                .event(DepositEvent.VERIFY_PASS)
            .and()
            .withExternal()
                .source(DepositStatus.WAIT_VERIFY)
                .target(DepositStatus.MANUAL_CONFIRM)
                .event(DepositEvent.VERIFY_FAIL)
            .and()
            
            // Verified 状态流转
            .withExternal()
                .source(DepositStatus.VERIFIED)
                .target(DepositStatus.PENDING)
                .event(DepositEvent.DEPOSIT_PENDING)
            .and()
            .withExternal()
                .source(DepositStatus.VERIFIED)
                .target(DepositStatus.REFUND_PROCESSING)
                .event(DepositEvent.ACCOUNT_VALIDATE_FAIL)
            .and()
            
            // Pending 状态流转
            .withInternal()
                .source(DepositStatus.PENDING)
                .event(DepositEvent.STILL_PENDING)
                .action(ctx -> log.info("仍在处理中..."))
            .and()
            .withExternal()
                .source(DepositStatus.PENDING)
                .target(DepositStatus.SUCCESS)
                .event(DepositEvent.DEPOSIT_SUCCESS)
            .and()
            .withExternal()
                .source(DepositStatus.PENDING)
                .target(DepositStatus.REFUND_PROCESSING)
                .event(DepositEvent.DEPOSIT_FAILED)
            .and()
            
            // Manual Confirm 状态流转
            .withExternal()
                .source(DepositStatus.MANUAL_CONFIRM)
                .target(DepositStatus.REFUND_PROCESSING)
                .event(DepositEvent.MANUAL_CONFIRM_REFUND)
            .and()
            .withExternal()
                .source(DepositStatus.MANUAL_CONFIRM)
                .target(DepositStatus.REFUND_SUCCESS)
                .event(DepositEvent.MANUAL_CONFIRM_SUCCESS)
            .and()
            .withExternal()
                .source(DepositStatus.MANUAL_CONFIRM)
                .target(DepositStatus.MANUAL_FAILED)
                .event(DepositEvent.MANUAL_CONFIRM_FAILED)
            .and()
            
            // Refund Processing 状态流转
            .withExternal()
                .source(DepositStatus.REFUND_PROCESSING)
                .target(DepositStatus.REFUND_SUCCESS)
                .event(DepositEvent.REFUND_SUCCESS)
            .and()
            .withExternal()
                .source(DepositStatus.REFUND_PROCESSING)
                .target(DepositStatus.REFUND_FAILED)
                .event(DepositEvent.REFUND_FAILED)
            .and()
            
            // Refund Failed 状态流转(人工处理)
            .withExternal()
                .source(DepositStatus.REFUND_FAILED)
                .target(DepositStatus.REFUND_SUCCESS)
                .event(DepositEvent.MANUAL_CONFIRM_SUCCESS)
            .and()
            
            // Refund Success 状态流转
            .withExternal()
                .source(DepositStatus.REFUND_SUCCESS)
                .target(DepositStatus.RETURN_TICKET)
                .event(DepositEvent.RECEIVE_RETURN_TICKET)
            .and()
            
            // Return Ticket 状态流转(人工处理)
            .withExternal()
                .source(DepositStatus.RETURN_TICKET)
                .target(DepositStatus.REFUND_SUCCESS)
                .event(DepositEvent.MANUAL_CONFIRM_SUCCESS);
    }

    @Override
    public void configure(StateMachineConfigurationConfigurer<DepositStatus, DepositEvent> config) 
            throws Exception {
        config
            .withConfiguration()
            .autoStartup(true)
            .listener(new StateMachineListenerAdapter<>() {
                @Override
                public void stateChanged(State<DepositStatus, DepositEvent> from, 
                                        State<DepositStatus, DepositEvent> to) {
                    log.info("状态变更: {} -> {}", 
                        from != null ? from.getId() : "INIT", 
                        to.getId());
                }
                
                @Override
                public void eventNotAccepted(Message<DepositEvent> event) {
                    log.warn("事件被拒绝: {}", event.getPayload());
                }
            });
    }
}

/**
 * 状态流转 Action 配置
 */
@Configuration
public class DepositStateMachineActions {
    
    @Bean
    public Action<DepositStatus, DepositEvent> depositSuccessAction() {
        return context -> {
            DepositTransaction transaction = context.getExtendedState()
                .get("transaction", DepositTransaction.class);
            // 入金成功的业务逻辑
            log.info("入金成功,交易ID: {}", transaction.getId());
        };
    }
    
    @Bean
    public Action<DepositStatus, DepositEvent> refundAction() {
        return context -> {
            DepositTransaction transaction = context.getExtendedState()
                .get("transaction", DepositTransaction.class);
            // 发起退款的业务逻辑
            log.info("发起退款,交易ID: {}", transaction.getId());
        };
    }
    
    @Bean
    public Guard<DepositStatus, DepositEvent> verifyGuard() {
        return context -> {
            DepositTransaction transaction = context.getExtendedState()
                .get("transaction", DepositTransaction.class);
            // 验证条件
            return transaction != null && transaction.getAmount() != null;
        };
    }
}

/**
 * 状态机服务 - 封装 Spring State Machine 的使用
 */
@Service
@Slf4j
public class SpringStateMachineService {
    
    private final StateMachineFactory<DepositStatus, DepositEvent> stateMachineFactory;
    private final StateMachinePersister<DepositStatus, DepositEvent, String> persister;
    private final DepositTransactionRepository repository;
    
    public SpringStateMachineService(
            StateMachineFactory<DepositStatus, DepositEvent> stateMachineFactory,
            StateMachinePersister<DepositStatus, DepositEvent, String> persister,
            DepositTransactionRepository repository) {
        this.stateMachineFactory = stateMachineFactory;
        this.persister = persister;
        this.repository = repository;
    }
    
    /**
     * 触发事件
     */
    @Transactional
    public boolean fireEvent(String transactionId, DepositEvent event) {
        DepositTransaction transaction = repository.findById(transactionId)
            .orElseThrow(() -> new IllegalArgumentException("交易不存在"));
        
        // 获取或创建状态机实例
        StateMachine<DepositStatus, DepositEvent> stateMachine = 
            stateMachineFactory.getStateMachine(transactionId);
        
        try {
            // 恢复状态机状态
            persister.restore(stateMachine, transactionId);
            
            // 设置上下文
            stateMachine.getExtendedState().getVariables().put("transaction", transaction);
            
            // 发送事件
            boolean accepted = stateMachine.sendEvent(Mono.just(
                MessageBuilder.withPayload(event).build()
            )).blockLast().getResultType() == ResultType.ACCEPTED;
            
            if (accepted) {
                // 持久化状态
                persister.persist(stateMachine, transactionId);
                
                // 更新数据库
                transaction.setStatus(stateMachine.getState().getId());
                repository.save(transaction);
            }
            
            return accepted;
            
        } catch (Exception e) {
            log.error("状态机执行异常", e);
            throw new RuntimeException("状态流转失败", e);
        }
    }
}

/**
 * 状态机持久化配置
 */
@Configuration
public class StateMachinePersistConfig {
    
    @Bean
    public StateMachinePersister<DepositStatus, DepositEvent, String> persister(
            StateMachineRuntimePersister<DepositStatus, DepositEvent, String> runtimePersister) {
        return new DefaultStateMachinePersister<>(runtimePersister);
    }
    
    @Bean
    public StateMachineRuntimePersister<DepositStatus, DepositEvent, String> runtimePersister(
            JpaStateMachineRepository jpaRepository) {
        return new JpaPersistingStateMachineInterceptor<>(jpaRepository);
    }
}

点评: Spring State Machine 功能强大,支持分布式、持久化、分层状态机等高级特性。但配置繁琐,学习成本高,而且在高并发场景下性能是个问题(状态机实例需要加锁)。适合对 Spring 生态有执念且状态流转逻辑复杂的项目。


方案五:自研轻量级状态机框架(架构师的自我修养)

如果你觉得 Spring State Machine 太重,又觉得简单的流转表不够灵活,那就自己撸一个吧。

设计目标

  1. 声明式配置:用流式 API 定义状态和流转
  2. 支持 Guard(守卫条件):流转前的条件检查
  3. 支持 Action(动作):流转时执行的业务逻辑
  4. 支持监听器:状态变更通知
  5. 线程安全:支持并发场景
  6. 轻量级:零外部依赖

架构设计

classDiagram class StateMachine~S,E~ { -currentState: S -transitions: Map -listeners: List +fire(event): boolean +getCurrentState(): S +canFire(event): boolean } class StateMachineBuilder~S,E~ { +initialState(state): Builder +configureTransitions(): TransitionConfigurer +build(): StateMachine } class Transition~S,E~ { -source: S -target: S -event: E -guard: Predicate -action: Consumer +execute(context): boolean } class TransitionContext~S,E~ { -source: S -target: S -event: E -payload: Object +getPayload(): T } StateMachineBuilder --> StateMachine StateMachine --> Transition Transition --> TransitionContext

核心代码

/**
 * 流转上下文
 */
@Data
@Builder
public class TransitionContext<S, E> {
    private final S source;
    private final S target;
    private final E event;
    private final Object payload;
    private final Map<String, Object> variables;
    
    @SuppressWarnings("unchecked")
    public <T> T getPayload() {
        return (T) payload;
    }
    
    public <T> T getVariable(String key, Class<T> type) {
        return type.cast(variables.get(key));
    }
}

/**
 * 状态流转定义
 */
@Data
@Builder
public class Transition<S, E> {
    private final S source;
    private final S target;
    private final E event;
    private final Predicate<TransitionContext<S, E>> guard;
    private final Consumer<TransitionContext<S, E>> action;
    
    /**
     * 执行流转
     */
    public boolean execute(TransitionContext<S, E> context) {
        // 检查守卫条件
        if (guard != null && !guard.test(context)) {
            return false;
        }
        
        // 执行动作
        if (action != null) {
            action.accept(context);
        }
        
        return true;
    }
}

/**
 * 状态变更监听器
 */
@FunctionalInterface
public interface StateChangeListener<S, E> {
    void onStateChanged(S from, S to, E event, Object payload);
}

/**
 * 轻量级状态机
 */
@Slf4j
public class LiteStateMachine<S, E> {
    
    private volatile S currentState;
    private final Map<S, Map<E, Transition<S, E>>> transitionTable;
    private final Set<S> finalStates;
    private final List<StateChangeListener<S, E>> listeners;
    private final Map<String, Object> variables;
    private final ReentrantLock lock = new ReentrantLock();
    
    private LiteStateMachine(Builder<S, E> builder) {
        this.currentState = builder.initialState;
        this.transitionTable = builder.transitionTable;
        this.finalStates = builder.finalStates;
        this.listeners = new CopyOnWriteArrayList<>(builder.listeners);
        this.variables = new ConcurrentHashMap<>(builder.variables);
    }
    
    /**
     * 触发事件
     */
    public boolean fire(E event, Object payload) {
        lock.lock();
        try {
            if (isFinalState()) {
                log.warn("当前状态[{}]是终态,无法触发事件[{}]", currentState, event);
                return false;
            }
            
            Map<E, Transition<S, E>> transitions = transitionTable.get(currentState);
            if (transitions == null) {
                log.warn("状态[{}]没有配置任何流转规则", currentState);
                return false;
            }
            
            Transition<S, E> transition = transitions.get(event);
            if (transition == null) {
                log.warn("状态[{}]不支持事件[{}]", currentState, event);
                return false;
            }
            
            TransitionContext<S, E> context = TransitionContext.<S, E>builder()
                .source(currentState)
                .target(transition.getTarget())
                .event(event)
                .payload(payload)
                .variables(new HashMap<>(variables))
                .build();
            
            // 执行流转
            if (!transition.execute(context)) {
                log.info("流转被Guard拦截: {} --[{}]--> {}", currentState, event, transition.getTarget());
                return false;
            }
            
            S oldState = currentState;
            currentState = transition.getTarget();
            
            log.info("状态流转成功: {} --[{}]--> {}", oldState, event, currentState);
            
            // 通知监听器
            listeners.forEach(l -> {
                try {
                    l.onStateChanged(oldState, currentState, event, payload);
                } catch (Exception e) {
                    log.error("监听器执行异常", e);
                }
            });
            
            return true;
            
        } finally {
            lock.unlock();
        }
    }
    
    public boolean fire(E event) {
        return fire(event, null);
    }
    
    public S getCurrentState() {
        return currentState;
    }
    
    public boolean canFire(E event) {
        Map<E, Transition<S, E>> transitions = transitionTable.get(currentState);
        return transitions != null && transitions.containsKey(event);
    }
    
    public Set<E> getAvailableEvents() {
        Map<E, Transition<S, E>> transitions = transitionTable.get(currentState);
        return transitions != null ? transitions.keySet() : Collections.emptySet();
    }
    
    public boolean isFinalState() {
        return finalStates.contains(currentState);
    }
    
    public void setVariable(String key, Object value) {
        variables.put(key, value);
    }
    
    public void addListener(StateChangeListener<S, E> listener) {
        listeners.add(listener);
    }
    
    /**
     * 创建构建器
     */
    public static <S, E> Builder<S, E> builder() {
        return new Builder<>();
    }
    
    /**
     * 状态机构建器
     */
    public static class Builder<S, E> {
        private S initialState;
        private final Map<S, Map<E, Transition<S, E>>> transitionTable = new HashMap<>();
        private final Set<S> finalStates = new HashSet<>();
        private final List<StateChangeListener<S, E>> listeners = new ArrayList<>();
        private final Map<String, Object> variables = new HashMap<>();
        
        public Builder<S, E> initialState(S state) {
            this.initialState = state;
            return this;
        }
        
        public Builder<S, E> finalStates(S... states) {
            this.finalStates.addAll(Arrays.asList(states));
            return this;
        }
        
        public TransitionBuilder transition() {
            return new TransitionBuilder();
        }
        
        public Builder<S, E> listener(StateChangeListener<S, E> listener) {
            this.listeners.add(listener);
            return this;
        }
        
        public Builder<S, E> variable(String key, Object value) {
            this.variables.put(key, value);
            return this;
        }
        
        public LiteStateMachine<S, E> build() {
            Objects.requireNonNull(initialState, "初始状态不能为空");
            return new LiteStateMachine<>(this);
        }
        
        /**
         * 流转构建器
         */
        public class TransitionBuilder {
            private S source;
            private S target;
            private E event;
            private Predicate<TransitionContext<S, E>> guard;
            private Consumer<TransitionContext<S, E>> action;
            
            public TransitionBuilder from(S source) {
                this.source = source;
                return this;
            }
            
            public TransitionBuilder to(S target) {
                this.target = target;
                return this;
            }
            
            public TransitionBuilder on(E event) {
                this.event = event;
                return this;
            }
            
            public TransitionBuilder guard(Predicate<TransitionContext<S, E>> guard) {
                this.guard = guard;
                return this;
            }
            
            public TransitionBuilder action(Consumer<TransitionContext<S, E>> action) {
                this.action = action;
                return this;
            }
            
            public Builder<S, E> and() {
                Transition<S, E> transition = Transition.<S, E>builder()
                    .source(source)
                    .target(target)
                    .event(event)
                    .guard(guard)
                    .action(action)
                    .build();
                
                transitionTable
                    .computeIfAbsent(source, k -> new HashMap<>())
                    .put(event, transition);
                
                return Builder.this;
            }
        }
    }
}

使用示例

/**
 * 入金状态机工厂
 */
@Component
@Slf4j
public class DepositStateMachineFactory {
    
    private final RefundService refundService;
    private final NotificationService notificationService;
    
    public DepositStateMachineFactory(RefundService refundService, 
                                      NotificationService notificationService) {
        this.refundService = refundService;
        this.notificationService = notificationService;
    }
    
    /**
     * 创建入金状态机实例
     */
    public LiteStateMachine<DepositStatus, DepositEvent> create(DepositTransaction transaction) {
        return LiteStateMachine.<DepositStatus, DepositEvent>builder()
            // 根据交易当前状态初始化
            .initialState(transaction.getStatus())
            
            // 定义终态
            .finalStates(DepositStatus.SUCCESS, DepositStatus.MANUAL_FAILED)
            
            // 存储交易信息到上下文
            .variable("transaction", transaction)
            
            // WV -> V: 验证通过
            .transition()
                .from(DepositStatus.WAIT_VERIFY)
                .to(DepositStatus.VERIFIED)
                .on(DepositEvent.VERIFY_PASS)
                .guard(ctx -> {
                    // 检查交易金额是否合法
                    DepositTransaction tx = ctx.getVariable("transaction", DepositTransaction.class);
                    return tx.getAmount() != null && tx.getAmount().compareTo(BigDecimal.ZERO) > 0;
                })
                .and()
            
            // WV -> MC: 验证失败
            .transition()
                .from(DepositStatus.WAIT_VERIFY)
                .to(DepositStatus.MANUAL_CONFIRM)
                .on(DepositEvent.VERIFY_FAIL)
                .action(ctx -> {
                    // 发送人工确认通知
                    notificationService.sendManualConfirmNotify(ctx.getPayload());
                })
                .and()
            
            // V -> P: 入金处理中
            .transition()
                .from(DepositStatus.VERIFIED)
                .to(DepositStatus.PENDING)
                .on(DepositEvent.DEPOSIT_PENDING)
                .and()
            
            // V -> RP: 账户验证失败
            .transition()
                .from(DepositStatus.VERIFIED)
                .to(DepositStatus.REFUND_PROCESSING)
                .on(DepositEvent.ACCOUNT_VALIDATE_FAIL)
                .action(ctx -> refundService.initiateRefund(ctx.getPayload()))
                .and()
            
            // P -> P: 仍在处理中(自循环)
            .transition()
                .from(DepositStatus.PENDING)
                .to(DepositStatus.PENDING)
                .on(DepositEvent.STILL_PENDING)
                .and()
            
            // P -> S: 入金成功
            .transition()
                .from(DepositStatus.PENDING)
                .to(DepositStatus.SUCCESS)
                .on(DepositEvent.DEPOSIT_SUCCESS)
                .action(ctx -> {
                    log.info("入金成功,更新账户余额");
                    // 更新账户余额等业务逻辑
                })
                .and()
            
            // P -> RP: 入金失败
            .transition()
                .from(DepositStatus.PENDING)
                .to(DepositStatus.REFUND_PROCESSING)
                .on(DepositEvent.DEPOSIT_FAILED)
                .action(ctx -> refundService.initiateRefund(ctx.getPayload()))
                .and()
            
            // MC -> RP: 人工确认退款
            .transition()
                .from(DepositStatus.MANUAL_CONFIRM)
                .to(DepositStatus.REFUND_PROCESSING)
                .on(DepositEvent.MANUAL_CONFIRM_REFUND)
                .action(ctx -> refundService.initiateRefund(ctx.getPayload()))
                .and()
            
            // MC -> RS: 人工确认退款成功
            .transition()
                .from(DepositStatus.MANUAL_CONFIRM)
                .to(DepositStatus.REFUND_SUCCESS)
                .on(DepositEvent.MANUAL_CONFIRM_SUCCESS)
                .and()
            
            // MC -> MF: 人工确认失败
            .transition()
                .from(DepositStatus.MANUAL_CONFIRM)
                .to(DepositStatus.MANUAL_FAILED)
                .on(DepositEvent.MANUAL_CONFIRM_FAILED)
                .and()
            
            // RP -> RS: 退款成功
            .transition()
                .from(DepositStatus.REFUND_PROCESSING)
                .to(DepositStatus.REFUND_SUCCESS)
                .on(DepositEvent.REFUND_SUCCESS)
                .and()
            
            // RP -> RF: 退款失败
            .transition()
                .from(DepositStatus.REFUND_PROCESSING)
                .to(DepositStatus.REFUND_FAILED)
                .on(DepositEvent.REFUND_FAILED)
                .action(ctx -> notificationService.sendManualConfirmNotify(ctx.getPayload()))
                .and()
            
            // RF -> RS: 人工确认退款成功
            .transition()
                .from(DepositStatus.REFUND_FAILED)
                .to(DepositStatus.REFUND_SUCCESS)
                .on(DepositEvent.MANUAL_CONFIRM_SUCCESS)
                .and()
            
            // RS -> RT: 收到退票通知
            .transition()
                .from(DepositStatus.REFUND_SUCCESS)
                .to(DepositStatus.RETURN_TICKET)
                .on(DepositEvent.RECEIVE_RETURN_TICKET)
                .action(ctx -> notificationService.sendManualConfirmNotify(ctx.getPayload()))
                .and()
            
            // RT -> RS: 人工确认退款成功
            .transition()
                .from(DepositStatus.RETURN_TICKET)
                .to(DepositStatus.REFUND_SUCCESS)
                .on(DepositEvent.MANUAL_CONFIRM_SUCCESS)
                .and()
            
            // 添加状态变更监听器
            .listener((from, to, event, payload) -> {
                log.info("状态机状态变更: {} --[{}]--> {}", from, to, event);
                // 可以在这里做审计日志、消息通知等
            })
            
            .build();
    }
}

/**
 * 使用示例
 */
@Service
@Slf4j
public class DepositService {
    
    private final DepositStateMachineFactory stateMachineFactory;
    private final DepositTransactionRepository repository;
    
    @Transactional
    public void processDeposit(String transactionId, DepositEvent event, Object payload) {
        DepositTransaction transaction = repository.findById(transactionId)
            .orElseThrow(() -> new IllegalArgumentException("交易不存在"));
        
        // 创建状态机
        LiteStateMachine<DepositStatus, DepositEvent> stateMachine = 
            stateMachineFactory.create(transaction);
        
        // 触发事件
        boolean success = stateMachine.fire(event, payload);
        
        if (success) {
            // 更新交易状态
            transaction.setStatus(stateMachine.getCurrentState());
            repository.save(transaction);
            log.info("交易状态更新成功: {}", transaction);
        } else {
            log.warn("状态流转失败,交易ID: {}, 当前状态: {}, 事件: {}", 
                transactionId, transaction.getStatus(), event);
        }
    }
}

点评: 自研方案可以完全按照业务需求定制,没有任何冗余功能。缺点是需要自己维护,出了 bug 只能自己扛。适合有一定技术积累、追求极致可控的团队。


方案对比

方案复杂度可扩展性性能学习成本适用场景
Switch/If-Else⭐⭐⭐⭐⭐状态少于5个的简单场景
状态模式⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐状态行为复杂、需要独立测试
状态枚举流转表⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐中小型项目,推荐首选
Spring State Machine⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐Spring 生态、需要分布式支持
自研轻量级框架⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐追求极致可控、有技术积累

最佳实践建议

  1. 状态枚举一定要有:不管用哪种方案,状态和事件都应该用枚举定义,避免魔法字符串
  2. 流转规则要可视化:建议用 Mermaid 或者其他工具画出状态流转图,方便团队理解和评审
  3. 做好审计日志:每次状态变更都要记录,方便排查问题
  4. 考虑并发场景:多个线程同时触发同一笔交易的状态流转,需要加锁或者用乐观锁
  5. 幂等性很重要:同一个事件重复触发,结果应该一致
  6. 分离状态和业务逻辑:状态流转逻辑和业务处理逻辑要分开,职责清晰

总结

状态机看起来简单,写好却不容易。从最原始的 if-else 到 Spring State Machine,每种方案都有其适用场景。

我的建议是:

  • 快速原型:用 Switch 或状态枚举表
  • 长期维护的项目:用状态枚举表或自研轻量级框架
  • 大型分布式系统:考虑 Spring State Machine 或者自研支持分布式的方案

记住,没有最好的方案,只有最合适的方案。就像写代码一样,不是炫技,而是解决问题。

如果你看到这里还没放弃,那恭喜你,你已经比大部分人更了解状态机了。现在,去把你项目里的 if-else 地狱重构一下吧!

评论区
暂无评论
avatar