电商订单管理系统设计与实现

2024/1/1

# 电商订单管理系统设计与实现

# 系统概述

订单管理系统是电商平台的核心业务模块,负责处理用户下单、订单状态流转、库存扣减、支付对接、物流跟踪等全流程业务。一个完善的订单系统需要保证数据一致性、支持高并发、具备良好的扩展性。

# 订单状态流转

# 订单状态定义

public enum OrderStatus {
    PENDING_PAYMENT("待支付"),
    PAID("已支付"),
    CONFIRMED("已确认"),
    SHIPPED("已发货"),
    DELIVERED("已送达"),
    COMPLETED("已完成"),
    CANCELLED("已取消"),
    REFUNDING("退款中"),
    REFUNDED("已退款");
    
    private final String description;
    
    OrderStatus(String description) {
        this.description = description;
    }
}

# 状态流转图

待支付 → 已支付 → 已确认 → 已发货 → 已送达 → 已完成
  ↓        ↓        ↓        ↓        ↓
已取消   已取消   退款中   退款中   退款中
                   ↓        ↓        ↓
                 已退款   已退款   已退款

# 核心实体设计

# 1. 订单主表

@Entity
@Table(name = "orders")
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(unique = true, nullable = false, length = 32)
    private String orderNumber;
    
    @Column(nullable = false)
    private Long userId;
    
    @Enumerated(EnumType.STRING)
    @Column(nullable = false)
    private OrderStatus status;
    
    @Column(nullable = false, precision = 10, scale = 2)
    private BigDecimal totalAmount;
    
    @Column(nullable = false, precision = 10, scale = 2)
    private BigDecimal payAmount;
    
    @Column(precision = 10, scale = 2)
    private BigDecimal discountAmount = BigDecimal.ZERO;
    
    @Column(precision = 10, scale = 2)
    private BigDecimal shippingFee = BigDecimal.ZERO;
    
    @Enumerated(EnumType.STRING)
    private PaymentMethod paymentMethod;
    
    @Column(length = 64)
    private String paymentTransactionId;
    
    @Embedded
    private ShippingAddress shippingAddress;
    
    @Column(length = 500)
    private String remark;
    
    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<OrderItem> orderItems;
    
    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<OrderStatusHistory> statusHistory;
    
    @CreationTimestamp
    private LocalDateTime createdAt;
    
    @UpdateTimestamp
    private LocalDateTime updatedAt;
    
    private LocalDateTime paidAt;
    private LocalDateTime shippedAt;
    private LocalDateTime deliveredAt;
    private LocalDateTime completedAt;
}

# 2. 订单明细表

@Entity
@Table(name = "order_items")
public class OrderItem {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "order_id")
    private Order order;
    
    @Column(nullable = false)
    private Long productId;
    
    @Column(nullable = false, length = 200)
    private String productName;
    
    @Column(length = 500)
    private String productImage;
    
    @Column(length = 200)
    private String productSpec;
    
    @Column(nullable = false, precision = 10, scale = 2)
    private BigDecimal unitPrice;
    
    @Column(nullable = false)
    private Integer quantity;
    
    @Column(nullable = false, precision = 10, scale = 2)
    private BigDecimal totalPrice;
}

# 3. 收货地址

@Embeddable
public class ShippingAddress {
    @Column(length = 50)
    private String receiverName;
    
    @Column(length = 20)
    private String receiverPhone;
    
    @Column(length = 50)
    private String province;
    
    @Column(length = 50)
    private String city;
    
    @Column(length = 50)
    private String district;
    
    @Column(length = 200)
    private String detailAddress;
    
    @Column(length = 10)
    private String zipCode;
}

# 订单服务实现

# 1. 订单创建服务

@Service
@Transactional
public class OrderService {
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private ProductService productService;
    
    @Autowired
    private InventoryService inventoryService;
    
    @Autowired
    private CartService cartService;
    
    @Autowired
    private CouponService couponService;
    
    @Autowired
    private OrderNumberGenerator orderNumberGenerator;
    
    /**
     * 创建订单
     */
    public Order createOrder(OrderCreateRequest request) {
        // 1. 参数验证
        validateOrderRequest(request);
        
        // 2. 获取购物车商品信息
        List<CartItem> cartItems = cartService.getCartItems(request.getUserId(), request.getCartItemIds());
        if (cartItems.isEmpty()) {
            throw new BusinessException("购物车商品不能为空");
        }
        
        // 3. 验证商品库存
        validateProductStock(cartItems);
        
        // 4. 计算订单金额
        OrderAmountCalculation calculation = calculateOrderAmount(cartItems, request.getCouponId());
        
        // 5. 预减库存
        boolean stockReduced = preReduceStock(cartItems);
        if (!stockReduced) {
            throw new BusinessException("库存不足,下单失败");
        }
        
        try {
            // 6. 创建订单
            Order order = buildOrder(request, cartItems, calculation);
            order = orderRepository.save(order);
            
            // 7. 创建订单明细
            List<OrderItem> orderItems = buildOrderItems(order, cartItems);
            order.setOrderItems(orderItems);
            
            // 8. 记录状态变更历史
            recordStatusHistory(order, OrderStatus.PENDING_PAYMENT, "订单创建");
            
            // 9. 清空购物车
            cartService.removeCartItems(request.getUserId(), request.getCartItemIds());
            
            // 10. 发送订单创建事件
            publishOrderCreatedEvent(order);
            
            return order;
            
        } catch (Exception e) {
            // 回滚库存
            rollbackStock(cartItems);
            throw e;
        }
    }
    
    /**
     * 计算订单金额
     */
    private OrderAmountCalculation calculateOrderAmount(List<CartItem> cartItems, Long couponId) {
        BigDecimal totalAmount = cartItems.stream()
            .map(item -> item.getUnitPrice().multiply(BigDecimal.valueOf(item.getQuantity())))
            .reduce(BigDecimal.ZERO, BigDecimal::add);
        
        BigDecimal discountAmount = BigDecimal.ZERO;
        if (couponId != null) {
            discountAmount = couponService.calculateDiscount(couponId, totalAmount);
        }
        
        BigDecimal shippingFee = calculateShippingFee(totalAmount);
        BigDecimal payAmount = totalAmount.subtract(discountAmount).add(shippingFee);
        
        return new OrderAmountCalculation(totalAmount, discountAmount, shippingFee, payAmount);
    }
    
    /**
     * 预减库存
     */
    private boolean preReduceStock(List<CartItem> cartItems) {
        for (CartItem item : cartItems) {
            boolean success = inventoryService.preReduceStock(item.getProductId(), item.getQuantity());
            if (!success) {
                // 回滚已减库存
                rollbackPartialStock(cartItems, item);
                return false;
            }
        }
        return true;
    }
    
    /**
     * 构建订单对象
     */
    private Order buildOrder(OrderCreateRequest request, List<CartItem> cartItems, OrderAmountCalculation calculation) {
        Order order = new Order();
        order.setOrderNumber(orderNumberGenerator.generate());
        order.setUserId(request.getUserId());
        order.setStatus(OrderStatus.PENDING_PAYMENT);
        order.setTotalAmount(calculation.getTotalAmount());
        order.setDiscountAmount(calculation.getDiscountAmount());
        order.setShippingFee(calculation.getShippingFee());
        order.setPayAmount(calculation.getPayAmount());
        order.setShippingAddress(request.getShippingAddress());
        order.setRemark(request.getRemark());
        
        return order;
    }
}

# 2. 订单状态管理

@Service
public class OrderStatusService {
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private OrderStatusHistoryRepository statusHistoryRepository;
    
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    
    /**
     * 更新订单状态
     */
    @Transactional
    public void updateOrderStatus(Long orderId, OrderStatus newStatus, String remark) {
        Order order = orderRepository.findById(orderId)
            .orElseThrow(() -> new BusinessException("订单不存在"));
        
        // 验证状态流转是否合法
        validateStatusTransition(order.getStatus(), newStatus);
        
        OrderStatus oldStatus = order.getStatus();
        order.setStatus(newStatus);
        
        // 更新特定状态的时间戳
        updateStatusTimestamp(order, newStatus);
        
        orderRepository.save(order);
        
        // 记录状态变更历史
        recordStatusHistory(order, newStatus, remark);
        
        // 发布状态变更事件
        publishStatusChangeEvent(order, oldStatus, newStatus);
    }
    
    /**
     * 验证状态流转
     */
    private void validateStatusTransition(OrderStatus currentStatus, OrderStatus newStatus) {
        Map<OrderStatus, Set<OrderStatus>> allowedTransitions = Map.of(
            OrderStatus.PENDING_PAYMENT, Set.of(OrderStatus.PAID, OrderStatus.CANCELLED),
            OrderStatus.PAID, Set.of(OrderStatus.CONFIRMED, OrderStatus.CANCELLED, OrderStatus.REFUNDING),
            OrderStatus.CONFIRMED, Set.of(OrderStatus.SHIPPED, OrderStatus.REFUNDING),
            OrderStatus.SHIPPED, Set.of(OrderStatus.DELIVERED, OrderStatus.REFUNDING),
            OrderStatus.DELIVERED, Set.of(OrderStatus.COMPLETED, OrderStatus.REFUNDING),
            OrderStatus.REFUNDING, Set.of(OrderStatus.REFUNDED),
            OrderStatus.COMPLETED, Set.of(OrderStatus.REFUNDING)
        );
        
        Set<OrderStatus> allowed = allowedTransitions.get(currentStatus);
        if (allowed == null || !allowed.contains(newStatus)) {
            throw new BusinessException(String.format("订单状态不能从%s变更为%s", 
                currentStatus.getDescription(), newStatus.getDescription()));
        }
    }
    
    /**
     * 更新状态时间戳
     */
    private void updateStatusTimestamp(Order order, OrderStatus status) {
        LocalDateTime now = LocalDateTime.now();
        switch (status) {
            case PAID:
                order.setPaidAt(now);
                break;
            case SHIPPED:
                order.setShippedAt(now);
                break;
            case DELIVERED:
                order.setDeliveredAt(now);
                break;
            case COMPLETED:
                order.setCompletedAt(now);
                break;
        }
    }
}

# 3. 订单支付处理

@Service
public class OrderPaymentService {
    
    @Autowired
    private OrderService orderService;
    
    @Autowired
    private PaymentService paymentService;
    
    @Autowired
    private InventoryService inventoryService;
    
    /**
     * 处理支付成功回调
     */
    @Transactional
    public void handlePaymentSuccess(PaymentNotification notification) {
        String orderNumber = notification.getOrderNumber();
        Order order = orderService.findByOrderNumber(orderNumber);
        
        if (order == null) {
            log.warn("支付回调:订单不存在,订单号:{}", orderNumber);
            return;
        }
        
        if (order.getStatus() != OrderStatus.PENDING_PAYMENT) {
            log.warn("支付回调:订单状态异常,订单号:{},当前状态:{}", orderNumber, order.getStatus());
            return;
        }
        
        // 验证支付金额
        if (!order.getPayAmount().equals(notification.getAmount())) {
            log.error("支付回调:金额不匹配,订单号:{},订单金额:{},支付金额:{}", 
                orderNumber, order.getPayAmount(), notification.getAmount());
            return;
        }
        
        // 更新订单状态
        order.setStatus(OrderStatus.PAID);
        order.setPaidAt(LocalDateTime.now());
        order.setPaymentTransactionId(notification.getTransactionId());
        orderService.save(order);
        
        // 确认扣减库存
        confirmStockReduction(order);
        
        // 发送支付成功事件
        publishPaymentSuccessEvent(order);
    }
    
    /**
     * 处理支付失败
     */
    @Transactional
    public void handlePaymentFailure(String orderNumber, String reason) {
        Order order = orderService.findByOrderNumber(orderNumber);
        if (order != null && order.getStatus() == OrderStatus.PENDING_PAYMENT) {
            // 取消订单
            orderService.cancelOrder(order.getId(), "支付失败:" + reason);
            
            // 恢复库存
            restoreStock(order);
        }
    }
    
    /**
     * 确认库存扣减
     */
    private void confirmStockReduction(Order order) {
        for (OrderItem item : order.getOrderItems()) {
            inventoryService.confirmStockReduction(item.getProductId(), item.getQuantity());
        }
    }
    
    /**
     * 恢复库存
     */
    private void restoreStock(Order order) {
        for (OrderItem item : order.getOrderItems()) {
            inventoryService.restoreStock(item.getProductId(), item.getQuantity());
        }
    }
}

# 订单查询优化

# 1. 分页查询

@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
    
    @Query("SELECT o FROM Order o WHERE o.userId = :userId ORDER BY o.createdAt DESC")
    Page<Order> findByUserIdOrderByCreatedAtDesc(@Param("userId") Long userId, Pageable pageable);
    
    @Query("SELECT o FROM Order o WHERE o.status = :status AND o.createdAt >= :startTime ORDER BY o.createdAt DESC")
    Page<Order> findByStatusAndCreatedAtAfter(@Param("status") OrderStatus status, 
                                             @Param("startTime") LocalDateTime startTime, 
                                             Pageable pageable);
    
    @Query(value = "SELECT * FROM orders WHERE user_id = :userId AND status IN :statuses ORDER BY created_at DESC", 
           nativeQuery = true)
    Page<Order> findByUserIdAndStatusIn(@Param("userId") Long userId, 
                                       @Param("statuses") List<String> statuses, 
                                       Pageable pageable);
}

# 2. 订单统计

@Service
public class OrderStatisticsService {
    
    @Autowired
    private OrderRepository orderRepository;
    
    /**
     * 获取订单统计信息
     */
    public OrderStatistics getOrderStatistics(Long userId, LocalDate startDate, LocalDate endDate) {
        LocalDateTime startTime = startDate.atStartOfDay();
        LocalDateTime endTime = endDate.atTime(23, 59, 59);
        
        // 总订单数
        Long totalOrders = orderRepository.countByUserIdAndCreatedAtBetween(userId, startTime, endTime);
        
        // 已完成订单数
        Long completedOrders = orderRepository.countByUserIdAndStatusAndCreatedAtBetween(
            userId, OrderStatus.COMPLETED, startTime, endTime);
        
        // 总消费金额
        BigDecimal totalAmount = orderRepository.sumPayAmountByUserIdAndStatusAndCreatedAtBetween(
            userId, OrderStatus.COMPLETED, startTime, endTime);
        
        // 平均订单金额
        BigDecimal avgAmount = completedOrders > 0 ? 
            totalAmount.divide(BigDecimal.valueOf(completedOrders), 2, RoundingMode.HALF_UP) : 
            BigDecimal.ZERO;
        
        return new OrderStatistics(totalOrders, completedOrders, totalAmount, avgAmount);
    }
}

# 订单事件处理

# 1. 事件定义

public class OrderCreatedEvent extends ApplicationEvent {
    private final Order order;
    
    public OrderCreatedEvent(Object source, Order order) {
        super(source);
        this.order = order;
    }
    
    public Order getOrder() {
        return order;
    }
}

public class OrderStatusChangedEvent extends ApplicationEvent {
    private final Order order;
    private final OrderStatus oldStatus;
    private final OrderStatus newStatus;
    
    public OrderStatusChangedEvent(Object source, Order order, OrderStatus oldStatus, OrderStatus newStatus) {
        super(source);
        this.order = order;
        this.oldStatus = oldStatus;
        this.newStatus = newStatus;
    }
}

# 2. 事件监听器

@Component
public class OrderEventListener {
    
    @Autowired
    private NotificationService notificationService;
    
    @Autowired
    private LogisticsService logisticsService;
    
    @Autowired
    private PointsService pointsService;
    
    /**
     * 订单创建事件处理
     */
    @EventListener
    @Async
    public void handleOrderCreated(OrderCreatedEvent event) {
        Order order = event.getOrder();
        
        // 发送订单创建通知
        notificationService.sendOrderCreatedNotification(order);
        
        // 记录用户行为日志
        logUserBehavior(order.getUserId(), "ORDER_CREATED", order.getId());
    }
    
    /**
     * 订单状态变更事件处理
     */
    @EventListener
    @Async
    public void handleOrderStatusChanged(OrderStatusChangedEvent event) {
        Order order = event.getOrder();
        OrderStatus newStatus = event.getNewStatus();
        
        switch (newStatus) {
            case PAID:
                handleOrderPaid(order);
                break;
            case SHIPPED:
                handleOrderShipped(order);
                break;
            case COMPLETED:
                handleOrderCompleted(order);
                break;
            case CANCELLED:
                handleOrderCancelled(order);
                break;
        }
    }
    
    private void handleOrderPaid(Order order) {
        // 发送支付成功通知
        notificationService.sendPaymentSuccessNotification(order);
        
        // 自动确认订单(如果是虚拟商品)
        if (isVirtualProduct(order)) {
            orderService.confirmOrder(order.getId());
        }
    }
    
    private void handleOrderCompleted(Order order) {
        // 发放积分
        pointsService.awardPoints(order.getUserId(), order.getPayAmount());
        
        // 发送完成通知
        notificationService.sendOrderCompletedNotification(order);
    }
}

# 性能优化

# 1. 数据库优化

-- 订单表索引
CREATE INDEX idx_order_user_status ON orders(user_id, status);
CREATE INDEX idx_order_user_created ON orders(user_id, created_at DESC);
CREATE INDEX idx_order_status_created ON orders(status, created_at);
CREATE INDEX idx_order_number ON orders(order_number);
CREATE INDEX idx_order_payment_transaction ON orders(payment_transaction_id);

-- 订单明细表索引
CREATE INDEX idx_order_item_order_id ON order_items(order_id);
CREATE INDEX idx_order_item_product_id ON order_items(product_id);

# 2. 缓存策略

@Service
public class OrderCacheService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    private static final String ORDER_CACHE_PREFIX = "order:";
    private static final String USER_ORDER_LIST_PREFIX = "user:orders:";
    
    /**
     * 缓存订单信息
     */
    public void cacheOrder(Order order) {
        String key = ORDER_CACHE_PREFIX + order.getId();
        redisTemplate.opsForValue().set(key, order, 30, TimeUnit.MINUTES);
    }
    
    /**
     * 缓存用户订单列表
     */
    public void cacheUserOrderList(Long userId, List<Order> orders, int page) {
        String key = USER_ORDER_LIST_PREFIX + userId + ":" + page;
        redisTemplate.opsForValue().set(key, orders, 10, TimeUnit.MINUTES);
    }
}

# 总结

订单管理系统的关键设计要点:

  1. 状态管理:清晰的状态定义和流转规则,确保业务流程的正确性
  2. 数据一致性:通过事务、分布式锁等机制保证订单和库存的一致性
  3. 高并发处理:使用Redis预减库存,异步处理非核心业务
  4. 事件驱动:通过事件机制实现业务解耦,提高系统扩展性
  5. 性能优化:合理的索引设计、缓存策略和分页查询
  6. 异常处理:完善的异常处理和补偿机制,确保系统稳定性

通过以上设计,可以构建一个高性能、高可用的订单管理系统,支撑大规模电商业务的稳定运行。