电商购物车系统设计与实现

2024/1/1

# 电商购物车系统设计与实现

# 系统概述

购物车系统是电商平台的重要组成部分,为用户提供商品临时存储、数量管理、价格计算等功能。一个优秀的购物车系统需要支持高并发访问、数据持久化、实时同步等特性,同时要考虑用户体验和系统性能的平衡。

# 购物车架构设计

# 存储方案对比

存储方案 优点 缺点 适用场景
Cookie存储 无需登录、减轻服务器压力 容量限制、安全性差 简单场景
Session存储 服务器控制、相对安全 占用内存、不支持分布式 单机应用
数据库存储 数据持久化、支持复杂查询 性能较低、并发能力有限 数据重要性高
Redis存储 高性能、支持分布式 数据可能丢失 高并发场景
混合存储 兼顾性能和可靠性 实现复杂 生产环境推荐

# 推荐架构

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   Web前端       │    │   移动端APP     │    │   小程序        │
└─────────────────┘    └─────────────────┘    └─────────────────┘
         │                       │                       │
         └───────────────────────┼───────────────────────┘
                                 │
         ┌─────────────────────────────────────────────────┐
         │              购物车网关层                        │
         │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐ │
         │  │  用户认证   │  │  参数验证   │  │  限流控制   │ │
         │  └─────────────┘  └─────────────┘  └─────────────┘ │
         └─────────────────────────────────────────────────┘
                                 │
         ┌─────────────────────────────────────────────────┐
         │              购物车服务层                        │
         │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐ │
         │  │  购物车管理  │  │  商品验证   │  │  价格计算   │ │
         │  └─────────────┘  └─────────────┘  └─────────────┘ │
         └─────────────────────────────────────────────────┘
                                 │
         ┌─────────────────────────────────────────────────┐
         │              数据存储层                          │
         │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐ │
         │  │  Redis缓存   │  │  MySQL数据库 │  │  商品服务   │ │
         │  └─────────────┘  └─────────────┘  └─────────────┘ │
         └─────────────────────────────────────────────────┘

# 核心实体设计

# 1. 购物车实体

@Entity
@Table(name = "shopping_carts")
public class ShoppingCart {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false)
    private Long userId;
    
    @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)
    private Boolean selected = true;
    
    @CreationTimestamp
    private LocalDateTime createdAt;
    
    @UpdateTimestamp
    private LocalDateTime updatedAt;
    
    // 计算总价
    public BigDecimal getTotalPrice() {
        return unitPrice.multiply(BigDecimal.valueOf(quantity));
    }
}

# 2. 购物车DTO

@Data
public class CartItemDTO {
    private Long id;
    private Long productId;
    private String productName;
    private String productImage;
    private String productSpec;
    private BigDecimal unitPrice;
    private Integer quantity;
    private Boolean selected;
    private BigDecimal totalPrice;
    private Boolean available; // 商品是否可用
    private Integer stock; // 库存数量
    private LocalDateTime addTime;
}

@Data
public class CartSummaryDTO {
    private List<CartItemDTO> items;
    private Integer totalItems; // 总商品种类数
    private Integer totalQuantity; // 总商品数量
    private BigDecimal totalAmount; // 总金额
    private BigDecimal selectedAmount; // 选中商品总金额
    private Integer selectedCount; // 选中商品数量
}

# 购物车服务实现

# 1. 核心服务类

@Service
public class ShoppingCartService {
    
    @Autowired
    private ShoppingCartRepository cartRepository;
    
    @Autowired
    private ProductService productService;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private CartCacheService cartCacheService;
    
    private static final String CART_KEY_PREFIX = "cart:user:";
    
    /**
     * 添加商品到购物车
     */
    @Transactional
    public void addToCart(Long userId, Long productId, Integer quantity, String spec) {
        // 1. 验证商品信息
        Product product = productService.findById(productId);
        if (product == null || product.getStatus() != ProductStatus.ACTIVE) {
            throw new BusinessException("商品不存在或已下架");
        }
        
        // 2. 检查库存
        if (product.getStock() < quantity) {
            throw new BusinessException("库存不足");
        }
        
        // 3. 查找是否已存在相同商品
        ShoppingCart existingItem = cartRepository.findByUserIdAndProductIdAndProductSpec(
            userId, productId, spec);
        
        if (existingItem != null) {
            // 更新数量
            int newQuantity = existingItem.getQuantity() + quantity;
            if (newQuantity > product.getStock()) {
                throw new BusinessException("购物车中商品数量超过库存");
            }
            existingItem.setQuantity(newQuantity);
            cartRepository.save(existingItem);
        } else {
            // 新增购物车项
            ShoppingCart cartItem = new ShoppingCart();
            cartItem.setUserId(userId);
            cartItem.setProductId(productId);
            cartItem.setProductName(product.getName());
            cartItem.setProductImage(product.getMainImage());
            cartItem.setProductSpec(spec);
            cartItem.setUnitPrice(product.getPrice());
            cartItem.setQuantity(quantity);
            cartRepository.save(cartItem);
        }
        
        // 4. 更新缓存
        cartCacheService.refreshUserCart(userId);
        
        // 5. 发布事件
        publishCartChangedEvent(userId, CartEventType.ADD_ITEM);
    }
    
    /**
     * 更新购物车商品数量
     */
    @Transactional
    public void updateQuantity(Long userId, Long cartItemId, Integer quantity) {
        ShoppingCart cartItem = cartRepository.findByIdAndUserId(cartItemId, userId);
        if (cartItem == null) {
            throw new BusinessException("购物车商品不存在");
        }
        
        if (quantity <= 0) {
            // 删除商品
            removeFromCart(userId, cartItemId);
            return;
        }
        
        // 验证库存
        Product product = productService.findById(cartItem.getProductId());
        if (product.getStock() < quantity) {
            throw new BusinessException("库存不足");
        }
        
        cartItem.setQuantity(quantity);
        cartItem.setUnitPrice(product.getPrice()); // 更新最新价格
        cartRepository.save(cartItem);
        
        // 更新缓存
        cartCacheService.refreshUserCart(userId);
        
        publishCartChangedEvent(userId, CartEventType.UPDATE_QUANTITY);
    }
    
    /**
     * 从购物车移除商品
     */
    @Transactional
    public void removeFromCart(Long userId, Long cartItemId) {
        ShoppingCart cartItem = cartRepository.findByIdAndUserId(cartItemId, userId);
        if (cartItem != null) {
            cartRepository.delete(cartItem);
            cartCacheService.refreshUserCart(userId);
            publishCartChangedEvent(userId, CartEventType.REMOVE_ITEM);
        }
    }
    
    /**
     * 批量删除购物车商品
     */
    @Transactional
    public void batchRemove(Long userId, List<Long> cartItemIds) {
        List<ShoppingCart> cartItems = cartRepository.findByIdInAndUserId(cartItemIds, userId);
        if (!cartItems.isEmpty()) {
            cartRepository.deleteAll(cartItems);
            cartCacheService.refreshUserCart(userId);
            publishCartChangedEvent(userId, CartEventType.BATCH_REMOVE);
        }
    }
    
    /**
     * 选择/取消选择商品
     */
    @Transactional
    public void selectItems(Long userId, List<Long> cartItemIds, Boolean selected) {
        List<ShoppingCart> cartItems = cartRepository.findByIdInAndUserId(cartItemIds, userId);
        cartItems.forEach(item -> item.setSelected(selected));
        cartRepository.saveAll(cartItems);
        
        cartCacheService.refreshUserCart(userId);
        publishCartChangedEvent(userId, CartEventType.SELECT_CHANGE);
    }
    
    /**
     * 全选/全不选
     */
    @Transactional
    public void selectAll(Long userId, Boolean selected) {
        List<ShoppingCart> cartItems = cartRepository.findByUserId(userId);
        cartItems.forEach(item -> item.setSelected(selected));
        cartRepository.saveAll(cartItems);
        
        cartCacheService.refreshUserCart(userId);
        publishCartChangedEvent(userId, CartEventType.SELECT_ALL);
    }
    
    /**
     * 获取用户购物车
     */
    public CartSummaryDTO getUserCart(Long userId) {
        // 先从缓存获取
        CartSummaryDTO cachedCart = cartCacheService.getUserCart(userId);
        if (cachedCart != null) {
            return cachedCart;
        }
        
        // 从数据库获取
        List<ShoppingCart> cartItems = cartRepository.findByUserIdOrderByCreatedAtDesc(userId);
        CartSummaryDTO cartSummary = buildCartSummary(cartItems);
        
        // 更新缓存
        cartCacheService.cacheUserCart(userId, cartSummary);
        
        return cartSummary;
    }
    
    /**
     * 构建购物车摘要
     */
    private CartSummaryDTO buildCartSummary(List<ShoppingCart> cartItems) {
        List<CartItemDTO> itemDTOs = new ArrayList<>();
        int totalQuantity = 0;
        int selectedCount = 0;
        BigDecimal totalAmount = BigDecimal.ZERO;
        BigDecimal selectedAmount = BigDecimal.ZERO;
        
        for (ShoppingCart item : cartItems) {
            CartItemDTO dto = convertToDTO(item);
            
            // 验证商品可用性
            Product product = productService.findById(item.getProductId());
            dto.setAvailable(product != null && product.getStatus() == ProductStatus.ACTIVE);
            dto.setStock(product != null ? product.getStock() : 0);
            
            // 更新价格(如果商品价格有变化)
            if (product != null && !item.getUnitPrice().equals(product.getPrice())) {
                dto.setUnitPrice(product.getPrice());
                dto.setTotalPrice(product.getPrice().multiply(BigDecimal.valueOf(item.getQuantity())));
            }
            
            itemDTOs.add(dto);
            
            totalQuantity += item.getQuantity();
            totalAmount = totalAmount.add(dto.getTotalPrice());
            
            if (item.getSelected() && dto.getAvailable()) {
                selectedCount += item.getQuantity();
                selectedAmount = selectedAmount.add(dto.getTotalPrice());
            }
        }
        
        CartSummaryDTO summary = new CartSummaryDTO();
        summary.setItems(itemDTOs);
        summary.setTotalItems(cartItems.size());
        summary.setTotalQuantity(totalQuantity);
        summary.setTotalAmount(totalAmount);
        summary.setSelectedCount(selectedCount);
        summary.setSelectedAmount(selectedAmount);
        
        return summary;
    }
}

# 2. 缓存服务

@Service
public class CartCacheService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private ShoppingCartRepository cartRepository;
    
    private static final String CART_KEY_PREFIX = "cart:user:";
    private static final int CACHE_EXPIRE_SECONDS = 3600; // 1小时
    
    /**
     * 缓存用户购物车
     */
    public void cacheUserCart(Long userId, CartSummaryDTO cartSummary) {
        String key = CART_KEY_PREFIX + userId;
        redisTemplate.opsForValue().set(key, cartSummary, CACHE_EXPIRE_SECONDS, TimeUnit.SECONDS);
    }
    
    /**
     * 获取缓存的购物车
     */
    public CartSummaryDTO getUserCart(Long userId) {
        String key = CART_KEY_PREFIX + userId;
        return (CartSummaryDTO) redisTemplate.opsForValue().get(key);
    }
    
    /**
     * 刷新用户购物车缓存
     */
    public void refreshUserCart(Long userId) {
        String key = CART_KEY_PREFIX + userId;
        redisTemplate.delete(key);
    }
    
    /**
     * 预热购物车缓存
     */
    @Async
    public void preloadUserCart(Long userId) {
        CartSummaryDTO cachedCart = getUserCart(userId);
        if (cachedCart == null) {
            List<ShoppingCart> cartItems = cartRepository.findByUserIdOrderByCreatedAtDesc(userId);
            // 构建购物车摘要并缓存
            // ... 实现逻辑
        }
    }
}

# 3. 购物车同步服务

@Service
public class CartSyncService {
    
    @Autowired
    private ShoppingCartService cartService;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    /**
     * 合并游客购物车到用户购物车
     */
    @Transactional
    public void mergeGuestCart(String guestCartId, Long userId) {
        // 获取游客购物车
        List<CartItemDTO> guestItems = getGuestCartItems(guestCartId);
        
        if (guestItems.isEmpty()) {
            return;
        }
        
        // 获取用户购物车
        CartSummaryDTO userCart = cartService.getUserCart(userId);
        Map<String, CartItemDTO> userItemMap = userCart.getItems().stream()
            .collect(Collectors.toMap(
                item -> item.getProductId() + "_" + item.getProductSpec(),
                Function.identity()
            ));
        
        // 合并购物车
        for (CartItemDTO guestItem : guestItems) {
            String key = guestItem.getProductId() + "_" + guestItem.getProductSpec();
            CartItemDTO userItem = userItemMap.get(key);
            
            if (userItem != null) {
                // 商品已存在,更新数量
                int newQuantity = userItem.getQuantity() + guestItem.getQuantity();
                cartService.updateQuantity(userId, userItem.getId(), newQuantity);
            } else {
                // 新商品,添加到购物车
                cartService.addToCart(userId, guestItem.getProductId(), 
                    guestItem.getQuantity(), guestItem.getProductSpec());
            }
        }
        
        // 清除游客购物车
        clearGuestCart(guestCartId);
    }
    
    /**
     * 获取游客购物车商品
     */
    private List<CartItemDTO> getGuestCartItems(String guestCartId) {
        String key = "guest:cart:" + guestCartId;
        Object cartData = redisTemplate.opsForValue().get(key);
        
        if (cartData instanceof List) {
            return (List<CartItemDTO>) cartData;
        }
        
        return Collections.emptyList();
    }
    
    /**
     * 清除游客购物车
     */
    private void clearGuestCart(String guestCartId) {
        String key = "guest:cart:" + guestCartId;
        redisTemplate.delete(key);
    }
}

# 购物车API接口

# 1. 控制器实现

@RestController
@RequestMapping("/api/cart")
public class ShoppingCartController {
    
    @Autowired
    private ShoppingCartService cartService;
    
    @Autowired
    private CartSyncService cartSyncService;
    
    /**
     * 添加商品到购物车
     */
    @PostMapping("/add")
    public ApiResponse<Void> addToCart(@RequestBody @Valid AddToCartRequest request, 
                                      HttpServletRequest httpRequest) {
        Long userId = getCurrentUserId(httpRequest);
        cartService.addToCart(userId, request.getProductId(), 
            request.getQuantity(), request.getSpec());
        return ApiResponse.success();
    }
    
    /**
     * 获取购物车
     */
    @GetMapping
    public ApiResponse<CartSummaryDTO> getCart(HttpServletRequest request) {
        Long userId = getCurrentUserId(request);
        CartSummaryDTO cart = cartService.getUserCart(userId);
        return ApiResponse.success(cart);
    }
    
    /**
     * 更新商品数量
     */
    @PutMapping("/quantity")
    public ApiResponse<Void> updateQuantity(@RequestBody @Valid UpdateQuantityRequest request,
                                           HttpServletRequest httpRequest) {
        Long userId = getCurrentUserId(httpRequest);
        cartService.updateQuantity(userId, request.getCartItemId(), request.getQuantity());
        return ApiResponse.success();
    }
    
    /**
     * 删除购物车商品
     */
    @DeleteMapping("/items/{itemId}")
    public ApiResponse<Void> removeItem(@PathVariable Long itemId, 
                                       HttpServletRequest request) {
        Long userId = getCurrentUserId(request);
        cartService.removeFromCart(userId, itemId);
        return ApiResponse.success();
    }
    
    /**
     * 批量删除
     */
    @DeleteMapping("/items/batch")
    public ApiResponse<Void> batchRemove(@RequestBody @Valid BatchRemoveRequest request,
                                        HttpServletRequest httpRequest) {
        Long userId = getCurrentUserId(httpRequest);
        cartService.batchRemove(userId, request.getItemIds());
        return ApiResponse.success();
    }
    
    /**
     * 选择商品
     */
    @PutMapping("/select")
    public ApiResponse<Void> selectItems(@RequestBody @Valid SelectItemsRequest request,
                                        HttpServletRequest httpRequest) {
        Long userId = getCurrentUserId(httpRequest);
        cartService.selectItems(userId, request.getItemIds(), request.getSelected());
        return ApiResponse.success();
    }
    
    /**
     * 全选/全不选
     */
    @PutMapping("/select/all")
    public ApiResponse<Void> selectAll(@RequestParam Boolean selected,
                                      HttpServletRequest request) {
        Long userId = getCurrentUserId(request);
        cartService.selectAll(userId, selected);
        return ApiResponse.success();
    }
    
    /**
     * 合并游客购物车
     */
    @PostMapping("/merge")
    public ApiResponse<Void> mergeGuestCart(@RequestParam String guestCartId,
                                           HttpServletRequest request) {
        Long userId = getCurrentUserId(request);
        cartSyncService.mergeGuestCart(guestCartId, userId);
        return ApiResponse.success();
    }
    
    /**
     * 获取购物车商品数量
     */
    @GetMapping("/count")
    public ApiResponse<Integer> getCartCount(HttpServletRequest request) {
        Long userId = getCurrentUserId(request);
        CartSummaryDTO cart = cartService.getUserCart(userId);
        return ApiResponse.success(cart.getTotalQuantity());
    }
}

# 2. 请求DTO

@Data
public class AddToCartRequest {
    @NotNull(message = "商品ID不能为空")
    private Long productId;
    
    @NotNull(message = "数量不能为空")
    @Min(value = 1, message = "数量必须大于0")
    private Integer quantity;
    
    private String spec; // 商品规格
}

@Data
public class UpdateQuantityRequest {
    @NotNull(message = "购物车项ID不能为空")
    private Long cartItemId;
    
    @NotNull(message = "数量不能为空")
    @Min(value = 0, message = "数量不能小于0")
    private Integer quantity;
}

@Data
public class BatchRemoveRequest {
    @NotEmpty(message = "商品ID列表不能为空")
    private List<Long> itemIds;
}

@Data
public class SelectItemsRequest {
    @NotEmpty(message = "商品ID列表不能为空")
    private List<Long> itemIds;
    
    @NotNull(message = "选择状态不能为空")
    private Boolean selected;
}

# 购物车优化策略

# 1. 性能优化

@Component
public class CartPerformanceOptimizer {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    /**
     * 批量获取商品信息
     */
    public Map<Long, Product> batchGetProducts(List<Long> productIds) {
        // 先从缓存获取
        Map<Long, Product> cachedProducts = new HashMap<>();
        List<Long> uncachedIds = new ArrayList<>();
        
        for (Long productId : productIds) {
            String key = "product:" + productId;
            Product product = (Product) redisTemplate.opsForValue().get(key);
            if (product != null) {
                cachedProducts.put(productId, product);
            } else {
                uncachedIds.add(productId);
            }
        }
        
        // 从数据库批量获取未缓存的商品
        if (!uncachedIds.isEmpty()) {
            List<Product> products = productService.findByIds(uncachedIds);
            for (Product product : products) {
                cachedProducts.put(product.getId(), product);
                // 缓存商品信息
                String key = "product:" + product.getId();
                redisTemplate.opsForValue().set(key, product, 300, TimeUnit.SECONDS);
            }
        }
        
        return cachedProducts;
    }
    
    /**
     * 异步更新购物车价格
     */
    @Async
    public void asyncUpdateCartPrices(Long userId) {
        List<ShoppingCart> cartItems = cartRepository.findByUserId(userId);
        List<Long> productIds = cartItems.stream()
            .map(ShoppingCart::getProductId)
            .collect(Collectors.toList());
        
        Map<Long, Product> products = batchGetProducts(productIds);
        
        boolean hasUpdate = false;
        for (ShoppingCart item : cartItems) {
            Product product = products.get(item.getProductId());
            if (product != null && !item.getUnitPrice().equals(product.getPrice())) {
                item.setUnitPrice(product.getPrice());
                hasUpdate = true;
            }
        }
        
        if (hasUpdate) {
            cartRepository.saveAll(cartItems);
            cartCacheService.refreshUserCart(userId);
        }
    }
}

# 2. 数据一致性保证

@Component
public class CartConsistencyManager {
    
    @Autowired
    private ShoppingCartRepository cartRepository;
    
    @Autowired
    private ProductService productService;
    
    /**
     * 定时清理无效购物车项
     */
    @Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点执行
    public void cleanInvalidCartItems() {
        // 清理下架商品
        List<ShoppingCart> invalidItems = cartRepository.findInvalidItems();
        if (!invalidItems.isEmpty()) {
            cartRepository.deleteAll(invalidItems);
            log.info("清理无效购物车项:{} 条", invalidItems.size());
        }
        
        // 清理过期购物车(超过30天未更新)
        LocalDateTime expireTime = LocalDateTime.now().minusDays(30);
        List<ShoppingCart> expiredItems = cartRepository.findByUpdatedAtBefore(expireTime);
        if (!expiredItems.isEmpty()) {
            cartRepository.deleteAll(expiredItems);
            log.info("清理过期购物车项:{} 条", expiredItems.size());
        }
    }
    
    /**
     * 验证购物车数据一致性
     */
    public void validateCartConsistency(Long userId) {
        List<ShoppingCart> cartItems = cartRepository.findByUserId(userId);
        List<Long> productIds = cartItems.stream()
            .map(ShoppingCart::getProductId)
            .collect(Collectors.toList());
        
        Map<Long, Product> products = productService.findByIdsMap(productIds);
        
        for (ShoppingCart item : cartItems) {
            Product product = products.get(item.getProductId());
            
            // 检查商品是否存在
            if (product == null) {
                cartRepository.delete(item);
                continue;
            }
            
            // 检查商品状态
            if (product.getStatus() != ProductStatus.ACTIVE) {
                cartRepository.delete(item);
                continue;
            }
            
            // 检查库存
            if (item.getQuantity() > product.getStock()) {
                item.setQuantity(Math.max(1, product.getStock()));
                cartRepository.save(item);
            }
            
            // 检查价格
            if (!item.getUnitPrice().equals(product.getPrice())) {
                item.setUnitPrice(product.getPrice());
                cartRepository.save(item);
            }
        }
    }
}

# 3. 购物车事件处理

@Component
public class CartEventListener {
    
    @Autowired
    private NotificationService notificationService;
    
    @Autowired
    private UserBehaviorService userBehaviorService;
    
    @Autowired
    private RecommendationService recommendationService;
    
    /**
     * 购物车变更事件处理
     */
    @EventListener
    @Async
    public void handleCartChanged(CartChangedEvent event) {
        Long userId = event.getUserId();
        CartEventType eventType = event.getEventType();
        
        // 记录用户行为
        userBehaviorService.recordBehavior(userId, "CART_" + eventType.name(), 
            event.getProductId());
        
        // 更新推荐算法
        if (eventType == CartEventType.ADD_ITEM) {
            recommendationService.updateUserPreference(userId, event.getProductId());
        }
        
        // 发送通知(如购物车商品降价提醒)
        if (eventType == CartEventType.PRICE_CHANGE) {
            notificationService.sendPriceDropNotification(userId, event.getProductId());
        }
    }
    
    /**
     * 购物车商品库存不足事件
     */
    @EventListener
    @Async
    public void handleStockShortage(StockShortageEvent event) {
        // 通知用户库存不足
        notificationService.sendStockShortageNotification(
            event.getUserId(), event.getProductId());
        
        // 推荐替代商品
        recommendationService.recommendAlternativeProducts(
            event.getUserId(), event.getProductId());
    }
}

# 数据库设计优化

# 1. 索引设计

-- 购物车表索引
CREATE INDEX idx_cart_user_id ON shopping_carts(user_id);
CREATE INDEX idx_cart_user_product ON shopping_carts(user_id, product_id);
CREATE INDEX idx_cart_user_product_spec ON shopping_carts(user_id, product_id, product_spec);
CREATE INDEX idx_cart_updated_at ON shopping_carts(updated_at);
CREATE INDEX idx_cart_created_at ON shopping_carts(created_at);

# 2. 分表策略

@Component
public class CartShardingStrategy {
    
    /**
     * 根据用户ID分表
     */
    public String getTableName(Long userId) {
        int shardIndex = (int) (userId % 10);
        return "shopping_carts_" + shardIndex;
    }
    
    /**
     * 获取所有分表名
     */
    public List<String> getAllTableNames() {
        List<String> tableNames = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            tableNames.add("shopping_carts_" + i);
        }
        return tableNames;
    }
}

# 总结

购物车系统的关键设计要点:

  1. 存储策略:采用Redis+MySQL混合存储,兼顾性能和可靠性
  2. 数据一致性:定时同步、实时验证、异常处理机制
  3. 性能优化:缓存策略、批量操作、异步处理
  4. 用户体验:游客购物车、登录合并、实时更新
  5. 扩展性:事件驱动、分表分库、微服务架构
  6. 数据清理:定时清理过期和无效数据

通过以上设计,可以构建一个高性能、高可用的购物车系统,为用户提供流畅的购物体验。