购物车系统 - 承载用户购买意愿的智能容器
# 购物车系统 - 承载用户购买意愿的智能容器
# 📖 故事继续
小明在搜索推荐引擎的帮助下,找到了几款心仪的瑞士手表。他点击了一款欧米茄海马系列手表的"加入购物车"按钮,页面右上角的购物车图标立即显示了"1"的红色数字。接着,他又浏览了推荐的手表配件,将一条真皮表带也加入了购物车。
当小明点击购物车图标时,一个侧边栏滑出,清晰地展示了他选择的商品:手表$1,299、表带$89,系统还贴心地计算出了总价$1,388,并提示他"再购买$12即可享受免费国际配送"。更智能的是,系统还推荐了一个手表保养套装,正好$15,既满足了免邮条件,又是实用的配件。
小红作为一个经常海淘的用户,她的购物车更加复杂。里面有来自不同国家的商品:美国的化妆品、日本的护肤品、德国的保健品。系统智能地按照发货地进行了分组,并分别计算了每组的运费和预计到达时间。当她在手机和电脑之间切换时,购物车的内容始终保持同步。
这个看似简单的购物车,实际上是一个复杂的分布式系统,需要处理高并发、数据一致性、跨设备同步、实时计算等多种技术挑战。今天,我们就来深入了解这个"智能容器"的设计奥秘。
# 🎯 系统概述
购物车系统是跨境电商平台的核心交易组件,它不仅要存储用户的购买意愿,还要提供实时的价格计算、库存检查、物流预估、优惠券应用等功能。系统需要支持高并发访问、跨设备同步、离线操作等复杂场景。
# 核心功能
- 商品添加/删除/修改
- 实时价格计算
- 库存状态检查
- 跨设备数据同步
- 购物车合并
- 优惠券应用
- 物流费用计算
- 智能推荐
# 🏗️ 系统架构设计
# 整体架构图
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 客户端 │ │ API网关 │ │ 购物车服务 │
│ │────│ │────│ │
│ - Web端 │ │ - 请求路由 │ │ - 购物车管理 │
│ - 移动端 │ │ - 身份验证 │ │ - 价格计算 │
│ - 小程序 │ │ - 限流熔断 │ │ - 库存检查 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│
┌─────────────────┐ ┌─────────────────┐
│ 消息队列 │ │ 数据存储层 │
│ │ │ │
│ - 异步处理 │ │ - Redis缓存 │
│ - 事件通知 │ │ - MySQL持久化 │
│ - 数据同步 │ │ - 分布式锁 │
└─────────────────┘ └─────────────────┘
│
┌─────────────────┐
│ 外部服务 │
│ │
│ - 商品服务 │
│ - 库存服务 │
│ - 价格服务 │
│ - 物流服务 │
└─────────────────┘
# 💻 核心代码实现
# 1. 购物车实体模型设计
/**
* 购物车实体类
* 设计思路:支持多设备、多会话的购物车管理
*/
@Entity
@Table(name = "shopping_carts")
public class ShoppingCart {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* 用户ID - 登录用户的唯一标识
*/
@Column(name = "user_id")
private String userId;
/**
* 会话ID - 未登录用户的临时标识
*/
@Column(name = "session_id")
private String sessionId;
/**
* 设备类型:WEB, MOBILE, MINIPROGRAM
*/
@Column(name = "device_type")
private String deviceType;
/**
* 购物车状态:ACTIVE, MERGED, EXPIRED
*/
@Column(name = "status")
private String status;
/**
* 创建时间
*/
@CreationTimestamp
@Column(name = "created_at")
private LocalDateTime createdAt;
/**
* 最后更新时间
*/
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
/**
* 过期时间(未登录用户30天,登录用户90天)
*/
@Column(name = "expires_at")
private LocalDateTime expiresAt;
// 构造函数、getter、setter省略...
}
/**
* 购物车商品项实体
*/
@Entity
@Table(name = "cart_items")
public class CartItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* 购物车ID
*/
@Column(name = "cart_id")
private Long cartId;
/**
* 商品SKU
*/
@Column(name = "product_sku")
private String productSku;
/**
* 商品数量
*/
@Column(name = "quantity")
private Integer quantity;
/**
* 添加时的价格(用于价格变动提醒)
*/
@Column(name = "added_price", precision = 10, scale = 2)
private BigDecimal addedPrice;
/**
* 选中状态(支持部分结算)
*/
@Column(name = "selected")
private Boolean selected = true;
/**
* 商品规格信息(JSON格式)
* 例如:{"颜色": "黑色", "尺寸": "42mm"}
*/
@Column(name = "specifications", columnDefinition = "JSON")
private String specifications;
/**
* 添加时间
*/
@CreationTimestamp
@Column(name = "created_at")
private LocalDateTime createdAt;
/**
* 更新时间
*/
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
// 构造函数、getter、setter省略...
}
# 2. 购物车服务核心实现
/**
* 购物车服务实现类
* 核心职责:购物车CRUD操作、价格计算、库存检查、数据同步
*/
@Service
@Transactional
public class ShoppingCartService {
@Autowired
private ShoppingCartRepository cartRepository;
@Autowired
private CartItemRepository cartItemRepository;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private ProductService productService;
@Autowired
private InventoryService inventoryService;
@Autowired
private PricingService pricingService;
@Autowired
private ShippingService shippingService;
@Autowired
private DistributedLockService lockService;
@Autowired
private CartEventPublisher eventPublisher;
/**
* 添加商品到购物车
* 核心逻辑:库存检查 → 价格获取 → 数据存储 → 缓存更新
*/
public CartOperationResult addToCart(AddToCartRequest request) {
String lockKey = "cart:lock:" + request.getUserIdentifier();
return lockService.executeWithLock(lockKey, Duration.ofSeconds(5), () -> {
// 1. 参数验证
validateAddToCartRequest(request);
// 2. 获取或创建购物车
ShoppingCart cart = getOrCreateCart(request.getUserId(), request.getSessionId(),
request.getDeviceType());
// 3. 检查商品信息
ProductInfo productInfo = productService.getProductInfo(request.getProductSku());
if (productInfo == null || !productInfo.isAvailable()) {
throw new ProductNotAvailableException("商品不可用: " + request.getProductSku());
}
// 4. 库存检查
InventoryInfo inventoryInfo = inventoryService.checkInventory(
request.getProductSku(), request.getQuantity());
if (!inventoryInfo.isAvailable()) {
return CartOperationResult.failure("库存不足,当前可用库存: " + inventoryInfo.getAvailableQuantity());
}
// 5. 检查是否已存在相同商品
Optional<CartItem> existingItem = cartItemRepository
.findByCartIdAndProductSkuAndSpecifications(
cart.getId(), request.getProductSku(), request.getSpecifications());
CartItem cartItem;
if (existingItem.isPresent()) {
// 更新数量
cartItem = existingItem.get();
int newQuantity = cartItem.getQuantity() + request.getQuantity();
// 再次检查库存
InventoryInfo newInventoryCheck = inventoryService.checkInventory(
request.getProductSku(), newQuantity);
if (!newInventoryCheck.isAvailable()) {
return CartOperationResult.failure(
"库存不足,当前购物车中已有 " + cartItem.getQuantity() + " 件,最多还能添加 " +
(newInventoryCheck.getAvailableQuantity() - cartItem.getQuantity()) + " 件");
}
cartItem.setQuantity(newQuantity);
cartItem.setUpdatedAt(LocalDateTime.now());
} else {
// 创建新的购物车项
cartItem = new CartItem();
cartItem.setCartId(cart.getId());
cartItem.setProductSku(request.getProductSku());
cartItem.setQuantity(request.getQuantity());
cartItem.setAddedPrice(productInfo.getCurrentPrice());
cartItem.setSpecifications(request.getSpecifications());
cartItem.setSelected(true);
}
// 6. 保存到数据库
cartItem = cartItemRepository.save(cartItem);
// 7. 更新购物车时间
cart.setUpdatedAt(LocalDateTime.now());
cartRepository.save(cart);
// 8. 更新缓存
updateCartCache(cart.getId());
// 9. 发布事件
eventPublisher.publishCartItemAdded(cart.getId(), cartItem);
log.info("商品添加到购物车成功 - 用户: {}, 商品: {}, 数量: {}",
request.getUserIdentifier(), request.getProductSku(), request.getQuantity());
return CartOperationResult.success("商品已添加到购物车", cartItem.getId());
});
}
/**
* 获取购物车详情
* 包含实时价格、库存状态、物流信息等
*/
public CartDetailDTO getCartDetail(String userId, String sessionId,
String currencyCode, String countryCode) {
// 1. 获取购物车
ShoppingCart cart = getActiveCart(userId, sessionId);
if (cart == null) {
return CartDetailDTO.empty();
}
// 2. 尝试从缓存获取
String cacheKey = buildCartCacheKey(cart.getId(), currencyCode, countryCode);
CartDetailDTO cachedDetail = (CartDetailDTO) redisTemplate.opsForValue().get(cacheKey);
if (cachedDetail != null && !isCartCacheExpired(cachedDetail)) {
log.debug("从缓存获取购物车详情,cartId: {}", cart.getId());
return cachedDetail;
}
// 3. 从数据库构建详情
CartDetailDTO cartDetail = buildCartDetail(cart, currencyCode, countryCode);
// 4. 缓存结果(5分钟过期)
redisTemplate.opsForValue().set(cacheKey, cartDetail, Duration.ofMinutes(5));
return cartDetail;
}
/**
* 构建购物车详情
* 核心逻辑:商品信息获取 → 价格计算 → 库存检查 → 物流计算
*/
private CartDetailDTO buildCartDetail(ShoppingCart cart, String currencyCode, String countryCode) {
// 1. 获取购物车商品列表
List<CartItem> cartItems = cartItemRepository.findByCartIdAndStatus(cart.getId(), "ACTIVE");
if (cartItems.isEmpty()) {
return CartDetailDTO.empty();
}
// 2. 批量获取商品信息(优化性能)
List<String> productSkus = cartItems.stream()
.map(CartItem::getProductSku)
.collect(Collectors.toList());
Map<String, ProductInfo> productInfoMap = productService.batchGetProductInfo(productSkus);
// 3. 批量检查库存
Map<String, InventoryInfo> inventoryInfoMap = inventoryService.batchCheckInventory(productSkus);
// 4. 构建购物车项详情列表
List<CartItemDetailDTO> itemDetails = new ArrayList<>();
BigDecimal totalAmount = BigDecimal.ZERO;
int totalQuantity = 0;
boolean hasUnavailableItems = false;
for (CartItem cartItem : cartItems) {
CartItemDetailDTO itemDetail = buildCartItemDetail(
cartItem, productInfoMap, inventoryInfoMap, currencyCode, countryCode);
itemDetails.add(itemDetail);
if (itemDetail.isSelected() && itemDetail.isAvailable()) {
totalAmount = totalAmount.add(itemDetail.getTotalPrice());
totalQuantity += itemDetail.getQuantity();
}
if (!itemDetail.isAvailable()) {
hasUnavailableItems = true;
}
}
// 5. 按发货地分组(跨境电商特有逻辑)
Map<String, List<CartItemDetailDTO>> itemsByOrigin = groupItemsByOrigin(itemDetails);
// 6. 计算物流费用
ShippingCalculationResult shippingResult = calculateShippingCosts(
itemsByOrigin, countryCode, totalAmount);
// 7. 构建购物车详情
CartDetailDTO cartDetail = new CartDetailDTO();
cartDetail.setCartId(cart.getId());
cartDetail.setItems(itemDetails);
cartDetail.setItemsByOrigin(itemsByOrigin);
cartDetail.setTotalQuantity(totalQuantity);
cartDetail.setSubtotal(totalAmount);
cartDetail.setShippingInfo(shippingResult);
cartDetail.setGrandTotal(totalAmount.add(shippingResult.getTotalShippingCost()));
cartDetail.setCurrencyCode(currencyCode);
cartDetail.setHasUnavailableItems(hasUnavailableItems);
cartDetail.setLastUpdated(LocalDateTime.now());
// 8. 添加优惠信息
addPromotionInfo(cartDetail);
// 9. 添加推荐商品
addRecommendedItems(cartDetail, cart.getUserId());
return cartDetail;
}
/**
* 构建单个购物车项详情
*/
private CartItemDetailDTO buildCartItemDetail(CartItem cartItem,
Map<String, ProductInfo> productInfoMap,
Map<String, InventoryInfo> inventoryInfoMap,
String currencyCode, String countryCode) {
ProductInfo productInfo = productInfoMap.get(cartItem.getProductSku());
InventoryInfo inventoryInfo = inventoryInfoMap.get(cartItem.getProductSku());
CartItemDetailDTO itemDetail = new CartItemDetailDTO();
itemDetail.setId(cartItem.getId());
itemDetail.setProductSku(cartItem.getProductSku());
itemDetail.setQuantity(cartItem.getQuantity());
itemDetail.setSelected(cartItem.getSelected());
itemDetail.setSpecifications(parseSpecifications(cartItem.getSpecifications()));
itemDetail.setAddedAt(cartItem.getCreatedAt());
if (productInfo != null) {
itemDetail.setProductName(productInfo.getName());
itemDetail.setProductImage(productInfo.getMainImage());
itemDetail.setBrand(productInfo.getBrand());
// 价格计算(考虑汇率和税费)
PriceCalculationResult priceResult = pricingService.calculatePrice(
productInfo.getBasePrice(), currencyCode, countryCode, cartItem.getQuantity());
itemDetail.setUnitPrice(priceResult.getUnitPrice());
itemDetail.setTotalPrice(priceResult.getTotalPrice());
itemDetail.setOriginalPrice(cartItem.getAddedPrice());
// 价格变动提醒
if (priceResult.getUnitPrice().compareTo(cartItem.getAddedPrice()) != 0) {
itemDetail.setPriceChanged(true);
itemDetail.setPriceChangeAmount(
priceResult.getUnitPrice().subtract(cartItem.getAddedPrice()));
}
}
if (inventoryInfo != null) {
itemDetail.setAvailable(inventoryInfo.isAvailable());
itemDetail.setAvailableQuantity(inventoryInfo.getAvailableQuantity());
// 库存不足提醒
if (cartItem.getQuantity() > inventoryInfo.getAvailableQuantity()) {
itemDetail.setQuantityExceedsStock(true);
itemDetail.setMaxAvailableQuantity(inventoryInfo.getAvailableQuantity());
}
}
return itemDetail;
}
/**
* 按发货地分组商品
* 跨境电商需要根据商品发货地计算不同的物流费用
*/
private Map<String, List<CartItemDetailDTO>> groupItemsByOrigin(List<CartItemDetailDTO> items) {
return items.stream()
.filter(item -> item.isSelected() && item.isAvailable())
.collect(Collectors.groupingBy(
item -> getProductOriginCountry(item.getProductSku()),
LinkedHashMap::new,
Collectors.toList()
));
}
/**
* 计算物流费用
*/
private ShippingCalculationResult calculateShippingCosts(
Map<String, List<CartItemDetailDTO>> itemsByOrigin,
String destinationCountry, BigDecimal totalAmount) {
ShippingCalculationResult result = new ShippingCalculationResult();
BigDecimal totalShippingCost = BigDecimal.ZERO;
List<ShippingGroupInfo> shippingGroups = new ArrayList<>();
for (Map.Entry<String, List<CartItemDetailDTO>> entry : itemsByOrigin.entrySet()) {
String originCountry = entry.getKey();
List<CartItemDetailDTO> items = entry.getValue();
// 计算该发货地的物流费用
ShippingCostCalculation shippingCalc = shippingService.calculateShippingCost(
originCountry, destinationCountry, items);
ShippingGroupInfo groupInfo = new ShippingGroupInfo();
groupInfo.setOriginCountry(originCountry);
groupInfo.setItems(items);
groupInfo.setShippingCost(shippingCalc.getCost());
groupInfo.setEstimatedDeliveryDays(shippingCalc.getEstimatedDays());
groupInfo.setFreeShippingThreshold(shippingCalc.getFreeShippingThreshold());
// 检查是否满足免邮条件
BigDecimal groupTotal = items.stream()
.map(CartItemDetailDTO::getTotalPrice)
.reduce(BigDecimal.ZERO, BigDecimal::add);
if (groupTotal.compareTo(shippingCalc.getFreeShippingThreshold()) >= 0) {
groupInfo.setShippingCost(BigDecimal.ZERO);
groupInfo.setFreeShippingApplied(true);
}
shippingGroups.add(groupInfo);
totalShippingCost = totalShippingCost.add(groupInfo.getShippingCost());
}
result.setShippingGroups(shippingGroups);
result.setTotalShippingCost(totalShippingCost);
// 计算距离免邮还需多少金额
calculateFreeShippingGap(result, totalAmount);
return result;
}
/**
* 更新购物车商品数量
*/
public CartOperationResult updateQuantity(UpdateQuantityRequest request) {
String lockKey = "cart:item:lock:" + request.getCartItemId();
return lockService.executeWithLock(lockKey, Duration.ofSeconds(3), () -> {
// 1. 获取购物车项
CartItem cartItem = cartItemRepository.findById(request.getCartItemId())
.orElseThrow(() -> new CartItemNotFoundException("购物车项不存在"));
// 2. 权限检查
validateCartItemOwnership(cartItem, request.getUserId(), request.getSessionId());
// 3. 数量验证
if (request.getQuantity() <= 0) {
// 数量为0或负数,删除商品
return removeFromCart(cartItem.getId(), request.getUserId(), request.getSessionId());
}
// 4. 库存检查
InventoryInfo inventoryInfo = inventoryService.checkInventory(
cartItem.getProductSku(), request.getQuantity());
if (!inventoryInfo.isAvailable()) {
return CartOperationResult.failure(
"库存不足,当前可用库存: " + inventoryInfo.getAvailableQuantity());
}
// 5. 更新数量
int oldQuantity = cartItem.getQuantity();
cartItem.setQuantity(request.getQuantity());
cartItem.setUpdatedAt(LocalDateTime.now());
cartItemRepository.save(cartItem);
// 6. 更新缓存
updateCartCache(cartItem.getCartId());
// 7. 发布事件
eventPublisher.publishCartItemQuantityUpdated(cartItem.getCartId(), cartItem, oldQuantity);
log.info("购物车商品数量更新成功 - 商品: {}, 原数量: {}, 新数量: {}",
cartItem.getProductSku(), oldQuantity, request.getQuantity());
return CartOperationResult.success("数量更新成功");
});
}
/**
* 购物车合并(用户登录时)
* 将未登录时的购物车合并到用户账户
*/
public CartMergeResult mergeCart(String userId, String sessionId) {
String lockKey = "cart:merge:lock:" + userId;
return lockService.executeWithLock(lockKey, Duration.ofSeconds(10), () -> {
// 1. 获取用户购物车和会话购物车
ShoppingCart userCart = cartRepository.findByUserIdAndStatus(userId, "ACTIVE")
.orElse(null);
ShoppingCart sessionCart = cartRepository.findBySessionIdAndStatus(sessionId, "ACTIVE")
.orElse(null);
if (sessionCart == null) {
return CartMergeResult.noSessionCart();
}
List<CartItem> sessionItems = cartItemRepository.findByCartIdAndStatus(
sessionCart.getId(), "ACTIVE");
if (sessionItems.isEmpty()) {
return CartMergeResult.emptySessionCart();
}
// 2. 创建或获取用户购物车
if (userCart == null) {
userCart = createUserCart(userId, sessionCart.getDeviceType());
}
// 3. 合并商品项
List<CartItem> userItems = cartItemRepository.findByCartIdAndStatus(
userCart.getId(), "ACTIVE");
Map<String, CartItem> userItemMap = userItems.stream()
.collect(Collectors.toMap(
item -> item.getProductSku() + ":" + item.getSpecifications(),
item -> item
));
int mergedCount = 0;
int conflictCount = 0;
for (CartItem sessionItem : sessionItems) {
String itemKey = sessionItem.getProductSku() + ":" + sessionItem.getSpecifications();
if (userItemMap.containsKey(itemKey)) {
// 存在相同商品,合并数量
CartItem userItem = userItemMap.get(itemKey);
int newQuantity = userItem.getQuantity() + sessionItem.getQuantity();
// 检查库存
InventoryInfo inventoryInfo = inventoryService.checkInventory(
userItem.getProductSku(), newQuantity);
if (inventoryInfo.isAvailable()) {
userItem.setQuantity(newQuantity);
userItem.setUpdatedAt(LocalDateTime.now());
cartItemRepository.save(userItem);
mergedCount++;
} else {
// 库存不足,保持原数量
conflictCount++;
}
} else {
// 新商品,直接添加
sessionItem.setCartId(userCart.getId());
sessionItem.setUpdatedAt(LocalDateTime.now());
cartItemRepository.save(sessionItem);
mergedCount++;
}
}
// 4. 标记会话购物车为已合并
sessionCart.setStatus("MERGED");
sessionCart.setUpdatedAt(LocalDateTime.now());
cartRepository.save(sessionCart);
// 5. 更新用户购物车时间
userCart.setUpdatedAt(LocalDateTime.now());
cartRepository.save(userCart);
// 6. 更新缓存
updateCartCache(userCart.getId());
clearCartCache(sessionCart.getId());
// 7. 发布事件
eventPublisher.publishCartMerged(userCart.getId(), sessionCart.getId(), mergedCount);
log.info("购物车合并完成 - 用户: {}, 合并商品数: {}, 冲突数: {}",
userId, mergedCount, conflictCount);
return CartMergeResult.success(mergedCount, conflictCount);
});
}
/**
* 清理过期购物车
* 定时任务,清理过期的购物车数据
*/
@Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点执行
public void cleanExpiredCarts() {
log.info("开始清理过期购物车...");
LocalDateTime expireTime = LocalDateTime.now();
// 1. 查找过期的购物车
List<ShoppingCart> expiredCarts = cartRepository.findExpiredCarts(expireTime);
if (expiredCarts.isEmpty()) {
log.info("没有过期的购物车需要清理");
return;
}
int cleanedCount = 0;
for (ShoppingCart cart : expiredCarts) {
try {
// 2. 删除购物车项
cartItemRepository.deleteByCartId(cart.getId());
// 3. 删除购物车
cartRepository.delete(cart);
// 4. 清理缓存
clearCartCache(cart.getId());
cleanedCount++;
} catch (Exception e) {
log.error("清理购物车失败,cartId: {}", cart.getId(), e);
}
}
log.info("过期购物车清理完成,清理数量: {}", cleanedCount);
}
// 辅助方法实现...
private void updateCartCache(Long cartId) {
// 清除相关缓存,下次访问时重新构建
String pattern = "cart:detail:" + cartId + ":*";
Set<String> keys = redisTemplate.keys(pattern);
if (!keys.isEmpty()) {
redisTemplate.delete(keys);
}
}
private String buildCartCacheKey(Long cartId, String currencyCode, String countryCode) {
return String.format("cart:detail:%d:%s:%s", cartId, currencyCode, countryCode);
}
private String getProductOriginCountry(String productSku) {
// 根据商品SKU获取发货地,实际项目中从商品服务获取
return "US"; // 示例
}
}
# 3. 分布式锁实现
/**
* 分布式锁服务
* 基于Redis实现,确保购物车操作的数据一致性
*/
@Service
public class DistributedLockService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
private static final String LOCK_PREFIX = "lock:";
private static final String LOCK_VALUE = "locked";
/**
* 执行带锁的操作
*/
public <T> T executeWithLock(String lockKey, Duration timeout, Supplier<T> operation) {
String fullLockKey = LOCK_PREFIX + lockKey;
String lockValue = UUID.randomUUID().toString();
try {
// 1. 尝试获取锁
boolean acquired = acquireLock(fullLockKey, lockValue, timeout);
if (!acquired) {
throw new LockAcquisitionException("获取锁失败: " + lockKey);
}
// 2. 执行业务操作
return operation.get();
} finally {
// 3. 释放锁
releaseLock(fullLockKey, lockValue);
}
}
/**
* 获取分布式锁
*/
private boolean acquireLock(String lockKey, String lockValue, Duration timeout) {
long timeoutMillis = timeout.toMillis();
long startTime = System.currentTimeMillis();
while (System.currentTimeMillis() - startTime < timeoutMillis) {
// 使用SET命令的NX和EX选项实现原子性加锁
Boolean result = redisTemplate.opsForValue().setIfAbsent(
lockKey, lockValue, Duration.ofSeconds(30));
if (Boolean.TRUE.equals(result)) {
return true;
}
// 短暂等待后重试
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
return false;
}
/**
* 释放分布式锁
*/
private void releaseLock(String lockKey, String lockValue) {
// 使用Lua脚本确保原子性释放锁
String luaScript =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";
redisTemplate.execute(
new DefaultRedisScript<>(luaScript, Long.class),
Collections.singletonList(lockKey),
lockValue
);
}
}
# 4. 购物车事件处理
/**
* 购物车事件发布器
* 用于发布购物车相关事件,支持异步处理和系统解耦
*/
@Component
public class CartEventPublisher {
@Autowired
private ApplicationEventPublisher eventPublisher;
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 发布商品添加事件
*/
public void publishCartItemAdded(Long cartId, CartItem cartItem) {
CartItemAddedEvent event = new CartItemAddedEvent();
event.setCartId(cartId);
event.setProductSku(cartItem.getProductSku());
event.setQuantity(cartItem.getQuantity());
event.setTimestamp(LocalDateTime.now());
// 1. 本地事件发布(同步处理)
eventPublisher.publishEvent(event);
// 2. 消息队列发布(异步处理)
rabbitTemplate.convertAndSend("cart.exchange", "cart.item.added", event);
log.debug("购物车商品添加事件已发布,cartId: {}, productSku: {}", cartId, cartItem.getProductSku());
}
/**
* 发布数量更新事件
*/
public void publishCartItemQuantityUpdated(Long cartId, CartItem cartItem, int oldQuantity) {
CartItemQuantityUpdatedEvent event = new CartItemQuantityUpdatedEvent();
event.setCartId(cartId);
event.setProductSku(cartItem.getProductSku());
event.setOldQuantity(oldQuantity);
event.setNewQuantity(cartItem.getQuantity());
event.setTimestamp(LocalDateTime.now());
eventPublisher.publishEvent(event);
rabbitTemplate.convertAndSend("cart.exchange", "cart.item.quantity.updated", event);
}
/**
* 发布购物车合并事件
*/
public void publishCartMerged(Long userCartId, Long sessionCartId, int mergedItemCount) {
CartMergedEvent event = new CartMergedEvent();
event.setUserCartId(userCartId);
event.setSessionCartId(sessionCartId);
event.setMergedItemCount(mergedItemCount);
event.setTimestamp(LocalDateTime.now());
eventPublisher.publishEvent(event);
rabbitTemplate.convertAndSend("cart.exchange", "cart.merged", event);
}
}
/**
* 购物车事件监听器
* 处理购物车相关的业务逻辑
*/
@Component
public class CartEventListener {
@Autowired
private UserBehaviorService userBehaviorService;
@Autowired
private RecommendationService recommendationService;
@Autowired
private InventoryService inventoryService;
/**
* 处理商品添加事件
*/
@EventListener
@Async
public void handleCartItemAdded(CartItemAddedEvent event) {
try {
// 1. 记录用户行为
userBehaviorService.recordCartAction(
event.getCartId(), "ADD_TO_CART", event.getProductSku(), event.getQuantity());
// 2. 更新推荐模型
recommendationService.updateUserPreference(
event.getCartId(), event.getProductSku(), "CART_ADD");
// 3. 预占库存(可选,根据业务需求)
// inventoryService.reserveInventory(event.getProductSku(), event.getQuantity());
} catch (Exception e) {
log.error("处理购物车商品添加事件失败", e);
}
}
/**
* 处理数量更新事件
*/
@EventListener
@Async
public void handleCartItemQuantityUpdated(CartItemQuantityUpdatedEvent event) {
try {
// 1. 记录用户行为
userBehaviorService.recordCartAction(
event.getCartId(), "UPDATE_QUANTITY", event.getProductSku(),
event.getNewQuantity() - event.getOldQuantity());
// 2. 调整库存预占(如果启用了预占机制)
int quantityDiff = event.getNewQuantity() - event.getOldQuantity();
if (quantityDiff > 0) {
// 增加预占
// inventoryService.reserveInventory(event.getProductSku(), quantityDiff);
} else if (quantityDiff < 0) {
// 释放预占
// inventoryService.releaseReservation(event.getProductSku(), Math.abs(quantityDiff));
}
} catch (Exception e) {
log.error("处理购物车数量更新事件失败", e);
}
}
}
# 🔧 技术要点解析
# 1. 数据一致性保证
分布式锁机制:
- 使用Redis实现分布式锁
- 确保并发操作的数据一致性
- 支持锁超时和自动释放
事务管理:
- 数据库事务确保ACID特性
- 补偿机制处理分布式事务
- 幂等性设计避免重复操作
# 2. 高性能设计
缓存策略:
- Redis缓存购物车详情
- 批量操作减少数据库访问
- 缓存预热和智能失效
异步处理:
- 事件驱动的异步处理
- 消息队列解耦系统组件
- 非关键路径异步执行
# 3. 跨设备同步
数据同步机制:
- 基于用户ID的数据关联
- 实时缓存更新
- 购物车合并算法
# 📊 监控与运维
# 1. 关键指标监控
/**
* 购物车监控指标
*/
@Component
public class CartMetricsCollector {
private final Counter cartOperationCounter;
private final Timer cartOperationTimer;
private final Gauge activeCartGauge;
public CartMetricsCollector(MeterRegistry meterRegistry) {
this.cartOperationCounter = Counter.builder("cart.operations.total")
.description("购物车操作总数")
.register(meterRegistry);
this.cartOperationTimer = Timer.builder("cart.operation.duration")
.description("购物车操作耗时")
.register(meterRegistry);
this.activeCartGauge = Gauge.builder("cart.active.count")
.description("活跃购物车数量")
.register(meterRegistry, this, CartMetricsCollector::getActiveCartCount);
}
public void recordCartOperation(String operation, boolean success, long duration) {
cartOperationCounter.increment(
Tags.of(
"operation", operation,
"success", String.valueOf(success)
)
);
cartOperationTimer.record(duration, TimeUnit.MILLISECONDS);
}
private double getActiveCartCount() {
// 实现获取活跃购物车数量的逻辑
return 0.0;
}
}
# 2. 性能优化配置
# application.yml
spring:
datasource:
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
redis:
lettuce:
pool:
max-active: 20
max-idle: 10
min-idle: 5
timeout: 2000ms
rabbitmq:
listener:
simple:
concurrency: 5
max-concurrency: 10
prefetch: 10
cart:
cache:
ttl: 300 # 5分钟
lock:
timeout: 5000 # 5秒
cleanup:
batch-size: 100
enabled: true
# 🎉 总结
通过本文的深入学习,我们全面了解了跨境电商购物车系统的设计与实现。从小明添加手表到购物车的简单操作,到小红复杂的多国商品购物车管理,背后是一套高度复杂的分布式系统在支撑。
# 关键收获
- 系统架构:分布式架构设计,支持高并发和高可用
- 数据一致性:分布式锁和事务管理确保数据准确性
- 性能优化:多级缓存、批量操作、异步处理等技术手段
- 业务逻辑:价格计算、库存检查、物流计算等核心算法
- 用户体验:跨设备同步、智能推荐、实时更新等功能
# 最佳实践
- 使用分布式锁确保并发操作的数据一致性
- 采用事件驱动架构实现系统解耦
- 实现多级缓存策略提升系统性能
- 设计合理的数据模型支持复杂业务场景
- 建立完善的监控和告警机制
# 技术演进方向
- 智能化:AI驱动的购物车优化和推荐
- 实时性:更快的数据同步和状态更新
- 个性化:基于用户行为的个性化购物车体验
- 全球化:更好的跨境电商支持和本地化
下一篇文章,我们将继续探讨跨境电商交易链路的下一个关键环节:价格计算引擎,看看如何处理复杂的跨境定价逻辑。