谷粒商城实战笔记-240~243-商城业务-购物车-页面环境搭建


这部分的主要内容:

  • 从product模块的详情页点击加入购物车,发送请求到cart购物车模块,添加成功后,跳转到success页面。
  • 详情页点击和首页点击我的购物车,向购物车服务发送请求,挑战到cartItem页面。
  • success页面点击去购物车结算跳转到结算页(即购物车列表页)
  • 完成添加购物车的后台逻辑
  • 解决重复提交的问题
    在这里插入图片描述

包含下面课程:

  • 240-商城业务-购物车-页面环境搭建
  • 241-商城业务-购物车-添加购物车
  • 242-商城业务-购物车-添加购物车细节
  • 243-商城业务-购物车-RedirectAttribute

一,页面调整

1,详情页增加“加入购物车”按钮

在这里插入图片描述
点击按钮向购物车服务发生请求。

加购成功后,跳转到success界面。

二,添加购物车后台实现

后台Service了一个名为 addToCart 的方法,用于将指定的商品添加到用户的购物车中。

添加到购物车中的主要逻辑如下:

  • 判断是已登录用户还是临时用户,不同类型的用户的redis key不同
  • redis中查询是否已经存在该skuId对应的商品
  • 如果存在,把该商品的数量加上页面上的值
  • 如果不存在,要调用远程接口查询sku的基本信息和销售属性信息,然后以skuid为key保存到redis中
 public CartItemVo addToCart(Long skuId, Integer num) throws ExecutionException, InterruptedException {

        //拿到要操作的购物车信息
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();

        //判断Redis是否有该商品的信息
        String productRedisValue = (String) cartOps.get(skuId.toString());
        //如果没有就添加数据
        if (StringUtils.isEmpty(productRedisValue)) {

            //2、添加新的商品到购物车(redis)
            CartItemVo cartItemVo = new CartItemVo();
            //开启第一个异步任务
            CompletableFuture<Void> getSkuInfoFuture = CompletableFuture.runAsync(() -> {
                //1、远程查询当前要添加商品的信息
                R productSkuInfo = productFeignService.getInfo(skuId);
                SkuInfoVo skuInfo = productSkuInfo.getData("skuInfo", new TypeReference<SkuInfoVo>() {});
                //数据赋值操作
                cartItemVo.setSkuId(skuInfo.getSkuId());
                cartItemVo.setTitle(skuInfo.getSkuTitle());
                cartItemVo.setImage(skuInfo.getSkuDefaultImg());
                cartItemVo.setPrice(skuInfo.getPrice());
                cartItemVo.setCount(num);
            }, executor);

            //开启第二个异步任务
            CompletableFuture<Void> getSkuAttrValuesFuture = CompletableFuture.runAsync(() -> {
                //2、远程查询skuAttrValues组合信息
                List<String> skuSaleAttrValues = productFeignService.getSkuSaleAttrValues(skuId);
                cartItemVo.setSkuAttrValues(skuSaleAttrValues);
            }, executor);

            //等待所有的异步任务全部完成
            CompletableFuture.allOf(getSkuInfoFuture, getSkuAttrValuesFuture).get();

            String cartItemJson = JSON.toJSONString(cartItemVo);
            cartOps.put(skuId.toString(), cartItemJson);

            return cartItemVo;
        } else {
            //购物车有此商品,修改数量即可
            CartItemVo cartItemVo = JSON.parseObject(productRedisValue, CartItemVo.class);
            cartItemVo.setCount(cartItemVo.getCount() + num);
            //修改redis的数据
            String cartItemJson = JSON.toJSONString(cartItemVo);
            cartOps.put(skuId.toString(),cartItemJson);

            return cartItemVo;
        }
    }



  1. 获取购物车操作对象:

    • 使用 getCartOps() 方法获取 Redis 中购物车相关的 BoundHashOperations 对象,用于后续的读写操作。
  2. 检查商品是否已经在购物车中:

    • 从 Redis 中查询当前商品 skuId 是否已经存在于购物车中。
    • 如果商品不存在,则需要从远程服务获取商品信息并将其添加到购物车;如果存在,则直接更新商品的数量。
  3. 商品不在购物车中的处理流程:

    • 创建一个新的 CartItemVo 实例。
    • 异步调用远程服务获取商品的基本信息,并填充到 CartItemVo 中。
    • 异步调用远程服务获取商品的属性值列表,并填充到 CartItemVo 中。
    • 等待所有异步任务完成。
    • CartItemVo 序列化为 JSON 字符串并存入 Redis。
  4. 商品已在购物车中的处理流程:

    • 从 Redis 中读取已存在的 CartItemVo 并反序列化。
    • 更新商品的数量。
    • 将更新后的 CartItemVo 再次序列化为 JSON 字符串并存回 Redis。

详细步骤

  1. 获取购物车操作对象:

    • 调用 getCartOps() 方法获取购物车的 BoundHashOperations 对象。
  2. 检查商品是否存在:

    • 检查商品是否已经在 Redis 购物车中。
  3. 商品不存在时:

    • 创建 CartItemVo 实例。
    • 使用 CompletableFuture 异步获取商品信息和属性值。
    • 等待所有异步任务完成。
    • CartItemVo 序列化并存入 Redis。
  4. 商品已存在时:

    • 从 Redis 获取 CartItemVo
    • 修改商品的数量。
    • 将更新后的 CartItemVo 序列化并存入 Redis。

异步处理的优点

  • 提高性能:通过异步处理,减少了等待远程服务响应的时间,从而提高了整体性能。
  • 提高响应速度:由于异步处理不需要等待所有任务完成就可以返回结果,因此能更快地响应客户端。

三,解决加购重复提交问题

我们加购的提交的地址是:

http://cart.gulimall.com/addCartItem?skuId=1&num=2

这个请求发出后,后台执行加购逻辑,如果直接返回success页面。

	@GetMapping(value = "/addCartItem")
    public String addToCart() {
        return "success";
    }

会导致一个问题,如果用户刷新这个页面,就会重复提交加购请求,这个商品会被重复加购,导致不好的客户体验。

所以不能把这个地址暴露在浏览器的地址栏。

参考京东的做法,我们可以在用户点击加购请求后不直接返回success页面,而是让浏览器重定向到一个新的地址,这个地址会返回sucess页面,但是这个地址不会有其他的业务逻辑。

具体的逻辑是这样:

  • 用户点击加购,浏览器发出加购请求
  • 后台接收处理加购请求,让浏览器重定向到一个新的地址
  • 这个地址对应的接口会返回success页面,但不执行其他逻辑,用户刷新不会导致加购的重复提交

代码如下。

@GetMapping(value = "/addCartItem")
    public String addCartItem(@RequestParam("skuId") Long skuId,
                              @RequestParam("num") Integer num,
                              RedirectAttributes attributes) throws ExecutionException, InterruptedException {

        cartService.addToCart(skuId,num);

        attributes.addAttribute("skuId",skuId);
        return "redirect:http://cart.gulimall.com/addToCartSuccessPage.html";
    }


    /**
     * 跳转到添加购物车成功页面
     * @param skuId
     * @param model
     * @return
     */
    @GetMapping(value = "/addToCartSuccessPage.html")
    public String addToCartSuccessPage(@RequestParam("skuId") Long skuId,
                                       Model model) {
        //重定向到成功页面。再次查询购物车数据即可
        CartItemVo cartItemVo = cartService.getCartItem(skuId);
        model.addAttribute("cartItem",cartItemVo);
        return "success";
    }

如果addCartItem接口直接返回success界面,那么浏览器地址栏的地址就是:

http://cart.gulimall.com/addCartItem?skuId=1&num=2

用户刷新浏览器,就会提交一个加购请求。

如果addCartItem接口重定向到其他接口,那么浏览器显示的地址就是:

http://cart.gulimall.com/addToCartSuccessPage.html?skuId=1

用户刷新页面,并不会导致加购的重复提交。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小手追梦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值