在电商系统的开发过程中,购物车是连接用户浏览和订单支付的关键中间环节,Java作为后端开发的主流语言,实现购物车功能需要兼顾数据存储、业务逻辑和性能优化多个维度。合理的购物车实现方案能够提升用户体验,同时降低后续订单模块的开发复杂度。

购物车核心需求分析
开发购物车功能前首先要明确核心需求,通常包含以下功能点:
- 未登录用户可以将商品加入临时购物车,登录后合并临时购物车数据
- 支持修改购物车中商品的数量,同时校验商品库存是否充足
- 支持删除购物车中的单个商品或者清空全部商品
- 支持勾选部分商品进入结算流程,计算选中商品的总价
- 购物车数据需要持久化存储,同时保证高频操作的性能
购物车数据结构设计
购物车的核心数据包含用户标识、商品信息、商品数量、选中状态几个部分,我们可以设计如下的数据结构:
// 购物车项实体类
public class CartItem {
// 商品ID
private Long productId;
// 商品名称
private String productName;
// 商品单价
private BigDecimal price;
// 商品数量
private Integer quantity;
// 是否选中,用于结算
private Boolean checked;
// 商品库存
private Integer stock;
// 商品图片地址
private String imageUrl;
// 省略getter、setter和构造方法
}
// 购物车实体类
public class Cart {
// 用户ID,未登录时为临时标识
private String userId;
// 购物车项集合
private List<CartItem> items;
// 购物车商品总数量
private Integer totalCount;
// 选中商品的总价
private BigDecimal totalPrice;
// 省略getter、setter和构造方法
}
基于Spring Boot的基础实现
环境搭建
首先创建Spring Boot项目,引入Web、Redis、Lombok等依赖,Redis用于存储购物车数据,提升读写性能。配置文件添加Redis连接信息:
spring.redis.host=127.0.0.1 spring.redis.port=6379 spring.redis.database=0
核心功能实现
1. 添加商品到购物车,需要判断当前商品是否已经在购物车中,如果存在则更新数量,不存在则新增条目:
@Service
public class CartServiceImpl implements CartService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private ProductService productService;
private static final String CART_PREFIX = "cart:";
@Override
public Cart addProductToCart(String userId, Long productId, Integer quantity) {
// 校验商品是否存在且库存充足
Product product = productService.getProductById(productId);
if (product == null) {
throw new RuntimeException("商品不存在");
}
if (product.getStock() < quantity) {
throw new RuntimeException("商品库存不足");
}
// 从Redis获取当前购物车
String cartKey = CART_PREFIX + userId;
Cart cart = (Cart) redisTemplate.opsForValue().get(cartKey);
if (cart == null) {
cart = new Cart();
cart.setUserId(userId);
cart.setItems(new ArrayList<>());
}
// 判断商品是否已在购物车中
List<CartItem> items = cart.getItems();
boolean exists = false;
for (CartItem item : items) {
if (item.getProductId().equals(productId)) {
// 已存在则更新数量
int newQuantity = item.getQuantity() + quantity;
if (newQuantity > product.getStock()) {
throw new RuntimeException("商品库存不足");
}
item.setQuantity(newQuantity);
exists = true;
break;
}
}
// 不存在则新增购物车项
if (!exists) {
CartItem newItem = new CartItem();
newItem.setProductId(productId);
newItem.setProductName(product.getName());
newItem.setPrice(product.getPrice());
newItem.setQuantity(quantity);
newItem.setChecked(true);
newItem.setStock(product.getStock());
newItem.setImageUrl(product.getImageUrl());
items.add(newItem);
}
// 重新计算购物车汇总数据
calculateCartSummary(cart);
// 更新Redis中的购物车数据,设置过期时间7天
redisTemplate.opsForValue().set(cartKey, cart, 7, TimeUnit.DAYS);
return cart;
}
// 计算购物车总数量和选中商品总价
private void calculateCartSummary(Cart cart) {
int totalCount = 0;
BigDecimal totalPrice = BigDecimal.ZERO;
for (CartItem item : cart.getItems()) {
totalCount += item.getQuantity();
if (item.getChecked()) {
totalPrice = totalPrice.add(item.getPrice().multiply(new BigDecimal(item.getQuantity())));
}
}
cart.setTotalCount(totalCount);
cart.setTotalPrice(totalPrice);
}
}
2. 修改购物车商品数量,需要校验库存和数量合法性:
@Override
public Cart updateCartItemQuantity(String userId, Long productId, Integer quantity) {
if (quantity <= 0) {
throw new RuntimeException("商品数量不能小于1");
}
String cartKey = CART_PREFIX + userId;
Cart cart = (Cart) redisTemplate.opsForValue().get(cartKey);
if (cart == null) {
throw new RuntimeException("购物车不存在");
}
Product product = productService.getProductById(productId);
if (product.getStock() < quantity) {
throw new RuntimeException("商品库存不足");
}
for (CartItem item : cart.getItems()) {
if (item.getProductId().equals(productId)) {
item.setQuantity(quantity);
break;
}
}
calculateCartSummary(cart);
redisTemplate.opsForValue().set(cartKey, cart, 7, TimeUnit.DAYS);
return cart;
}
3. 删除购物车商品,支持删除单个或者清空全部:
@Override
public Cart deleteCartItem(String userId, Long productId) {
String cartKey = CART_PREFIX + userId;
Cart cart = (Cart) redisTemplate.opsForValue().get(cartKey);
if (cart == null) {
throw new RuntimeException("购物车不存在");
}
cart.getItems().removeIf(item -> item.getProductId().equals(productId));
calculateCartSummary(cart);
redisTemplate.opsForValue().set(cartKey, cart, 7, TimeUnit.DAYS);
return cart;
}
@Override
public void clearCart(String userId) {
String cartKey = CART_PREFIX + userId;
redisTemplate.delete(cartKey);
}
未登录用户购物车处理
对于未登录的用户,我们可以使用一个临时标识(比如前端生成的UUID)作为购物车的用户ID,当用户登录后,将临时购物车的数据合并到登录用户的购物车中:
@Override
public Cart mergeCart(String tempUserId, String loginUserId) {
String tempCartKey = CART_PREFIX + tempUserId;
String loginCartKey = CART_PREFIX + loginUserId;
// 获取临时购物车和登录用户购物车
Cart tempCart = (Cart) redisTemplate.opsForValue().get(tempCartKey);
Cart loginCart = (Cart) redisTemplate.opsForValue().get(loginCartKey);
if (tempCart == null) {
return loginCart == null ? new Cart() : loginCart;
}
if (loginCart == null) {
loginCart = new Cart();
loginCart.setUserId(loginUserId);
loginCart.setItems(new ArrayList<>());
}
// 合并购物车项
for (CartItem tempItem : tempCart.getItems()) {
boolean exists = false;
for (CartItem loginItem : loginCart.getItems()) {
if (loginItem.getProductId().equals(tempItem.getProductId())) {
// 已存在则累加数量
int newQuantity = loginItem.getQuantity() + tempItem.getQuantity();
Product product = productService.getProductById(tempItem.getProductId());
if (newQuantity > product.getStock()) {
newQuantity = product.getStock();
}
loginItem.setQuantity(newQuantity);
exists = true;
break;
}
}
if (!exists) {
loginCart.getItems().add(tempItem);
}
}
calculateCartSummary(loginCart);
// 更新登录用户购物车,删除临时购物车
redisTemplate.opsForValue().set(loginCartKey, loginCart, 7, TimeUnit.DAYS);
redisTemplate.delete(tempCartKey);
return loginCart;
}
开发注意事项
- 购物车数据存储在Redis时,建议设置合理的过期时间,避免无效数据占用存储空间
- 每次操作购物车后都要重新计算汇总数据,保证数据准确性
- 商品库存发生变更时,需要同步更新购物车中对应商品的库存信息,避免超卖
- 如果购物车数据量较大,可以考虑对Redis的key进行分片存储,提升读写效率
Java购物车功能项目开发Spring_BootRedis修改时间:2026-07-22 21:12:18