【架构实战】状态机架构:订单/工单状态流转设计

一、状态机概述

状态机(State Machine)是一种非常重要的业务逻辑设计模式:

核心概念:

  • 有限个状态(State)
  • 状态之间的转换(Transition)
  • 触发转换的事件(Event)
  • 每个状态对应的动作(Action)

解决的问题:

  • 订单状态流转的合法性
  • 工单流程的规范管理
  • 状态转换的可追溯性
  • 并发操作的安全性

二、状态机核心概念

1. 状态机模型

复制代码
┌─────────────────────────────────────────────────────────────────┐
│                      订单状态机                                   │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│         ┌─────────┐                                            │
│         │  PENDING │  创建订单                                  │
│         └────┬────┘                                            │
│              │ pay()                                           │
│              ▼                                                  │
│         ┌─────────┐                                            │
│    ┌───▶│  PAID   │ 支付成功                                   │
│    │     └────┬────┘                                            │
│    │          │ deliver()                                       │
│    │          ▼                                                  │
│    │     ┌─────────┐                                            │
│    │     │SHIPPING │ 发货                                       │
│    │     └────┬────┘                                            │
│    │          │ receive()                                       │
│    │          ▼                                                  │
│    │     ┌─────────┐                                            │
│    │     │COMPLETED│ 完成                                       │
│    │     └─────────┘                                            │
│    │                                                          │
│    │     ┌─────────┐                                            │
│    └─────│ CANCELLED│ 取消                                       │
│ cancel()└─────────┘                                            │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

2. 状态机组成

复制代码
StateMachine = (States, Events, Transitions, Actions, Guards)

- States:      状态的集合
- Events:      触发事件
- Transitions: 状态转换规则
- Actions:     转换时执行的动作
- Guards:      转换条件判断

三、状态机实现

1. 状态枚举

java 复制代码
// 订单状态枚举
public enum OrderStatus {
    PENDING("待支付"),
    PAID("已支付"),
    SHIPPING("配送中"),
    COMPLETED("已完成"),
    CANCELLED("已取消");
    
    private final String description;
    
    OrderStatus(String description) {
        this.description = description;
    }
    
    public String getDescription() {
        return description;
    }
}

// 订单事件枚举
public enum OrderEvent {
    PAY("支付"),
    CANCEL("取消"),
    DELIVER("发货"),
    RECEIVE("收货"),
    REFUND("退款");
}

2. 状态转换定义

java 复制代码
// 状态转换配置
@Data
public class StateTransition {
    private OrderStatus from;
    private OrderEvent event;
    private OrderStatus to;
    private String action;          // 执行的动作
    private String guard;          // 守卫条件
}

// 状态机配置
@Configuration
public class OrderStateMachineConfig {
    
    @Bean
    public Map<String, StateTransition> orderTransitions() {
        Map<String, StateTransition> transitions = new HashMap<>();
        
        // 待支付 -> 已支付(支付事件)
        transitions.put("PENDING:PAY", StateTransition.builder()
            .from(OrderStatus.PENDING)
            .event(OrderEvent.PAY)
            .to(OrderStatus.PAID)
            .action("paymentService.processPayment")
            .build());
        
        // 已支付 -> 配送中(发货事件)
        transitions.put("PAID:DELIVER", StateTransition.builder()
            .from(OrderStatus.PAID)
            .event(OrderEvent.DELIVER)
            .to(OrderStatus.SHIPPING)
            .action("logisticsService.ship")
            .build());
        
        // 配送中 -> 已完成(收货事件)
        transitions.put("SHIPPING:RECEIVE", StateTransition.builder()
            .from(OrderStatus.SHIPPING)
            .event(OrderEvent.RECEIVE)
            .to(OrderStatus.COMPLETED)
            .action("orderService.complete")
            .build());
        
        // 待支付 -> 已取消(取消事件)
        transitions.put("PENDING:CANCEL", StateTransition.builder()
            .from(OrderStatus.PENDING)
            .event(OrderEvent.CANCEL)
            .to(OrderStatus.CANCELLED)
            .action("orderService.cancel")
            .build());
        
        // 已支付 -> 已取消(取消事件,需要退款)
        transitions.put("PAID:CANCEL", StateTransition.builder()
            .from(OrderStatus.PAID)
            .event(OrderEvent.CANCEL)
            .to(OrderStatus.CANCELLED)
            .action("refundService.refund")
            .guard("paymentAmount > 0")
            .build());
        
        return transitions;
    }
}

3. 状态机实现

java 复制代码
@Service
public class OrderStateMachine {
    
    @Autowired
    private Map<String, StateTransition> transitions;
    
    // 执行状态转换
    public void fire(Order order, OrderEvent event) {
        String key = order.getStatus() + ":" + event;
        StateTransition transition = transitions.get(key);
        
        if (transition == null) {
            throw new InvalidTransitionException(
                "状态转换不存在: " + order.getStatus() + " + " + event);
        }
        
        // 检查守卫条件
        if (!checkGuard(order, transition.getGuard())) {
            throw new GuardViolationException("不满足转换条件");
        }
        
        // 执行动作
        executeAction(order, transition.getAction());
        
        // 更新状态
        order.setStatus(transition.getTo());
        orderRepository.save(order);
        
        // 记录状态变更日志
        logStatusChange(order, transition);
    }
    
    // 获取可用的转换
    public List<OrderEvent> getAvailableEvents(Order order) {
        return transitions.values().stream()
            .filter(t -> t.getFrom() == order.getStatus())
            .filter(t -> checkGuard(order, t.getGuard()))
            .map(StateTransition::getEvent)
            .collect(Collectors.toList());
    }
    
    private boolean checkGuard(Order order, String guard) {
        if (guard == null || guard.isEmpty()) {
            return true;
        }
        
        // 简单的守卫实现
        switch (guard) {
            case "paymentAmount > 0":
                return order.getPaymentAmount() != null && order.getPaymentAmount() > 0;
            default:
                return true;
        }
    }
    
    private void executeAction(Order order, String action) {
        if (action == null || action.isEmpty()) {
            return;
        }
        
        // 简单的动作执行实现
        // 实际项目中可以使用Spring的MethodInvoker或规则引擎
    }
}

四、状态机持久化

1. 状态变更记录

java 复制代码
// 状态变更记录
@Entity
@Table(name = "order_status_log")
public class OrderStatusLog {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String orderId;
    private OrderStatus fromStatus;
    private OrderStatus toStatus;
    private OrderEvent event;
    private String operator;
    private String remark;
    private LocalDateTime createdAt;
}

// 保存状态变更记录
@Service
public class OrderStateMachine {
    
    @Autowired
    private OrderStatusLogRepository logRepository;
    
    private void logStatusChange(Order order, StateTransition transition) {
        OrderStatusLog log = OrderStatusLog.builder()
            .orderId(order.getId().toString())
            .fromStatus(transition.getFrom())
            .toStatus(transition.getTo())
            .event(transition.getEvent())
            .operator(getCurrentOperator())
            .createdAt(LocalDateTime.now())
            .build();
        
        logRepository.save(log);
    }
}

2. 乐观锁实现

java 复制代码
// 使用乐观锁防止并发问题
@Entity
public class Order {
    
    @Id
    private Long id;
    
    @Enumerated(EnumType.STRING)
    private OrderStatus status;
    
    @Version
    private Long version;  // 乐观锁版本
    
    // 状态转换时检查版本
    public void changeStatus(OrderStatus newStatus, Long expectedVersion) {
        if (!this.version.equals(expectedVersion)) {
            throw new OptimisticLockException("订单状态已被其他操作修改");
        }
        this.status = newStatus;
        this.version = expectedVersion + 1;
    }
}

// 使用服务
@Service
public class OrderService {
    
    @Transactional
    public void payOrder(Long orderId, PaymentInfo paymentInfo) {
        Order order = orderRepository.findById(orderId)
            .orElseThrow(() -> new OrderNotFoundException(orderId));
        
        // 获取当前版本
        Long currentVersion = order.getVersion();
        
        // 检查状态
        if (order.getStatus() != OrderStatus.PENDING) {
            throw new InvalidStateException("订单状态不允许支付");
        }
        
        // 执行支付
        paymentService.processPayment(paymentInfo);
        
        // 更新状态
        order.changeStatus(OrderStatus.PAID, currentVersion);
        orderRepository.save(order);
    }
}

五、工单状态机设计

1. 工单状态定义

java 复制代码
// 工单状态
public enum TicketStatus {
    CREATED("已创建"),
    ASSIGNED("已分配"),
    PROCESSING("处理中"),
    PENDING_APPROVAL("待审批"),
    RESOLVED("已解决"),
    CLOSED("已关闭"),
    REJECTED("已拒绝");
}

// 工单事件
public enum TicketEvent {
    ASSIGN("分配"),
    ACCEPT("接受"),
    REJECT("拒绝"),
    PROCESS("处理"),
    APPROVE("审批"),
    RESOLVE("解决"),
    CLOSE("关闭"),
    REOPEN("重新打开");
}

// 工单状态机配置
@Configuration
public class TicketStateMachineConfig {
    
    public static final Map<String, StateTransition> TRANSITIONS;
    
    static {
        TRANSITIONS = new HashMap<>();
        
        // 创建 -> 分配
        TRANSITIONS.put("CREATED:ASSIGN", 
            StateTransition.builder()
                .from(TicketStatus.CREATED)
                .event(TicketEvent.ASSIGN)
                .to(TicketStatus.ASSIGNED)
                .action("ticketService.assign")
                .build());
        
        // 分配 -> 处理中
        TRANSITIONS.put("ASSIGNED:ACCEPT",
            StateTransition.builder()
                .from(TicketStatus.ASSIGNED)
                .event(TicketEvent.ACCEPT)
                .to(TicketStatus.PROCESSING)
                .action("ticketService.startProcessing")
                .build());
        
        // 处理中 -> 待审批
        TRANSITIONS.put("PROCESSING:APPROVE",
            StateTransition.builder()
                .from(TicketStatus.PROCESSING)
                .event(TicketEvent.APPROVE)
                .to(TicketStatus.PENDING_APPROVAL)
                .action("ticketService.submitForApproval")
                .guard("requireApproval()")
                .build());
        
        // 处理中 -> 已解决
        TRANSITIONS.put("PROCESSING:RESOLVE",
            StateTransition.builder()
                .from(TicketStatus.PROCESSING)
                .event(TicketEvent.RESOLVE)
                .to(TicketStatus.RESOLVED)
                .action("ticketService.resolve")
                .build());
        
        // 待审批 -> 已解决
        TRANSITIONS.put("PENDING_APPROVAL:APPROVE",
            StateTransition.builder()
                .from(TicketStatus.PENDING_APPROVAL)
                .event(TicketEvent.APPROVE)
                .to(TicketStatus.RESOLVED)
                .action("ticketService.approve")
                .build());
        
        // 待审批 -> 处理中(驳回)
        TRANSITIONS.put("PENDING_APPROVAL:REJECT",
            StateTransition.builder()
                .from(TicketStatus.PENDING_APPROVAL)
                .event(TicketEvent.REJECT)
                .to(TicketStatus.PROCESSING)
                .action("ticketService.reject")
                .build());
        
        // 已解决 -> 已关闭
        TRANSITIONS.put("RESOLVED:CLOSE",
            StateTransition.builder()
                .from(TicketStatus.RESOLVED)
                .event(TicketEvent.CLOSE)
                .to(TicketStatus.CLOSED)
                .action("ticketService.close")
                .build());
        
        // 已关闭 -> 处理中(重新打开)
        TRANSITIONS.put("CLOSED:REOPEN",
            StateTransition.builder()
                .from(TicketStatus.CLOSED)
                .event(TicketEvent.REOPEN)
                .to(TicketStatus.PROCESSING)
                .action("ticketService.reopen")
                .build());
    }
}

2. 工单状态机服务

java 复制代码
@Service
public class TicketStateMachine {
    
    @Autowired
    private TicketRepository ticketRepository;
    
    @Autowired
    private TicketStatusLogRepository logRepository;
    
    public void fire(Ticket ticket, TicketEvent event) {
        String key = ticket.getStatus() + ":" + event;
        StateTransition transition = TicketStateMachineConfig.TRANSITIONS.get(key);
        
        if (transition == null) {
            throw new InvalidTransitionException(
                "工单状态不允许此操作: " + ticket.getStatus() + " + " + event);
        }
        
        // 检查守卫
        if (!checkGuard(ticket, transition.getGuard())) {
            throw new GuardViolationException("不满足操作条件: " + transition.getGuard());
        }
        
        // 记录原状态
        TicketStatus fromStatus = ticket.getStatus();
        
        // 执行动作
        executeAction(transition.getAction(), ticket);
        
        // 更新状态
        ticket.setStatus(transition.getTo());
        ticketRepository.save(ticket);
        
        // 记录日志
        saveStatusLog(ticket, fromStatus, transition.getTo(), event);
    }
    
    private void executeAction(String action, Ticket ticket) {
        if (action == null) return;
        
        switch (action) {
            case "ticketService.assign":
                ticket.setAssignedAt(LocalDateTime.now());
                break;
            case "ticketService.startProcessing":
                ticket.setProcessingStartedAt(LocalDateTime.now());
                break;
            case "ticketService.resolve":
                ticket.setResolvedAt(LocalDateTime.now());
                break;
        }
    }
    
    private void saveStatusLog(Ticket ticket, TicketStatus from, 
                               TicketStatus to, TicketEvent event) {
        TicketStatusLog log = TicketStatusLog.builder()
            .ticketId(ticket.getId())
            .fromStatus(from)
            .toStatus(to)
            .event(event)
            .operator(getCurrentOperator())
            .createdAt(LocalDateTime.now())
            .build();
        
        logRepository.save(log);
    }
    
    // 获取可用操作
    public List<TicketEvent> getAvailableEvents(Ticket ticket) {
        return TicketStateMachineConfig.TRANSITIONS.values().stream()
            .filter(t -> t.getFrom() == ticket.getStatus())
            .filter(t -> checkGuard(ticket, t.getGuard()))
            .map(StateTransition::getEvent)
            .collect(Collectors.toList());
    }
}

六、状态机可视化

1. 状态图生成

java 复制代码
// 生成Mermaid格式的状态图
@Service
public class StateDiagramGenerator {
    
    public String generateMermaidDiagram(Map<String, StateTransition> transitions) {
        StringBuilder diagram = new StringBuilder();
        diagram.append("stateDiagram-v2\n");
        
        for (StateTransition t : transitions.values()) {
            String line = "    " + t.getFrom() + " --> " + t.getTo() + 
                          " : " + t.getEvent();
            
            if (t.getGuard() != null) {
                line += " [" + t.getGuard() + "]";
            }
            
            diagram.append(line).append("\n");
        }
        
        return diagram.toString();
    }
    
    // 使用示例输出:
    // stateDiagram-v2
    //     CREATED --> ASSIGNED : ASSIGN
    //     ASSIGNED --> PROCESSING : ACCEPT
    //     PROCESSING --> PENDING_APPROVAL : APPROVE [requireApproval()]
    //     PROCESSING --> RESOLVED : RESOLVE
    //     PENDING_APPROVAL --> RESOLVED : APPROVE
    //     PENDING_APPROVAL --> PROCESSING : REJECT
    //     RESOLVED --> CLOSED : CLOSE
    //     CLOSED --> PROCESSING : REOPEN
}

2. 状态机监控

java 复制代码
// 状态转换统计
@Service
public class StateTransitionMetrics {
    
    @Autowired
    private TicketStatusLogRepository logRepository;
    
    public Map<String, Long> getTransitionStats(LocalDate start, LocalDate end) {
        List<TicketStatusLog> logs = logRepository
            .findByCreatedAtBetween(start.atStartOfDay(), end.plusDays(1).atStartOfDay());
        
        return logs.stream()
            .collect(Collectors.groupingBy(
                log -> log.getFromStatus() + "->" + log.getToStatus(),
                Collectors.counting()
            ));
    }
    
    // 统计平均处理时间
    public Map<TicketStatus, Double> getAvgTimeInStatus(LocalDate start, LocalDate end) {
        List<TicketStatusLog> logs = logRepository.findByCreatedAtBetween(
            start.atStartOfDay(), end.plusDays(1).atStartOfDay());
        
        Map<TicketStatus, List<Long>> durations = new HashMap<>();
        
        for (int i = 0; i < logs.size() - 1; i++) {
            TicketStatusLog current = logs.get(i);
            TicketStatusLog next = logs.get(i + 1);
            
            if (current.getTicketId().equals(next.getTicketId())) {
                long duration = java.time.Duration.between(
                    current.getCreatedAt(), next.getCreatedAt()).toMinutes();
                
                durations.computeIfAbsent(current.getToStatus(), k -> new ArrayList<>())
                    .add(duration);
            }
        }
        
        return durations.entrySet().stream()
            .collect(Collectors.toMap(
                Map.Entry::getKey,
                e -> e.getValue().stream()
                    .mapToLong(Long::longValue).average().orElse(0)
            ));
    }
}

七、状态机框架

1. Spring StateMachine

java 复制代码
// Spring StateMachine配置
@Configuration
@EnableStateMachine
public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderStatus, OrderEvent> {
    
    @Override
    public void configure(StateMachineStateConfigurer<OrderStatus, OrderEvent> states) throws Exception {
        states
            .withStates()
            .initial(OrderStatus.PENDING)
            .states(EnumSet.allOf(OrderStatus.class))
            .end(OrderStatus.COMPLETED)
            .end(OrderStatus.CANCELLED);
    }
    
    @Override
    public void configure(StateMachineTransitionConfigurer<OrderStatus, OrderEvent> transitions) throws Exception {
        transitions
            .withExternal()
                .source(OrderStatus.PENDING).target(OrderStatus.PAID)
                .event(OrderEvent.PAY)
                .action(paymentAction())
            .and()
            .withExternal()
                .source(OrderStatus.PAID).target(OrderStatus.SHIPPING)
                .event(OrderEvent.DELIVER)
                .action(deliveryAction())
            .and()
            .withExternal()
                .source(OrderStatus.SHIPPING).target(OrderStatus.COMPLETED)
                .event(OrderEvent.RECEIVE)
            .and()
            .withExternal()
                .source(OrderStatus.PENDING).target(OrderStatus.CANCELLED)
                .event(OrderEvent.CANCEL)
            .and()
            .withExternal()
                .source(OrderStatus.PAID).target(OrderStatus.CANCELLED)
                .event(OrderEvent.CANCEL)
                .action(refundAction());
    }
    
    @Bean
    public Action<OrderStatus, OrderEvent> paymentAction() {
        return context -> {
            System.out.println("执行支付逻辑");
        };
    }
    
    @Bean
    public Action<OrderStatus, OrderEvent> deliveryAction() {
        return context -> {
            System.out.println("执行发货逻辑");
        };
    }
    
    @Bean
    public Action<OrderStatus, OrderEvent> refundAction() {
        return context -> {
            System.out.println("执行退款逻辑");
        };
    }
}

// 使用状态机
@Service
public class OrderServiceWithStateMachine {
    
    @Autowired
    private StateMachine<OrderStatus, OrderEvent> stateMachine;
    
    public void payOrder(Long orderId) {
        stateMachine.start();
        
        // 设置订单ID到上下文
        stateMachine.getStateMachineAccessor()
            .doWithAllRegions(accessor -> {
                accessor.addStateMachineInterceptor(new StateMachineInterceptorAdapter<>() {
                    @Override
                    public void preStateChange(State<OrderStatus, OrderEvent> state) {
                        System.out.println("状态即将变更为: " + state.getId());
                    }
                });
            });
        
        // 发送支付事件
        boolean result = stateMachine.sendEvent(OrderEvent.PAY);
        
        System.out.println("支付结果: " + result);
        System.out.println("当前状态: " + stateMachine.getState().getId());
    }
}

八、总结

状态机是复杂业务逻辑的利器:

  • 状态流转规范:明确的状态转换规则
  • 可追溯性:完整的状态变更记录
  • 安全性:防止非法状态转换
  • 可视化:清晰的状态图

最佳实践:

  1. 清晰定义所有状态和事件
  2. 做好守卫条件检查
  3. 记录完整的状态变更日志
  4. 使用乐观锁防止并发问题

个人观点,仅供参考

相关推荐
小江的记录本2 小时前
【Kafka核心】架构模型:Producer、Broker、Consumer、Consumer Group、Topic、Partition、Replica
java·数据库·分布式·后端·搜索引擎·架构·kafka
一切皆是因缘际会3 小时前
AI数字分身的底层原理:破解意识、自我与人格复刻的核心难题
大数据·人工智能·ai·架构
jinanwuhuaguo5 小时前
(第二十七篇)OpenClaw四月的演化风暴:OpenClaw 2026年4月全版本更新的文明级解读
大数据·人工智能·架构·kotlin·openclaw
James_WangA5 小时前
我给 AOI 设备装了一个 Agent,然后发现工具注册才是最难写的
架构·github
James_WangA5 小时前
产线上跑 Agent:LLM 挂了不是 500 错误,是停线
架构·github
生成论实验室6 小时前
《事件关系阴阳博弈动力学:识势应势之道》第四篇:降U动力学——认知确定度的自驱演化
人工智能·科技·神经网络·算法·架构
SamDeepThinking6 小时前
并发量就算只有2,该上锁还得上呀
java·后端·架构
Sam_Deep_Thinking6 小时前
如何让订单系统和营销系统解耦
java·架构·系统架构
ting94520007 小时前
Micro1 超详细深度解析:架构原理、部署实战、性能评测与落地应用全指南
人工智能·架构