电商平台-常见购物车功能实现介绍

  1. 购物车里面的购物项常见字段

user_id、product_id、buy_num

  1. 购物车常见实现方式

实现方式一: 存储到数据库

性能存在瓶颈

实现方式二: 前端本地存储-localstorage-sessionstorage

localstorage在浏览器中存储 key/value 对,没有过期时间。

sessionstorage在浏览器中存储 key/value 对,在关闭会话窗口后将会删除这些数据。

实现方式三: 后端存储到缓存如redis

可以开启AOF持久化防止重启丢失(推荐)

可以存储海量的数据

实现方式四: 后端存储到缓存如redis-并同步更新到数据库

可能会想到缓存和数据库的一致性,加了用户唯一标识后,没高并发操作同一数据的场景

1. 代码落地 -购物车和购物项VO类开发

CartVO 购物车:一个购物车里面,存在多个购物项

/**
 * @author:Carl
 * @Description: 购物车 一个购物车里面,存在多个购物项
 */
public class CartVO {

    /**
     * 购物项集合
     */
    @JsonProperty("cart_items")
    private List<CartItemVO> cartItems;

    /**
     * 购物项总数量
     */
    @JsonProperty("total_num")
    private Integer totalNum;

    /**
     * 购物车总金额
     */
    @JsonProperty("total_prices")
    private BigDecimal totalPrices;


    /**
     * 最终真实支付价格
     */
    @JsonProperty("real_pay_price")
    private BigDecimal realPayPrice;


    public List<CartItemVO> getCartItems() {
        return cartItems;
    }

    public Integer getTotalNum() {

        if (this.cartItems != null){
            int total = cartItems.stream().mapToInt(CartItemVO::getBuyNum).sum();
            return total;
        }
        return 0;
    }

    public BigDecimal getTotalPrices() {
        BigDecimal amount = new BigDecimal("0");
        if (this.cartItems != null){
            for (CartItemVO cartItemVO : cartItems){
                BigDecimal totalAmount = cartItemVO.getTotalAmount();
                amount = amount.add(totalAmount);
            }
        }
        return amount;
    }

    public BigDecimal getRealPayPrice() {
        BigDecimal amount = new BigDecimal("0");
        if (this.cartItems != null){
            for (CartItemVO cartItemVO : cartItems){
                BigDecimal totalAmount = cartItemVO.getTotalAmount();
                amount = amount.add(totalAmount);
            }
        }
        return amount;
    }

    public void setCartItems(List<CartItemVO> cartItems) {
        this.cartItems = cartItems;
    }
}

CartItemVO 购物项:

  1. 商品id
  2. 购买数量
  3. 商品标题(冗余)
  4. 商品图片(冗余)
  5. 商品单价
  6. 总价格 ( 单价*数量 )
/**
 * @author:Carl
 * @Description:购物项
 */

public class CartItemVO {

    /**
     * 商品id
     */
    @JsonProperty("product_id")
    private Long productId;

    /**
     * 购买数量
     */
    @JsonProperty("buy_num")
    private Integer buyNum;

    /**
     * 商品标题
     */
    @JsonProperty("product_title")
    private String productTitle;

    /**
     * 商品图片
     */
    @JsonProperty("product_img")
    private String productImg;

    /**
     * 商品单价
     */
    private BigDecimal amount;

    /**
     *总价格 ( 单价*数量 )
     */
    @JsonProperty("total_amount")
    private BigDecimal totalAmount;

    public Long getProductId() {
        return productId;
    }

    public void setProductId(Long productId) {
        this.productId = productId;
    }

    public Integer getBuyNum() {
        return buyNum;
    }

    public void setBuyNum(Integer buyNum) {
        this.buyNum = buyNum;
    }

    public String getProductTitle() {
        return productTitle;
    }

    public void setProductTitle(String productTitle) {
        this.productTitle = productTitle;
    }

    public String getProductImg() {
        return productImg;
    }

    public void setProductImg(String productImg) {
        this.productImg = productImg;
    }

    public BigDecimal getAmount() {
        return amount;
    }

    public void setAmount(BigDecimal amount) {
        this.amount = amount;
    }

    /**
     * 总价格 ( 单价*数量 )
     * @return TotalAmount
     */
    public BigDecimal getTotalAmount() {
        return this.amount.multiply(new BigDecimal(this.buyNum));
    }

    public void setTotalAmount(BigDecimal totalAmount) {
        this.totalAmount = totalAmount;
    }
}

2.代码落地 - 购物车redis数据结构

  1. 购物车数据结构介绍
  2. 一个购物车里面,存在多个购物项
  3. 所以 购物车结构是一个双层Map:
  4. Map<String,Map<String,String>>
  5. 第一层Map,Key是用户id
  6. 第二层Map,Key是购物车中商品id,值是购物车数据
  7. 对应redis里面的存储
  8. redis里面有多种数据结构,应该使用哪种?
  9. 答案是 hash结构

image

3. 商品微服务购物车接口 - 代码落地

3.1 interfaces :

/**
添加到购物车
@param cartItemRequest
@return
 */
@RequestMapping(value = "add_to_cart",method = {RequestMethod.GET,RequestMethod.POST})
JsonData addToCart(@RequestBody CartItemRequest cartItemRequest){
    cartService.addToCart(cartItemRequest);
    return JsonData.buildSuccess();}
  /**
     * 清空购物车 只需要携带header头信息即可
     *
     * @return
     */
    @RequestMapping(value = "clear_cart", method = {RequestMethod.GET, RequestMethod.POST})
    JsonData clear() {
        cartService.clear();
        return JsonData.buildSuccess();
    }


    /**
     * 查看我的购物车
     */
    @RequestMapping(value = "my_cart", method = {RequestMethod.GET, RequestMethod.POST})
    JsonData myCart() {
        CartVO myCart = cartService.getMyCart();
        return JsonData.buildSuccess(myCart);
    }

    /**
     * 删除购物车中的购物项
     */
    @RequestMapping(value = "remove_item", method = RequestMethod.DELETE)
    JsonData removeItem(Long productId) {
        cartService.removeItemByProductId(productId);
        return JsonData.buildSuccess();
    }

    /**
     * 修改购物项数量
     */
    @RequestMapping(value = "change_Item", method = {RequestMethod.POST,RequestMethod.GET})
    JsonData changItem(@RequestBody CartItemRequest cartItemRequest) {
        cartService.changItem(cartItemRequest);
       return JsonData.buildSuccess();
    }

3.2 拦截器配置:

public LoginInterceptor loginInterceptor(){
    return new LoginInterceptor();
}

@Override
public void addInterceptors(InterceptorRegistry registry) {


    registry.addInterceptor(loginInterceptor())
            .addPathPatterns("/api/cart/*/**")
            //不拦截的路径
            .excludePathPatterns("");

    WebMvcConfigurer.super.addInterceptors(registry);

}

3.3 application :

/**
 * 添加商品到购物车接口
 * @param cartItemRequest
 */
void addToCart(CartItemRequest cartItemRequest);

/**
 * 清空购物车
 */
void clear();

/**
 * 获得购物车详情
 * @return
 */
CartVO getMyCart();

/**
 * 删除一个购物车中的购物项通过商品id
 * @param productId
 */
void removeItemByProductId(Long productId);

/**
 * 修改购物项
 * @param cartItemRequest
 */
void changItem(CartItemRequest cartItemRequest);

其中addToCart :

Long productId = cartItemRequest.getProductId();
        Integer buyNum = cartItemRequest.getBuyNum();
        BoundHashOperations<String,Object,Object>  mycart =  getMyCartOPs();
        Object cacheObj = mycart.get(productId);
        String result = "";
        if (cacheObj != null){
            result = (String) cacheObj;
         }
        if (StringUtils.isBlank(result)){
            //不存在则创建一个购物车项
            CartItemVO cartItemVO = new CartItemVO();
            ProductVO productVO = productService.findByDetail(productId);
            if (productVO == null){
                throw new BizException(BizCodeEnum.CART_FAIL);
            }
            cartItemVO.setAmount(productVO.getAmount());
            cartItemVO.setBuyNum(buyNum);
            cartItemVO.setProductId(productId);
            cartItemVO.setProductImg(productVO.getCoverImg());
            cartItemVO.setProductTitle(productVO.getTitle());
            mycart.put(productId, JSON.toJSONString(cartItemVO));
        }else {
            //存在则新增数量
            CartItemVO cartItemVO = JSON.parseObject(result,CartItemVO.class);
            cartItemVO.setBuyNum(cartItemVO.getBuyNum()+buyNum);
            mycart.put(productId,JSON.toJSONString(cartItemVO));
        }
最后修改:2026 年 06 月 06 日
如果觉得我的文章对你有用,请随意赞赏