购物车管理系统核心是理清持有关系、状态变更时机和并发处理;用ArrayList持商品项,CartItem封装ID、数量、快照价;登录态通过AuthenticationContext隔离临时与用户购物车;addProduct返回boolean供前端反馈;并发超卖需分布式锁或乐观锁校验库存。

Java里实现购物车管理系统,核心不是堆砌类,而是理清「谁持有谁」「状态何时变更」「并发怎么扛」。直接上继承和封装容易绕进设计模式陷阱,先跑通业务流再优化结构更实际。
购物车类该持有商品列表还是订单快照?
用 ArrayList 持有商品项最稳妥,别用 Map 存 productId 和数量——后期要加价格、规格、库存校验时会卡死。
-
CartItem必须封装productID、quantity、unitPrice(下单时快照价),不能只存 ID 去实时查库 - 加购时检查
quantity > 0和库存余量,失败就抛IllegalArgumentException,别默默吞掉 - 避免在
Cart类里写数据库操作——它只管内存态聚合,持久化交给单独的CartRepository
怎么处理用户登录态和购物车归属?
不要把 userId 当字段塞进 Cart 类。真实场景中:未登录用户用临时 sessionId 存 Redis,登录后合并;已登录用户用 userId 作主键查 DB 或缓存。
- 构造
CartService时传入AuthenticationContext(含userId或sessionId),由它决定加载哪份数据 - 合并逻辑写在服务层:比对临时购物车与用户历史购物车的
productID,数量相加,冲突时以用户端为准 - Redis key 建议用
"cart:{userId}"或"cart:temp:{sessionId}",别拼错导致取不到
为什么 addProduct() 方法必须返回 boolean 而非 void?
因为前端需要明确知道加购是否成功,尤其是库存不足或重复添加时。返回 true 表示新增/更新成功,false 表示被拒绝,调用方才能触发 Toast 提示或跳转缺货页。
public boolean addProduct(String productId, int quantity) {
if (quantity <= 0) return false;
CartItem existing = findItemByProductId(productId);
if (existing != null) {
int newQty = existing.getQuantity() + quantity;
if (newQty > inventoryService.getStock(productId)) {
return false;
}
existing.setQuantity(newQty);
} else {
CartItem item = new CartItem(productId, quantity, priceService.getPrice(productId));
items.add(item);
}
return true;
}并发场景下如何避免超卖?
单机用 synchronized(this) 不够,集群环境必须靠分布式锁或数据库乐观锁。别信「先查再更新」这种写法,高并发下必然出错。
- Redis 分布式锁推荐用
SET cart:{userId} NX PX 30000,加锁失败立刻返回错误,别重试 - DB 层更新库存用带条件的 SQL:
UPDATE inventory SET stock = stock - ? WHERE product_id = ? AND stock >= ?,检查executeUpdate()返回值是否为 1 - 购物车本身的数量变更可以不加锁——只要库存校验和扣减是原子的,购物车多加几次只是暂时态,最终下单时才拍板
真正难的不是写几个 getter/setter,而是库存校验点放哪、锁粒度怎么控、临时与永久购物车怎么桥接。这些地方写错,压测时才会暴露,不是编译报错能拦住的。










