企业级订单系统开发指南:Spring Statemachine核心应用与最佳实践(入门篇)

在这里插入图片描述

肖哥弹架构 跟大家“弹弹” Spring Statemachine设计与实战应用,需要代码关注

欢迎 点赞,关注,转发。

关注公号Solomon肖哥弹架构获取更多精彩内容

历史热点文章

详细解析订单从创建、支付到发货的完整状态流转机制,通过清晰的时序图和状态转换图展示核心业务流程。

  1. 全流程架构设计:从订单创建、支付到发货的完整状态流转机制,配有多张流程图解
  2. 深度技术整合:Spring Statemachine与Spring Boot、JPA的无缝集成方案
  3. 企业级数据库设计:包含5张核心业务表和2张状态机持久化表的详细定义,附SQL脚本

一、 项目启动说明

  1. 环境要求:
    • JDK 17+
    • MySQL 8.0+
    • Maven 3.8+
  2. 数据库初始化:
    • 执行提供的SQL脚本创建数据库和表
  3. 配置修改:
    • 修改application.yml中的数据库连接信息
    • 配置其他必要参数

二、系统架构图

在这里插入图片描述

三、核心流程说明

1. 创建订单流程

客户端 -> OrderController.createOrder() 
-> OrderService.createOrder() 
-> OrderRepository.save() 
-> 返回订单ID

在这里插入图片描述

2. 订单支付流程

客户端 -> OrderController.payOrder() 
-> OrderService.payOrder() 
-> StateMachine.sendEvent(PAY)
-> PaymentGuard检查
-> OrderPayAction执行
-> OrderStateMachineListener记录
-> OrderRepository更新状态

在这里插入图片描述

3. 订单发货流程

客户端 -> OrderController.shipOrder() 
-> OrderService.shipOrder() 
-> StateMachine.sendEvent(SHIP)
-> ShippingGuard检查
-> OrderShipAction执行
-> OrderStateMachineListener记录
-> OrderShippingRepository保存物流信息
-> OrderRepository更新状态

在这里插入图片描述

四、项目目录结构图

order-statemachine-system/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/
│   │   │       └── example/
│   │   │           └── ordersystem/
│   │   │               ├── config/                # 配置类
│   │   │               │   ├── StateMachineConfig.java
│   │   │               │   └── WebConfig.java
│   │   │               ├── controller/            # 控制器层
│   │   │               │   ├── OrderController.java
│   │   │               │   └── ApiExceptionHandler.java
│   │   │               ├── dto/                   # 数据传输对象
│   │   │               │   ├── request/
│   │   │               │   │   ├── CreateOrderRequest.java
│   │   │               │   │   ├── PaymentRequest.java
│   │   │               │   │   └── ShippingRequest.java
│   │   │               │   ├── response/
│   │   │               │   │   ├── OrderDetailResponse.java
│   │   │               │   │   └── OrderSimpleResponse.java
│   │   │               │   └── enums/             # 枚举
│   │   │               │       ├── OrderEvent.java
│   │   │               │       └── OrderState.java
│   │   │               ├── exception/             # 异常处理
│   │   │               │   ├── BusinessException.java
│   │   │               │   └── GlobalExceptionHandler.java
│   │   │               ├── model/                 # 数据模型
│   │   │               │   ├── entity/
│   │   │               │   │   ├── Order.java
│   │   │               │   │   ├── OrderPayment.java
│   │   │               │   │   └── OrderShipping.java
│   │   │               │   └── vo/
│   │   │               │       └── OrderStateLogVO.java
│   │   │               ├── repository/            # 数据访问层
│   │   │               │   ├── OrderRepository.java
│   │   │               │   └── OrderStateHistoryRepository.java
│   │   │               ├── service/               # 服务层
│   │   │               │   ├── impl/
│   │   │               │   │   └── OrderServiceImpl.java
│   │   │               │   ├── OrderService.java
│   │   │               │   └── statemachine/      # 状态机相关
│   │   │               │       ├── action/
│   │   │               │       │   ├── OrderPayAction.java
│   │   │               │       │   └── OrderShipAction.java
│   │   │               │       ├── guard/
│   │   │               │       │   ├── PaymentGuard.java
│   │   │               │       │   └── ShippingGuard.java
│   │   │               │       ├── listener/
│   │   │               │       │   └── OrderStateMachineListener.java
│   │   │               │       └── OrderStateMachineBuilder.java
│   │   │               └── OrderSystemApplication.java # 启动类
│   │   └── resources/
│   │       ├── application.yml                   # 应用配置
│   │       ├── static/                          # 静态资源
│   │       └── templates/                       # 模板文件
│   └── test/                                   # 测试代码
│       └── java/
│           └── com/
│               └── example/
│                   └── ordersystem/
│                       ├── OrderServiceTest.java
│                       └── StateMachineTest.java
├── pom.xml                                      # Maven配置
└── README.md                                   # 项目说明

1.目录结构说明

1.1. 配置层 (config)
  • StateMachineConfig.java: 状态机核心配置,定义状态、事件和转换规则
  • WebConfig.java: Web相关配置(拦截器、消息转换器等)
1.2. 控制器层 (controller)
  • OrderController.java: 订单相关REST接口
  • ApiExceptionHandler.java: API异常统一处理
1.3. DTO层 (dto)
  • request/ : 入参DTO
    • CreateOrderRequest.java: 创建订单请求
    • PaymentRequest.java: 支付请求
    • ShippingRequest.java: 发货请求
  • response/ : 出参DTO
    • OrderDetailResponse.java: 订单详情响应
    • OrderSimpleResponse.java: 订单简略信息响应
  • enums/ : 枚举定义
    • OrderEvent.java: 订单事件枚举
    • OrderState.java: 订单状态枚举
1.4. 异常处理 (exception)
  • BusinessException.java: 业务异常基类
  • GlobalExceptionHandler.java: 全局异常处理器
1.5. 模型层 (model)
  • entity/ : 数据库实体
    • Order.java: 订单主实体
    • OrderPayment.java: 支付信息实体
    • OrderShipping.java: 物流信息实体
  • vo/ : 视图对象
    • OrderStateLogVO.java: 状态变更日志视图对象
1.6. 数据访问层 (repository)
  • OrderRepository.java: 订单数据访问接口
  • OrderStateHistoryRepository.java: 状态历史数据访问接口
1.7. 服务层 (service)
  • impl/OrderServiceImpl.java: 订单服务实现
  • OrderService.java: 订单服务接口
  • statemachine/ : 状态机相关实现
    • action/ : 状态转换动作
      • OrderPayAction.java: 支付动作
      • OrderShipAction.java: 发货动作
    • guard/ : 转换守卫条件
      • PaymentGuard.java: 支付守卫条件
      • ShippingGuard.java: 发货守卫条件
    • listener/ : 状态机监听器
      • OrderStateMachineListener.java: 状态变更监听器
    • OrderStateMachineBuilder.java: 状态机构建器
1.8. 资源文件 (resources)
  • application.yml: 应用配置文件
  • static/ : 静态资源文件
  • templates/ : 模板文件

五、项目核心代码

1. 完整的领域模型

Order.java (订单实体)
import lombok.Data;
import javax.persistence.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * 订单实体类
 * 注解说明:
 * @Entity - 标记为JPA实体
 * @Table - 指定数据库表名
 * @Enumerated - 枚举类型存储为字符串
 */
@Entity
@Table(name = "t_order")
@Data
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(unique = true, nullable = false)
    private String orderNo;
    
    @Enumerated(EnumType.STRING)
    @Column(nullable = false, length = 20)
    private OrderState state;
    
    @Column(nullable = false, precision = 10, scale = 2)
    private BigDecimal amount;
    
    @Column(nullable = false)
    private Long userId;
    
    @Column(nullable = false, updatable = false)
    private LocalDateTime createTime;
    
    @Column
    private LocalDateTime updateTime;
    
    @Version
    private Integer version; // 乐观锁版本号
    
    /**
     * 订单预处理 - 在持久化前执行
     */
    @PrePersist
    protected void onCreate() {
        this.createTime = LocalDateTime.now();
        this.state = OrderState.UNPAID; // 确保新建订单状态正确
    }
    
    /**
     * 更新前处理
     */
    @PreUpdate
    protected void onUpdate() {
        this.updateTime = LocalDateTime.now();
    }
}

2. 完整的状态机配置

OrderStateMachineConfig.java
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.config.EnableStateMachineFactory;
import org.springframework.statemachine.config.StateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.*;

/**
 * 订单状态机配置
 * 配置说明:
 * 1. 使用@EnableStateMachineFactory创建状态机工厂
 * 2. 定义所有订单状态和初始状态
 * 3. 配置状态转换规则
 */
@Configuration
@EnableStateMachineFactory(name = "orderStateMachineFactory")
public class OrderStateMachineConfig 
    extends StateMachineConfigurerAdapter<OrderState, OrderEvent> {

    @Override
    public void configure(StateMachineStateConfigurer<OrderState, OrderEvent> states) 
        throws Exception {
        
        states.withStates()
            .initial(OrderState.UNPAID)
            .state(OrderState.PAID, context -> {
                // 进入PAID状态时的处理
                Order order = context.getExtendedState().get("order", Order.class);
                System.out.println("订单[" + order.getOrderNo() + "]已支付");
            })
            .state(OrderState.SHIPPED, context -> {
                // 发货后的处理
                Order order = context.getExtendedState().get("order", Order.class);
                System.out.println("订单[" + order.getOrderNo() + "]已发货");
            })
            .state(OrderState.COMPLETED, context -> {
                // 订单完成处理
                Order order = context.getExtendedState().get("order", Order.class);
                System.out.println("订单[" + order.getOrderNo() + "]已完成");
            })
            .state(OrderState.CANCELLED, context -> {
                // 订单取消处理
                Order order = context.getExtendedState().get("order", Order.class);
                System.out.println("订单[" + order.getOrderNo() + "]已取消");
            })
            .state(OrderState.RETURNING, context -> {
                // 退货处理
                Order order = context.getExtendedState().get("order", Order.class);
                System.out.println("订单[" + order.getOrderNo() + "]申请退货");
            });
    }

    @Override
    public void configure(StateMachineTransitionConfigurer<OrderState, OrderEvent> transitions) 
        throws Exception {
        
        transitions
            // 支付转换:UNPAID -> PAID
            .withExternal()
                .source(OrderState.UNPAID)
                .target(OrderState.PAID)
                .event(OrderEvent.PAY)
                .action(payAction()) // 支付动作
                .and()
            
            // 发货转换:PAID -> SHIPPED
            .withExternal()
                .source(OrderState.PAID)
                .target(OrderState.SHIPPED)
                .event(OrderEvent.SHIP)
                .guard(this::shipGuard) // 发货前检查
                .and()
            
            // 确认收货:SHIPPED -> COMPLETED
            .withExternal()
                .source(OrderState.SHIPPED)
                .target(OrderState.COMPLETED)
                .event(OrderEvent.RECEIVE)
                .and()
            
            // 取消订单:UNPAID -> CANCELLED
            .withExternal()
                .source(OrderState.UNPAID)
                .target(OrderState.CANCELLED)
                .event(OrderEvent.CANCEL)
                .action(cancelAction()) // 取消动作
                .and()
            
            // 申请退货:SHIPPED -> RETURNING
            .withExternal()
                .source(OrderState.SHIPPED)
                .target(OrderState.RETURNING)
                .event(OrderEvent.RETURN)
                .guard(this::returnGuard); // 退货条件检查
    }

    /**
     * 支付动作处理
     */
    private Action<OrderState, OrderEvent> payAction() {
        return context -> {
            Order order = context.getExtendedState().get("order", Order.class);
            // 实际业务中这里会调用支付网关
            System.out.println("处理支付逻辑,订单金额:" + order.getAmount());
            
            // 记录支付时间
            context.getExtendedState().getVariables().put("payTime", LocalDateTime.now());
        };
    }

    /**
     * 取消订单处理
     */
    private Action<OrderState, OrderEvent> cancelAction() {
        return context -> {
            Order order = context.getExtendedState().get("order", Order.class);
            System.out.println("订单取消原因:" + 
                context.getExtendedState().get("cancelReason", String.class));
        };
    }

    /**
     * 发货前检查守卫
     */
    private boolean shipGuard(StateContext<OrderState, OrderEvent> context) {
        Order order = context.getExtendedState().get("order", Order.class);
        // 检查是否有库存等业务规则
        boolean canShip = checkInventory(order);
        if (!canShip) {
            System.out.println("库存不足,无法发货");
        }
        return canShip;
    }

    /**
     * 退货条件检查守卫
     */
    private boolean returnGuard(StateContext<OrderState, OrderEvent> context) {
        Order order = context.getExtendedState().get("order", Order.class);
        LocalDateTime payTime = (LocalDateTime) context.getExtendedState()
            .getVariables().get("payTime");
        
        // 检查是否在退货期内(示例:支付后7天内可退货)
        if (payTime.plusDays(7).isBefore(LocalDateTime.now())) {
            System.out.println("已超过退货期限");
            return false;
        }
        return true;
    }

    // 模拟库存检查
    private boolean checkInventory(Order order) {
        return true; // 实际业务中会查询库存系统
    }
}

3. 完整的订单服务实现

OrderServiceImpl.java
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.config.StateMachineFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 订单服务实现
 * 关键点:
 * 1. 使用@Transactional确保状态变更和订单保存的原子性
 * 2. 处理状态机的启动和停止
 * 3. 业务校验前置
 */
@Service
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {
    
    private final OrderRepository orderRepository;
    private final StateMachineFactory<OrderState, OrderEvent> stateMachineFactory;
    private final OrderStateListener orderStateListener;

    @Override
    @Transactional
    public Order createOrder(CreateOrderCommand command) {
        Order order = new Order();
        order.setOrderNo(generateOrderNo());
        order.setAmount(command.getAmount());
        order.setUserId(command.getUserId());
        return orderRepository.save(order);
    }

    @Override
    @Transactional
    public void payOrder(Long orderId, PaymentInfo paymentInfo) {
        Order order = getOrder(orderId);
        checkState(order, OrderState.UNPAID, "支付");
        
        StateMachine<OrderState, OrderEvent> stateMachine = buildStateMachine(order);
        
        // 将订单和支付信息放入扩展状态
        stateMachine.getExtendedState()
            .getVariables()
            .put("order", order);
        stateMachine.getExtendedState()
            .getVariables()
            .put("paymentInfo", paymentInfo);
        
        // 发送支付事件
        boolean accepted = stateMachine.sendEvent(OrderEvent.PAY);
        
        if (!accepted) {
            throw new BusinessException("订单当前状态不能支付");
        }
        
        // 保存状态变更
        order.setState(stateMachine.getState().getId());
        orderRepository.save(order);
    }

    @Override
    @Transactional
    public void shipOrder(Long orderId, ShippingInfo shippingInfo) {
        Order order = getOrder(orderId);
        checkState(order, OrderState.PAID, "发货");
        
        StateMachine<OrderState, OrderEvent> stateMachine = buildStateMachine(order);
        stateMachine.getExtendedState()
            .getVariables()
            .put("order", order);
        stateMachine.getExtendedState()
            .getVariables()
            .put("shippingInfo", shippingInfo);
        
        boolean accepted = stateMachine.sendEvent(OrderEvent.SHIP);
        
        if (!accepted) {
            throw new BusinessException("订单当前状态不能发货");
        }
        
        order.setState(stateMachine.getState().getId());
        orderRepository.save(order);
    }

    @Override
    @Transactional
    public void cancelOrder(Long orderId, String cancelReason) {
        Order order = getOrder(orderId);
        checkState(order, OrderState.UNPAID, "取消");
        
        StateMachine<OrderState, OrderEvent> stateMachine = buildStateMachine(order);
        stateMachine.getExtendedState()
            .getVariables()
            .put("order", order);
        stateMachine.getExtendedState()
            .getVariables()
            .put("cancelReason", cancelReason);
        
        boolean accepted = stateMachine.sendEvent(OrderEvent.CANCEL);
        
        if (!accepted) {
            throw new BusinessException("订单当前状态不能取消");
        }
        
        order.setState(stateMachine.getState().getId());
        orderRepository.save(order);
    }

    @Override
    public OrderState getOrderState(Long orderId) {
        return getOrder(orderId).getState();
    }

    /**
     * 构建状态机并启动
     */
    private StateMachine<OrderState, OrderEvent> buildStateMachine(Order order) {
        StateMachine<OrderState, OrderEvent> stateMachine = 
            stateMachineFactory.getStateMachine(order.getId().toString());
        
        stateMachine.addStateListener(orderStateListener);
        stateMachine.start();
        
        // 恢复状态机到订单当前状态
        stateMachine.getStateMachineAccessor()
            .doWithAllRegions(access -> 
                access.resetStateMachine(new DefaultStateMachineContext<>(
                    order.getState(), null, null, null)));
        
        return stateMachine;
    }

    /**
     * 检查订单状态是否允许操作
     */
    private void checkState(Order order, OrderState expectedState, String operation) {
        if (order.getState() != expectedState) {
            throw new BusinessException(
                String.format("订单[%s]当前状态为%s,不能执行%s操作", 
                    order.getOrderNo(), order.getState(), operation));
        }
    }

    /**
     * 获取订单并检查是否存在
     */
    private Order getOrder(Long orderId) {
        return orderRepository.findById(orderId)
            .orElseThrow(() -> new NotFoundException("订单不存在"));
    }

    private String generateOrderNo() {
        return "ORD" + System.currentTimeMillis() + 
            ThreadLocalRandom.current().nextInt(1000, 9999);
    }
}

4. 完整的异常处理

BusinessException.java
/**
 * 业务异常
 * 用于处理业务规则违反情况
 */
public class BusinessException extends RuntimeException {
    public BusinessException(String message) {
        super(message);
    }
    
    public BusinessException(String message, Throwable cause) {
        super(message, cause);
    }
}

/**
 * 资源未找到异常
 */
public class NotFoundException extends BusinessException {
    public NotFoundException(String message) {
        super(message);
    }
}

5. 完整的DTO定义

OrderDTO.java
/**
 * 订单DTO - 用于接口传输
 */
@Data
public class OrderDTO {
    private Long id;
    private String orderNo;
    private String state;
    private BigDecimal amount;
    private String createTime;
    private String updateTime;
    
    public static OrderDTO fromEntity(Order order) {
        OrderDTO dto = new OrderDTO();
        BeanUtils.copyProperties(order, dto);
        dto.setState(order.getState().name());
        dto.setCreateTime(DateUtils.format(order.getCreateTime()));
        if (order.getUpdateTime() != null) {
            dto.setUpdateTime(DateUtils.format(order.getUpdateTime()));
        }
        return dto;
    }
}

/**
 * 创建订单命令
 */
@Data
public class CreateOrderCommand {
    @NotNull
    private Long userId;
    
    @DecimalMin(value = "0.01")
    private BigDecimal amount;
    
    // 其他创建订单需要的参数...
}

/**
 * 支付信息
 */
@Data
public class PaymentInfo {
    @NotBlank
    private String paymentMethod; // 支付方式: WECHAT/ALIPAY/UNIONPAY
    
    @NotBlank
    private String transactionId; // 支付流水号
    
    private LocalDateTime paymentTime;
}

/**
 * 发货信息
 */
@Data
public class ShippingInfo {
    @NotBlank
    private String shippingCompany; // 物流公司
    
    @NotBlank
    private String trackingNumber; // 运单号
    
    private LocalDateTime shippingTime;
}

6. 完整的REST控制器

OrderController.java
/**
 * 订单REST接口
 * 关键点:
 * 1. 使用@Valid进行参数校验
 * 2. 统一异常处理
 * 3. 清晰的API文档
 */
@RestController
@RequestMapping("/api/orders")
@RequiredArgsConstructor
public class OrderController {
    
    private final OrderService orderService;

    @PostMapping
    public ResponseEntity<OrderDTO> createOrder(
            @Valid @RequestBody CreateOrderCommand command) {
        Order order = orderService.createOrder(command);
        return ResponseEntity.created(URI.create("/orders/" + order.getId()))
            .body(OrderDTO.fromEntity(order));
    }

    @PostMapping("/{orderId}/pay")
    public ResponseEntity<Void> payOrder(
            @PathVariable Long orderId,
            @Valid @RequestBody PaymentInfo paymentInfo) {
        orderService.payOrder(orderId, paymentInfo);
        return ResponseEntity.ok().build();
    }

    @PostMapping("/{orderId}/ship")
    public ResponseEntity<Void> shipOrder(
            @PathVariable Long orderId,
            @Valid @RequestBody ShippingInfo shippingInfo) {
        orderService.shipOrder(orderId, shippingInfo);
        return ResponseEntity.ok().build();
    }

    @PostMapping("/{orderId}/cancel")
    public ResponseEntity<Void> cancelOrder(
            @PathVariable Long orderId,
            @RequestParam String cancelReason) {
        orderService.cancelOrder(orderId, cancelReason);
        return ResponseEntity.ok().build();
    }

    @GetMapping("/{orderId}")
    public ResponseEntity<OrderDTO> getOrder(@PathVariable Long orderId) {
        Order order = orderService.getOrder(orderId);
        return ResponseEntity.ok(OrderDTO.fromEntity(order));
    }

    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex) {
        return ResponseEntity.badRequest()
            .body(new ErrorResponse(ex.getMessage()));
    }

    @Data
    @AllArgsConstructor
    static class ErrorResponse {
        private String message;
    }
}

7. 完整的测试用例

OrderServiceTest.java
@SpringBootTest
@Transactional
class OrderServiceTest {

    @Autowired
    private OrderService orderService;
    
    @Autowired
    private OrderRepository orderRepository;

    @Test
    void testCreateOrder() {
        CreateOrderCommand command = new CreateOrderCommand();
        command.setUserId(1L);
        command.setAmount(new BigDecimal("100.00"));
        
        Order order = orderService.createOrder(command);
        
        assertNotNull(order.getId());
        assertEquals(OrderState.UNPAID, order.getState());
        assertEquals(new BigDecimal("100.00"), order.getAmount());
    }

    @Test
    void testPayOrder() {
        Order order = createTestOrder();
        
        PaymentInfo paymentInfo = new PaymentInfo();
        paymentInfo.setPaymentMethod("ALIPAY");
        paymentInfo.setTransactionId("TX123456");
        
        orderService.payOrder(order.getId(), paymentInfo);
        
        Order updated = orderRepository.findById(order.getId()).get();
        assertEquals(OrderState.PAID, updated.getState());
    }

    @Test
    void testPayOrderWrongState() {
        Order order = createTestOrder();
        order.setState(OrderState.PAID);
        orderRepository.save(order);
        
        PaymentInfo paymentInfo = new PaymentInfo();
        paymentInfo.setPaymentMethod("ALIPAY");
        paymentInfo.setTransactionId("TX123456");
        
        assertThrows(BusinessException.class, () -> {
            orderService.payOrder(order.getId(), paymentInfo);
        });
    }

    @Test
    void testShipOrder() {
        Order order = createTestOrder();
        order.setState(OrderState.PAID);
        orderRepository.save(order);
        
        ShippingInfo shippingInfo = new ShippingInfo();
        shippingInfo.setShippingCompany("SF");
        shippingInfo.setTrackingNumber("SF123456");
        
        orderService.shipOrder(order.getId(), shippingInfo);
        
        Order updated = orderRepository.findById(order.getId()).get();
        assertEquals(OrderState.SHIPPED, updated.getState());
    }

    private Order createTestOrder() {
        Order order = new Order();
        order.setOrderNo("TEST" + System.currentTimeMillis());
        order.setState(OrderState.UNPAID);
        order.setAmount(new BigDecimal("100.00"));
        order.setUserId(1L);
        return orderRepository.save(order);
    }
}

8. application.yml 配置

# 应用基础配置
spring:
  application:
    name: order-statemachine-system
  datasource:
    url: jdbc:mysql://localhost:3306/order_db?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8
    username: root
    password: root123
    driver-class-name: com.mysql.cj.jdbc.Driver
    hikari:
      pool-name: OrderHikariCP
      minimum-idle: 5
      maximum-pool-size: 20
      idle-timeout: 30000
      max-lifetime: 1800000
      connection-timeout: 30000
      connection-test-query: SELECT 1

  jpa:
    hibernate:
      ddl-auto: update # 生产环境建议改为validate
    show-sql: true
    properties:
      hibernate:
        format_sql: true
        dialect: org.hibernate.dialect.MySQL8Dialect
        jdbc:
          lob:
            non_contextual_creation: true

  # 状态机配置
  statemachine:
    enums:
      states: com.example.ordersystem.dto.enums.OrderState
      events: com.example.ordersystem.dto.enums.OrderEvent
    persist:
      enabled: true
      type: jpa # 使用JPA持久化状态机
    factory-bean: orderStateMachineFactory # 指定状态机工厂Bean

# MyBatis配置(如果使用)
mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.example.ordersystem.model.entity
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

# 应用自定义配置
app:
  order:
    state-machine:
      auto-start: true # 是否自动启动状态机
      persist-interval: 5000 # 状态机持久化间隔(ms)
    payment:
      timeout: 1800 # 支付超时时间(秒)
    shipping:
      companies: SF,STO,YTO,ZTO # 支持的物流公司

# 日志配置
logging:
  level:
    root: info
    org.springframework.statemachine: debug
    com.example.ordersystem: debug
  file:
    name: logs/order-system.log
    max-size: 10MB
    max-history: 7

# 服务端口
server:
  port: 8080
  servlet:
    context-path: /api

六、数据库设计

1. 核心业务表设计

1.1 订单主表 (t_order
-- 订单主表:存储订单核心信息
CREATE TABLE `t_order` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '订单ID,自增主键',
  `order_no` varchar(32) NOT NULL COMMENT '订单编号,业务唯一标识',
  `user_id` bigint(20) NOT NULL COMMENT '下单用户ID',
  `amount` decimal(10,2) NOT NULL COMMENT '订单总金额(元)',
  `state` varchar(20) NOT NULL COMMENT '订单状态:UNPAID/PAID/SHIPPED/COMPLETED/CANCELLED/RETURNING',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '订单创建时间',
  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '最后更新时间',
  `version` int(11) NOT NULL DEFAULT '0' COMMENT '乐观锁版本号,用于并发控制',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_order_no` (`order_no`) COMMENT '订单编号唯一索引',
  KEY `idx_user_id` (`user_id`) COMMENT '用户ID普通索引',
  KEY `idx_state` (`state`) COMMENT '状态查询索引',
  KEY `idx_create_time` (`create_time`) COMMENT '创建时间索引'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单主表';
1.2 订单状态变更历史表 (t_order_state_history)
-- 订单状态变更历史表:记录所有状态变更轨迹
CREATE TABLE `t_order_state_history` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `order_id` bigint(20) NOT NULL COMMENT '关联的订单ID',
  `from_state` varchar(20) DEFAULT NULL COMMENT '变更前状态',
  `to_state` varchar(20) NOT NULL COMMENT '变更后状态',
  `event` varchar(20) NOT NULL COMMENT '触发事件:PAY/SHIP/RECEIVE/CANCEL/RETURN',
  `operator` varchar(64) DEFAULT NULL COMMENT '操作人:系统自动/SYSTEM,用户ID,管理员ID',
  `operation_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间',
  `remark` varchar(255) DEFAULT NULL COMMENT '变更备注说明',
  PRIMARY KEY (`id`),
  KEY `idx_order_id` (`order_id`) COMMENT '订单ID索引',
  KEY `idx_operation_time` (`operation_time`) COMMENT '操作时间索引',
  CONSTRAINT `fk_history_order` FOREIGN KEY (`order_id`) 
    REFERENCES `t_order` (`id`) ON DELETE CASCADE COMMENT '外键关联订单主表'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单状态变更历史表';
1.3 支付信息表 (t_order_payment)
-- 订单支付信息表:记录支付相关数据
CREATE TABLE `t_order_payment` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '支付记录ID',
  `order_id` bigint(20) NOT NULL COMMENT '关联的订单ID',
  `payment_method` varchar(20) NOT NULL COMMENT '支付方式:WECHAT/ALIPAY/UNIONPAY/OTHER',
  `transaction_id` varchar(64) NOT NULL COMMENT '第三方支付流水号',
  `payment_amount` decimal(10,2) NOT NULL COMMENT '实际支付金额(可能含优惠)',
  `payment_time` datetime NOT NULL COMMENT '支付成功时间',
  `payment_status` varchar(20) NOT NULL COMMENT '支付状态:SUCCESS/FAIL/REFUND/PART_REFUND',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_transaction_id` (`transaction_id`) COMMENT '支付流水号唯一索引',
  KEY `idx_order_id` (`order_id`) COMMENT '订单ID索引',
  CONSTRAINT `fk_payment_order` FOREIGN KEY (`order_id`) 
    REFERENCES `t_order` (`id`) ON DELETE CASCADE COMMENT '外键关联订单主表'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单支付信息表';
1.4 物流信息表 (t_order_shipping)
-- 订单物流信息表:记录发货和物流信息
CREATE TABLE `t_order_shipping` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '物流记录ID',
  `order_id` bigint(20) NOT NULL COMMENT '关联的订单ID',
  `shipping_company` varchar(50) NOT NULL COMMENT '物流公司编码:SF,STO,YTO等',
  `tracking_number` varchar(50) NOT NULL COMMENT '物流运单号',
  `shipping_time` datetime NOT NULL COMMENT '实际发货时间',
  `receiver_name` varchar(50) NOT NULL COMMENT '收货人姓名',
  `receiver_phone` varchar(20) NOT NULL COMMENT '收货人手机号',
  `receiver_address` varchar(255) NOT NULL COMMENT '详细收货地址',
  PRIMARY KEY (`id`),
  KEY `idx_order_id` (`order_id`) COMMENT '订单ID索引',
  KEY `idx_tracking_number` (`tracking_number`) COMMENT '运单号索引',
  CONSTRAINT `fk_shipping_order` FOREIGN KEY (`order_id`) 
    REFERENCES `t_order` (`id`) ON DELETE CASCADE COMMENT '外键关联订单主表'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单物流信息表';
1.5 退货信息表 (t_order_return)
-- 订单退货信息表:记录退货/退款信息
CREATE TABLE `t_order_return` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '退货记录ID',
  `order_id` bigint(20) NOT NULL COMMENT '关联的订单ID',
  `return_reason` varchar(255) NOT NULL COMMENT '退货原因描述',
  `return_amount` decimal(10,2) DEFAULT NULL COMMENT '实际退款金额',
  `return_status` varchar(20) NOT NULL COMMENT '退货状态:PROCESSING/COMPLETED/REJECTED',
  `apply_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '退货申请时间',
  `complete_time` datetime DEFAULT NULL COMMENT '退货完成时间',
  `operator` varchar(64) DEFAULT NULL COMMENT '处理人:系统/客服ID',
  PRIMARY KEY (`id`),
  KEY `idx_order_id` (`order_id`) COMMENT '订单ID索引',
  CONSTRAINT `fk_return_order` FOREIGN KEY (`order_id`) 
    REFERENCES `t_order` (`id`) ON DELETE CASCADE COMMENT '外键关联订单主表'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单退货信息表';

2. 状态机持久化表设计

2.1 状态机实例表 (sm_machine_instance)
-- 状态机实例表:维护状态机运行时实例
CREATE TABLE `sm_machine_instance` (
  `machine_id` varchar(36) NOT NULL COMMENT '状态机实例UUID',
  `machine_type` varchar(50) NOT NULL COMMENT '状态机类型:ORDER/PAYMENT等',
  `state` varchar(50) NOT NULL COMMENT '当前状态值',
  `business_key` varchar(36) NOT NULL COMMENT '关联业务ID(如订单ID)',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '实例创建时间',
  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '最后状态更新时间',
  PRIMARY KEY (`machine_id`),
  UNIQUE KEY `uk_business` (`machine_type`, `business_key`) COMMENT '业务唯一性约束',
  KEY `idx_state` (`state`) COMMENT '状态查询索引'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='状态机实例表';
2.2 状态机上下文表 (sm_machine_context)
-- 状态机上下文表:存储状态机上下文数据
CREATE TABLE `sm_machine_context` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `machine_id` varchar(36) NOT NULL COMMENT '关联的状态机实例ID',
  `context_data` blob COMMENT '序列化的上下文数据(JSON/Protobuf格式)',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`),
  KEY `idx_machine_id` (`machine_id`) COMMENT '状态机实例ID索引',
  CONSTRAINT `fk_context_machine` FOREIGN KEY (`machine_id`) 
    REFERENCES `sm_machine_instance` (`machine_id`) ON DELETE CASCADE COMMENT '外键关联实例表'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='状态机上下文表';

3. 数据库初始化脚本

-- 初始化状态机类型配置
INSERT INTO `sm_machine_type` (`type`, `description`) VALUES 
('ORDER', '订单状态机'),
('PAYMENT', '支付状态机');

-- 初始化订单状态枚举
INSERT INTO `sys_enum` (`enum_type`, `enum_key`, `enum_value`, `sort_order`) VALUES
('ORDER_STATE', 'UNPAID', '待支付', 1),
('ORDER_STATE', 'PAID', '已支付', 2),
('ORDER_STATE', 'SHIPPED', '已发货', 3),
('ORDER_STATE', 'COMPLETED', '已完成', 4),
('ORDER_STATE', 'CANCELLED', '已取消', 5),
('ORDER_STATE', 'RETURNING', '退货中', 6);

-- 初始化订单事件枚举
INSERT INTO `sys_enum` (`enum_type`, `enum_key`, `enum_value`, `sort_order`) VALUES
('ORDER_EVENT', 'PAY', '支付', 1),
('ORDER_EVENT', 'SHIP', '发货', 2),
('ORDER_EVENT', 'RECEIVE', '确认收货', 3),
('ORDER_EVENT', 'CANCEL', '取消订单', 4),
('ORDER_EVENT', 'RETURN', '申请退货', 5);

4. 性能优化脚本

4.1 索引优化
-- 为订单表添加组合索引
ALTER TABLE `t_order` 
  ADD INDEX `idx_user_state` (`user_id`, `state`) COMMENT '用户+状态联合查询优化',
  ADD INDEX `idx_user_create_time` (`user_id`, `create_time`) COMMENT '用户+时间范围查询优化';

-- 为状态历史表添加查询优化索引
ALTER TABLE `t_order_state_history`
  ADD INDEX `idx_order_event` (`order_id`, `event`) COMMENT '订单+事件查询优化',
  ADD INDEX `idx_order_time` (`order_id`, `operation_time`) COMMENT '订单+时间范围查询优化';
4.2 分区方案(海量数据)
-- 订单表按月分区(RANGE分区)
ALTER TABLE `t_order` PARTITION BY RANGE (TO_DAYS(create_time)) (
    PARTITION p202301 VALUES LESS THAN (TO_DAYS('2023-02-01')),
    PARTITION p202302 VALUES LESS THAN (TO_DAYS('2023-03-01')),
    PARTITION p202303 VALUES LESS THAN (TO_DAYS('2023-04-01')),
    PARTITION pmax VALUES LESS THAN MAXVALUE
);

-- 状态历史表按订单ID哈希分区
ALTER TABLE `t_order_state_history` PARTITION BY HASH(order_id) PARTITIONS 8;

5. 数据库维护脚本

5.1 状态一致性检查
-- 检查订单表与状态机实例表的状态一致性
SELECT 
    o.id AS order_id,
    o.order_no,
    o.state AS order_state,
    m.state AS machine_state,
    CASE WHEN o.state = m.state THEN '一致' ELSE '不一致' END AS status
FROM t_order o
LEFT JOIN sm_machine_instance m ON o.id = m.business_key AND m.machine_type = 'ORDER'
WHERE o.state != m.state OR m.machine_id IS NULL;
5.2 状态历史修复
-- 修复缺失的初始状态记录
INSERT INTO t_order_state_history (
    order_id, from_state, to_state, event, operator, operation_time
)
SELECT 
    o.id, 
    NULL, 
    o.state, 
    'CREATE', 
    'SYSTEM', 
    o.create_time
FROM t_order o
WHERE NOT EXISTS (
    SELECT 1 FROM t_order_state_history h 
    WHERE h.order_id = o.id AND h.event = 'CREATE'
);

6. 数据库关系图说明

+----------------+       +-------------------+       +------------------+
|    t_order     |       | t_order_payment   |       | t_order_shipping |
+----------------+       +-------------------+       +------------------+
| PK id          |<---+  | PK id             |   +-->| PK id            |
|   order_no     |    |  | FK order_id       |   |   | FK order_id      |
|   user_id      |    |  |   payment_method  |   |   |   shipping_company
|   amount       |    |  |   transaction_id  |   |   |   tracking_number
|   state        |    |  |   payment_amount  |   |   |   shipping_time  |
|   create_time  |    |  |   payment_time    |   |   +------------------+
|   update_time  |    |  |   payment_status  |   |
|   version      |    |  +-------------------+   |
+----------------+    |                         |
                      |                         |
+---------------------+    +-------------------+
| t_order_state_history |  | t_order_return    |
+---------------------+    +-------------------+
| PK id               |    | PK id             |
| FK order_id         |    | FK order_id       |
|   from_state        |    |   return_reason   |
|   to_state          |    |   return_amount   |
|   event             |    |   return_status   |
|   operator          |    |   apply_time      |
|   operation_time    |    |   complete_time   |
|   remark            |    |   operator        |
+---------------------+    +-------------------+
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Solomon_肖哥弹架构

你的欣赏就是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值