电商订单管理系统设计与实现
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);
}
}
# 总结
订单管理系统的关键设计要点:
- 状态管理:清晰的状态定义和流转规则,确保业务流程的正确性
- 数据一致性:通过事务、分布式锁等机制保证订单和库存的一致性
- 高并发处理:使用Redis预减库存,异步处理非核心业务
- 事件驱动:通过事件机制实现业务解耦,提高系统扩展性
- 性能优化:合理的索引设计、缓存策略和分页查询
- 异常处理:完善的异常处理和补偿机制,确保系统稳定性
通过以上设计,可以构建一个高性能、高可用的订单管理系统,支撑大规模电商业务的稳定运行。