系统设计——状态机模型设计经验

344 阅读13分钟

摘要

本文主要介绍了状态机模型的设计经验,包括其定义、适用场景、建模示例、事件驱动设计以及配置数据化等内容。状态机模型通过事件驱动控制状态变化,适用于流程驱动系统、生命周期管理等场景,不适用于状态变化简单或不确定的场景。文中还提供了基于“信贷审批流程”的项目示例和事件驱动流转的Spring项目示例,最后总结了状态机模式设计的优势。

1. 状态机模型(State Machine Model)设计

1.1. 🧭 什么是数据状态机模型?

数据状态机模型是一种用来表示对象在不同“状态”之间流转的结构模型,通过“事件驱动”来控制状态变化,并可绑定“动作”、“条件”、“权限”等。

一个典型的状态机由以下几个部分组成:

组件描述
状态(State)对象处于的某种业务阶段,例如“待审批”、“已发货”
事件(Event)引起状态变化的触发,例如“审批通过”、“发货”
转移(Transition)定义从哪一个状态,通过什么事件,转移到哪个状态
动作(Action)状态变化时要执行的操作(如通知、写日志等)
条件(Guard)转移是否允许发生的判断条件(如权限、金额限制)

1.2. 📦 状态机适合什么场景?

1.2.1. ✅ 适合的场景(推荐使用):

场景类型示例
流程驱动系统审批流程、工作流引擎、合同流转、风险审核等
生命周期管理订单状态、任务状态、工单状态、用户状态、设备状态
多状态 + 严格控制流转状态间只能在特定事件触发、满足条件下跳转的系统
规则复杂、流程分支多比如信贷审批流程(审批 → 审批通过/拒绝 → 放款/结清)
需要追踪、可视化流程风控审核、OA 审批、ERP 物料流转、电子合同签署

1.2.2. ❌ 不适合的场景(无需使用):

场景类型原因
状态变化简单只有一个状态字段,状态转移无规则限制
状态变化不确定状态变更不固定、难以建模,如动态标签分类
流程非常简单没有“事件”,直接在业务代码里改状态即可
高性能极致场景状态机带来一定开销,不适合毫秒级超高频场景

1.3. 任务状态机与工作流状态机模型对比

1.3.1. 任务状态机模型(Task State Machine)

✅ 定义:任务状态机是用于描述“单个任务”的生命周期状态流转的一种模型,它关注的是“某个对象/任务”自身的状态变化。

1.3.2. 📌 使用场景

典型场景说明
✅ 订单处理订单状态:创建 → 支付中 → 已支付 → 已发货 → 已完成 / 已取消
✅ 审批单据审批单状态:待审批 → 审批中 → 已通过 / 被拒绝
✅ 运单管理运单状态:已创建 → 配送中 → 已签收 / 失败
✅ 任务调度任务状态:待执行 → 执行中 → 成功 / 失败 / 超时

1.3.3. 👍 优点

  • 简单清晰,聚焦一个对象的生命周期
  • 状态可配置、可视化
  • 易于维护和测试

1.3.4. 🔄 工作流状态机模型(Workflow State Machine)

✅ 定义:工作流状态机模型用于表示多个任务节点之间的执行顺序与依赖关系,关注的是流程结构、任务节点之间的衔接和条件流转。

1.3.5. 📌 使用场景

典型场景说明
✅ 审批流引擎流程:提交申请 → 部门审批 → 财务审批 → 总经理审批 → 完成
✅ BPM 流程平台支持流程编排,多个节点按条件分支/汇聚
✅ 流程自动化自动化数据采集、爬虫、清洗等按流程步骤进行
✅ 多阶段任务例如:贷款流程中的 授信审核 → 额度确认 → 合同签署 → 放款

1.3.6. 👍 优点

  • 表达复杂流程的逻辑依赖关系
  • 支持并发、条件、循环、分支等流程控制
  • 灵活性强,适用于流程驱动型系统

1.3.7. 📊 对比总结

比较维度任务状态机模型工作流状态机模型
关注点单个任务状态的变化多个节点/任务之间的控制流转
适合场景订单、审批单、任务、运单等审批流、BPM、业务流程引擎
流程复杂性简单,线性高,支持条件判断、并行、回滚等
是否节点驱动否(一个对象驱动)是(流程节点驱动)
状态转移发起者通常由任务自身逻辑或外部事件驱动由流程引擎或上游节点驱动

1.4. 📘 状态机建模示例(数据驱动设计)

假设你有一个“信贷审批流程”,包含以下状态:

APPLY → WAIT_APPROVE → APPROVED/REJECTED → LOANED → COMPLETED

1.4.1. 数据模型表结构设计:

状态表 fsm_state

idcodename
1APPLY已申请
2WAIT_APPROVE待审批
3APPROVED审批通过
4REJECTED审批拒绝
5LOANED已放款
6COMPLETED已结清

事件表 fsm_event

idcodename
1SUBMIT提交申请
2APPROVE审批通过
3REJECT审批拒绝
4LOAN放款
5CLOSE结清

状态转移表 fsm_transition

idsource_stateeventtarget_statecondition_classaction_class
1APPLYSUBMITWAIT_APPROVEnullnull
2WAIT_APPROVEAPPROVEAPPROVEDauditGuardnotifyAction
3WAIT_APPROVEREJECTREJECTEDnullnotifyAction
4APPROVEDLOANLOANEDcheckLimitGuardloanAction
5LOANEDCLOSECOMPLETEDnullnull

1.5. 基于“信贷审批流程”项目示例

1.5.1. 🧱 数据库结构

建表 SQL(推荐用 MySQL)

CREATE TABLE fsm_state (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  state_code VARCHAR(50) NOT NULL,
  state_name VARCHAR(100) NOT NULL
);

CREATE TABLE fsm_event (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  event_code VARCHAR(50) NOT NULL,
  event_name VARCHAR(100) NOT NULL
);

CREATE TABLE fsm_transition (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  source_state VARCHAR(50) NOT NULL,
  event_code VARCHAR(50) NOT NULL,
  target_state VARCHAR(50) NOT NULL,
  condition_class VARCHAR(100),
  action_class VARCHAR(100)
);

初始化数据(data.sql)

-- 状态
INSERT INTO fsm_state (state_code, state_name) VALUES
('APPLY', '已申请'),
('WAIT_APPROVE', '待审批'),
('APPROVED', '审批通过'),
('REJECTED', '审批拒绝'),
('LOANED', '已放款'),
('COMPLETED', '已结清');

-- 事件
INSERT INTO fsm_event (event_code, event_name) VALUES
('SUBMIT', '提交'),
('APPROVE', '审批通过'),
('REJECT', '审批拒绝'),
('LOAN', '放款'),
('CLOSE', '结清');

-- 流转
INSERT INTO fsm_transition (source_state, event_code, target_state, condition_class, action_class) VALUES
('APPLY', 'SUBMIT', 'WAIT_APPROVE', NULL, NULL),
('WAIT_APPROVE', 'APPROVE', 'APPROVED', 'auditGuard', 'notifyAction'),
('WAIT_APPROVE', 'REJECT', 'REJECTED', NULL, 'notifyAction'),
('APPROVED', 'LOAN', 'LOANED', NULL, 'loanAction'),
('LOANED', 'CLOSE', 'COMPLETED', NULL, NULL);

1.5.2. 📦 项目依赖(pom.xml)

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.statemachine</groupId>
    <artifactId>spring-statemachine-starter</artifactId>
    <version>3.2.0</version>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
  </dependency>
</dependencies>

1.5.3. ⚙️ 配置文件 application.yml

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/yourdb?useSSL=false
    username: root
    password: root
  jpa:
    hibernate:
      ddl-auto: none
    show-sql: true

1.5.4. 🧩 实体类与 Repository

FsmState.java

@Entity
@Data
public class FsmState {
    @Id @GeneratedValue private Long id;
    private String stateCode;
    private String stateName;
}

FsmEvent.java

@Entity
@Data
public class FsmEvent {
    @Id @GeneratedValue private Long id;
    private String eventCode;
    private String eventName;
}

FsmTransition.java

@Entity
@Data
public class FsmTransition {
    @Id @GeneratedValue private Long id;
    private String sourceState;
    private String eventCode;
    private String targetState;
    private String conditionClass;
    private String actionClass;
}

1.5.5. Repository 接口

public interface FsmStateRepository extends JpaRepository<FsmState, Long> {}
public interface FsmEventRepository extends JpaRepository<FsmEvent, Long> {}
public interface FsmTransitionRepository extends JpaRepository<FsmTransition, Long> {}

1.5.6. 🔧 Action 和 Guard 实现

NotifyAction.java

@Component("notifyAction")
public class NotifyAction implements Action<String, String> {
    @Override
    public void execute(StateContext<String, String> context) {
        System.out.println("【通知】状态变更:" + context.getSource().getId() + " → " + context.getTarget().getId());
    }
}

LoanAction.java

@Component("loanAction")
public class LoanAction implements Action<String, String> {
    @Override
    public void execute(StateContext<String, String> context) {
        System.out.println("【放款动作】业务放款处理中...");
    }
}

AuditGuard.java

@Component("auditGuard")
public class AuditGuard implements Guard<String, String> {
    @Override
    public boolean evaluate(StateContext<String, String> context) {
        return true; // 模拟审批通过
    }
}

1.5.7. 🏗️ 构建状态机

DynamicStateMachineBuilder.java

@Component
@RequiredArgsConstructor
public class DynamicStateMachineBuilder {

    private final ApplicationContext context;
    private final FsmStateRepository stateRepo;
    private final FsmTransitionRepository transRepo;

    public StateMachine<String, String> build() throws Exception {
        var builder = StateMachineBuilder.builder();

        Set<String> states = stateRepo.findAll().stream().map(FsmState::getStateCode).collect(Collectors.toSet());
        List<FsmTransition> transitions = transRepo.findAll();

        builder.configureStates().withStates().initial("APPLY").states(states);

        var config = builder.configureTransitions();
        for (FsmTransition t : transitions) {
            var transition = config.withExternal()
            .source(t.getSourceState())
            .target(t.getTargetState())
            .event(t.getEventCode());

            if (t.getConditionClass() != null) {
                transition.guard((Guard<String, String>) context.getBean(t.getConditionClass()));
            }

            if (t.getActionClass() != null) {
                transition.action((Action<String, String>) context.getBean(t.getActionClass()));
            }
        }

        return builder.build();
    }
}

1.5.8. 🧪 状态机执行器

StateMachineExecutor.java

@Service
@RequiredArgsConstructor
public class StateMachineExecutor {

    private final DynamicStateMachineBuilder builder;

    public String process(String currentState, String event) throws Exception {
        StateMachine<String, String> machine = builder.build();

        machine.getStateMachineAccessor()
        .doWithAllRegions(access -> access.resetStateMachine(
            new DefaultStateMachineContext<>(currentState, null, null, null)));

        machine.start();
        machine.sendEvent(event);

        return machine.getState().getId();
    }
}

1.5.9. 🌐 控制器示例

@RestController
@RequiredArgsConstructor
@RequestMapping("/fsm")
public class StateMachineController {

    private final StateMachineExecutor executor;

    @PostMapping("/trigger")
    public String trigger(@RequestParam String state, @RequestParam String event) throws Exception {
        String nextState = executor.process(state, event);
        return "当前状态:" + state + ",事件:" + event + ",转移后状态:" + nextState;
    }
}

1.5.10. ✅ 测试流程

# 初始状态 APPLY,事件 SUBMIT → WAIT_APPROVE
curl -X POST "http://localhost:8080/fsm/trigger?state=APPLY&event=SUBMIT"

# 状态 WAIT_APPROVE,事件 APPROVE → APPROVED
curl -X POST "http://localhost:8080/fsm/trigger?state=WAIT_APPROVE&event=APPROVE"

# 状态 APPROVED,事件 LOAN → LOANED
curl -X POST "http://localhost:8080/fsm/trigger?state=APPROVED&event=LOAN"

# 状态 LOANED,事件 CLOSE → COMPLETED
curl -X POST "http://localhost:8080/fsm/trigger?state=LOANED&event=CLOSE"

1.5.11. 📘 后续扩展建议

扩展点说明
多流程模型支持添加流程 ID 字段,支持多状态机模型
可视化建模器使用 Flowable 或自己实现状态流建模
状态持久化实现状态存储,支持恢复流程(可加 redis/db)
条件脚本执行动态脚本引擎(Groovy/SpEL)处理条件判断
权限控制/审计日志支持可集成权限服务和日志系统

1.6. ✅ 状态机模式设计总结

说明
状态机适用场景审批流、订单、合同、用户状态管理等流程控制系统
状态机不适用场景状态少、变化简单、无流程控制的轻量业务
建模建议状态、事件、流转配置建议入库 + 动态加载
技术选型建议小型项目可用枚举硬编码,大型可用spring-statemachine+ 数据驱动

2. 事件驱动设计

2.1. ✅ 什么是“事件驱动流转”?

事件驱动流转(Event-Driven Transition) 是一种通过 事件触发来驱动系统状态变化或执行逻辑 的方式。它是“事件驱动架构(EDA)”的核心理念之一,常用于业务流程的状态机设计中。通俗的解释就是:把一个系统看成一个有限状态机(Finite State Machine, FSM) ,它有多个“状态”,而“事件”就像“命令”或“信号”,告诉系统应该从一个状态跳转到另一个状态

2.1.1. 🧠 为什么要使用事件驱动流转?

  1. 提高可维护性:避免硬编码状态判断和修改。
  2. 支持复杂流程:比如分支判断、回退、并行状态等。
  3. 方便扩展:后期加新状态或事件不改业务核心逻辑。
  4. 天然支持异步/微服务架构:可通过消息总线发送事件,实现解耦。

2.1.2. 🧩 延伸:事件可以来源于

  • 用户行为(点击、提交)
  • 定时任务(定期检查过期状态)
  • 消息队列(MQ 传入事件)
  • 外部系统回调(比如支付平台通知)

2.1.3. 🎯 核心要素:

概念说明
状态系统或数据当前的情况,比如“待支付”、“已支付”、“已发货”
事件某个动作或输入,比如“支付成功”、“发货按钮点击”
流转(Transition)在事件发生后状态的变更,比如“收到支付成功事件”后,状态从“待支付”变为“已支付”

2.2. 📦 示例:订单状态流转

假设你有一个电商订单系统,订单的状态如下:

初始状态: 待支付
→ [支付成功事件] → 已支付
→ [发货事件] → 已发货
→ [签收事件] → 已完成

如果是事件驱动:你不是直接改状态,而是:

orderStateMachine.sendEvent(OrderEvent.PAY); // 自动将状态从“待支付”→“已支付”

2.3. 🚀 事件驱动 vs 手动流转(对比)

方式手动流转事件驱动流转
状态变更方式代码直接修改字段:order.setStatus(...)触发事件:stateMachine.sendEvent(OrderEvent.X)
可读性状态逻辑分散在代码中状态变更清晰、集中在状态机配置中
复杂流程管理不容易维护易于添加新状态、路径,支持条件、动作等复杂流转
异步支持不方便可结合消息队列实现异步事件触发

2.4. 事件驱动流转 spring项目示例

下面是一个基于 Spring Boot + Spring State Machine 实现“事件驱动流转” 的完整项目示例,模拟一个简单的“订单流转”场景。

2.5. ✅ Spring示例项目说明

业务场景:订单状态变更

  • 初始状态WAITING_PAYMENT
  • 事件驱动变更
    • PAY → 状态变更为 PAID
    • SHIP → 状态变更为 SHIPPED
    • RECEIVE → 状态变更为 RECEIVED

2.5.1. 📁 项目结构

spring-state-machine-demo/
├── pom.xml
└── src/main/java/com/example/statemachine/
    ├── OrderStatus.java
    ├── OrderEvent.java
    ├── Order.java
    ├── OrderController.java
    ├── OrderService.java
    ├── OrderStateMachineConfig.java
    └── StateMachineApplication.java

2.5.2. 1️⃣ pom.xml 依赖

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>spring-state-machine-demo</artifactId>
  <version>1.0.0</version>
  <dependencies>
    <!-- Spring Boot -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Spring State Machine -->
    <dependency>
      <groupId>org.springframework.statemachine</groupId>
      <artifactId>spring-statemachine-starter</artifactId>
      <version>3.2.0</version>
    </dependency>
  </dependencies>
</project>

2.5.3. 2️⃣ 状态&事件定义

OrderStatus.java

public enum OrderStatus {
    WAITING_PAYMENT,
    PAID,
    SHIPPED,
    RECEIVED
}

OrderEvent.java

public enum OrderEvent {
    PAY, SHIP, RECEIVE
}

2.5.4. 3️⃣ 状态机配置

OrderStateMachineConfig.java

@Configuration
@EnableStateMachine
public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderStatus, OrderEvent> {

    @Override
    public void configure(StateMachineStateConfigurer<OrderStatus, OrderEvent> states) throws Exception {
        states.withStates()
        .initial(OrderStatus.WAITING_PAYMENT)
        .state(OrderStatus.PAID)
        .state(OrderStatus.SHIPPED)
        .end(OrderStatus.RECEIVED);
    }

    @Override
    public void configure(StateMachineTransitionConfigurer<OrderStatus, OrderEvent> transitions) throws Exception {
        transitions
        .withExternal().source(OrderStatus.WAITING_PAYMENT).target(OrderStatus.PAID).event(OrderEvent.PAY)
        .and()
        .withExternal().source(OrderStatus.PAID).target(OrderStatus.SHIPPED).event(OrderEvent.SHIP)
        .and()
        .withExternal().source(OrderStatus.SHIPPED).target(OrderStatus.RECEIVED).event(OrderEvent.RECEIVE);
    }
}

2.5.5. 4️⃣ 业务模型

Order.java

@Data
public class Order {
    private String id;
    private OrderStatus status;

    public Order(String id) {
        this.id = id;
        this.status = OrderStatus.WAITING_PAYMENT;
    }
}

2.5.6. 5️⃣ 服务类

OrderService.java

@Service
public class OrderService {

    @Autowired
    private StateMachine<OrderStatus, OrderEvent> stateMachine;

    private Map<String, Order> orders = new HashMap<>();

    public Order createOrder(String id) {
        Order order = new Order(id);
        orders.put(id, order);
        return order;
    }

    public Order sendEvent(String orderId, OrderEvent event) {
        Order order = orders.get(orderId);
        if (order == null) {
            throw new RuntimeException("Order not found");
        }

        stateMachine.stop();
        stateMachine.getStateMachineAccessor()
        .doWithAllRegions(access -> access.resetStateMachine(new DefaultStateMachineContext<>(order.getStatus(), null, null, null)));
        stateMachine.start();

        boolean accepted = stateMachine.sendEvent(event);
        if (accepted) {
            order.setStatus(stateMachine.getState().getId());
        }
        return order;
    }

    public Order getOrder(String id) {
        return orders.get(id);
    }
}

2.5.7. 6️⃣ 控制器接口

OrderController.java

@RestController
@RequestMapping("/orders")
public class OrderController {

    @Autowired
    private OrderService orderService;

    @PostMapping("/{id}")
    public Order create(@PathVariable String id) {
        return orderService.createOrder(id);
    }

    @PostMapping("/{id}/event")
    public Order trigger(@PathVariable String id, @RequestParam OrderEvent event) {
        return orderService.sendEvent(id, event);
    }

    @GetMapping("/{id}")
    public Order get(@PathVariable String id) {
        return orderService.getOrder(id);
    }
}

2.5.8. 7️⃣ 启动类

StateMachineApplication.java

@SpringBootApplication
public class StateMachineApplication {
    public static void main(String[] args) {
        SpringApplication.run(StateMachineApplication.class, args);
    }
}

2.5.9. 📬 示例调用流程(Postman / curl)

# 创建订单
POST http://localhost:8080/orders/123

# 触发支付事件
POST http://localhost:8080/orders/123/event?event=PAY

# 触发发货事件
POST http://localhost:8080/orders/123/event?event=SHIP

# 触发签收事件
POST http://localhost:8080/orders/123/event?event=RECEIVE

# 查看订单状态
GET http://localhost:8080/orders/123

2.5.10. ✅ 项目特点

  • 使用 事件驱动方式 处理状态变更
  • 状态机配置清晰
  • 便于扩展、复用、统一管理业务流转

3. 状态机配置数据化,动态加载执行状态转移设计

3.1. ✅ 将下面这些配置信息持久化(存入数据库或配置中心):

类型示例
状态列表WAITING, PAID, SHIPPED, ...
事件列表PAY, SHIP, RECEIVE, ...
流转表从哪个状态,接收哪个事件,到哪个状态
条件/动作流转时要不要执行校验/回调等业务

3.2. ✅ 数据库表设计(最基础版本)

3.2.1. 状态定义表 fsm_state

idstate_codestate_name
1WAITING_PAYMENT待支付
2PAID已支付

3.2.2. 事件定义表 fsm_event

idevent_codeevent_name
1PAY支付成功

3.2.3. 状态流转配置表 fsm_transition

idsource_stateevent_codetarget_statecondition_classaction_class
1WAITING_PAYMENTPAYPAIDnullnull

3.3. ✅ 动态状态机加载实现(核心代码结构)

3.3.1. 加载状态机模型

public class DynamicStateMachineModel {
    private Set<String> states;
    private Set<String> events;
    private List<Transition> transitions;

    public static class Transition {
        private String source;
        private String event;
        private String target;
        private String conditionBean;
        private String actionBean;
    }
}

3.3.2. 动态构建 Spring State Machine

@Component
public class DynamicStateMachineBuilder {

    @Autowired
    private ApplicationContext context;

    public StateMachine<String, String> build(DynamicStateMachineModel model) throws Exception {
        StateMachineBuilder.Builder<String, String> builder = StateMachineBuilder.builder();

        builder.configureStates()
        .withStates()
        .initial(model.states.iterator().next())
        .states(new HashSet<>(model.states));

        StateMachineTransitionConfigurer<String, String> transitions = builder.configureTransitions();

        for (DynamicStateMachineModel.Transition t : model.getTransitions()) {
            ExternalTransitionConfigurer<String, String> config = transitions
            .withExternal()
            .source(t.getSource())
            .target(t.getTarget())
            .event(t.getEvent());

            // 条件动态注入
            if (t.getConditionBean() != null) {
                Guard<String, String> guard = context.getBean(t.getConditionBean(), Guard.class);
                config.guard(guard);
            }

            // 动作动态注入
            if (t.getActionBean() != null) {
                Action<String, String> action = context.getBean(t.getActionBean(), Action.class);
                config.action(action);
            }
        }

        return builder.build();
    }
}

3.4. ✅ 业务调用示例

public class StateMachineExecutor {

    @Autowired
    private DynamicStateMachineBuilder builder;

    @Autowired
    private FsmRepository fsmRepository;

    public void process(String bizId, String currentState, String event) {
        
        // 1. 查询数据库加载流程模型
        DynamicStateMachineModel model = fsmRepository.loadModel();

        // 2. 构建状态机
        StateMachine<String, String> machine = builder.build(model);

        // 3. 初始化状态
        machine.getStateMachineAccessor()
        .doWithAllRegions(access -> access.resetStateMachine(new DefaultStateMachineContext<>(currentState, null, null, null)));

        machine.start();

        // 4. 发送事件驱动流转
        machine.sendEvent(event);

        // 5. 保存新状态
        String newState = machine.getState().getId();
        System.out.println("新状态为:" + newState);
    }
}

3.5. ✅ Action 和 Guard 可插拔式实现(可动态配置 Bean)

3.5.1. 示例Action

@Component("logAction")
public class LogAction implements Action<String, String> {
    
    @Override
    public void execute(StateContext<String, String> context) {
        System.out.println("动作:记录日志");
    }
}

3.5.2. 示例Guard

@Component("checkPaymentGuard")
public class CheckPaymentGuard implements Guard<String, String> {
    
    @Override
    public boolean evaluate(StateContext<String, String> context) {
        // 模拟业务校验逻辑
        return true;
    }
}

3.6. ✅ 优势总结

特点优势
动态配置状态、事件、流转逻辑存数据库或配置中心
易于扩展支持多个流程(订单、审批、签约等)
可视化支持可做可视化流程设计器
动作/条件可插拔支持动态行为绑定
解耦状态逻辑不写死在代码里

博文参考