谷粒商城-购物车2

购物车

一.配置购物车环境

1.点击 我的购物车

	从商品的首页index.html,跳转到购物车的页面cartList.html
	<img src="/static/index/img/img_15.png"/>
	<span><a href="http://cart.gulimall.com/cart.html">我的购物车</a></span>

2.点击 加入购物车

从商品需求页面item.html,到购物车添加成功页面success.html
<a href="http://cart.gulimall.com/addCart">加入购物车</a>

3.点击 去购物车结算

从购物车添加成功页面success.html,到购物车列表cartList.html,即我的购物车
<a class="btn-addtocart"  href="http://cart.gulimall.com/cart.html"id="GotoShoppingCart"><b></b>去购物车结算</a>

4.点击 查看商品详情

从购物车添加成功页面success.html,到商品详情页面item.html
   <a class="btn-tobback" href="http://item.gulimall.com/11.html">查看商品详情</a>

5.点击 去结算

从购物车到结算界面

二. 业务操作

1.添加购物车

使用的技术,feign远程,线程池及异步编排CompletableFuture,redis。
逻辑:点击商品后,获取商品skuId,数量。根据skuId、数量获取商品信息
使用异步编排实现使用同一线程

1).controller

CartItem cartItem = cartService.addCart(skuId, num);

2).CartServiceImpl分3步(a、b、c)

a.远程查询当前要添加的商品信息。product
//1.远程查询当前要添加的商品信息。product的SkuInfo
R r = productFeignService.info(skuId);
SkuInfoVo data = r.getData("skuInfo", new TypeReference<SkuInfoVo>() {
});
b.商品信息添加到购物车
    cartItem.setSkuId(skuId);
    cartItem.setCheck(true);
    cartItem.setImage(data.getSkuDefaultImg());
    cartItem.setTitle(data.getSkuTitle());
    cartItem.setPrice(data.getPrice());
    cartItem.setCount(num);
c.远程查询属组合信息,product
    //3.远程查询属组合信息.product的SkuSaleAttrValue
    List<String> values = productFeignService.getSkuSaleAttrValues(skuId);
    cartItem.setSkuAttr(values);
d.使用异步编排CompletableFuture,使得2个远程任务使用一个线程
e.所有任务完成,数据才放入redis
CompletableFuture.allOf(futureSkuInfo,futureSkuSaleAttrValues).get();
String s = JSON.toJSONString(cartItem);
cartOps.put(skuId.toString(),s);
f.获取操作的购物车(抽取的方法)
   //获取 操作的购物车。
    private BoundHashOperations<String, Object, Object> getCartOps() {
        UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
        String cartKey="";
        if (userInfoTo.getUserId()!=null){
            //登录成功。登录后,用户id不为空
            cartKey= CartConstant.CART_PREFIX+userInfoTo.getUserId();
            System.out.println("用户登录后的购物车key: "+cartKey);
        }else {
            //登录失败
            cartKey=CartConstant.CART_PREFIX+userInfoTo.getUserKey();
            System.out.println("用户未登录后的临时用户key: "+cartKey);
        }

        //因为数据是hash
        BoundHashOperations<String, Object, Object> operations = redisTemplate.boundHashOps(cartKey);
        return operations;
    }
g.组合为最终的CartServiceImpl的添加购物车的方法
    //添加购物车
    @Override
    public CartItem addCart(Long skuId, Integer num) throws ExecutionException, InterruptedException {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        CartItem cartItem = new CartItem();
        //异步编排,作用:多个任务使用同一线程
        CompletableFuture<Void> futureSkuInfo = CompletableFuture.runAsync(() -> {
            //1.远程查询当前要添加的商品信息。product的SkuInfo
            R r = productFeignService.info(skuId);
            SkuInfoVo data = r.getData("skuInfo", new TypeReference<SkuInfoVo>() {
            });
            //2.商品信息添加到购物车
            cartItem.setSkuId(skuId);
            cartItem.setCheck(true);
            cartItem.setImage(data.getSkuDefaultImg());
            cartItem.setTitle(data.getSkuTitle());
            cartItem.setPrice(data.getPrice());
            cartItem.setCount(num);
        }, executor);

        CompletableFuture<Void> futureSkuSaleAttrValues = CompletableFuture.runAsync(() -> {
            //3.远程查询属组合信息.product的SkuSaleAttrValue
            List<String> values = productFeignService.getSkuSaleAttrValues(skuId);
            cartItem.setSkuAttr(values);
        }, executor);

        CompletableFuture.allOf(futureSkuInfo,futureSkuSaleAttrValues).get();
        String s = JSON.toJSONString(cartItem);
        cartOps.put(skuId.toString(),s);
        return cartItem;
    }

3). 上面2)的结果

在未登录,登录下,分表添加商品到购物车,会在redis存不同的数据。
在这里插入图片描述

4).添加购物车细节

添加购物车分两种情况

    @Override
    public CartItem addCart(Long skuId, Integer num) throws ExecutionException, InterruptedException {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();        
第1种购物车有此商品
//如果购物车有此商品,修改数量即可。没有就添加新商品
	 String res = (String) cartOps.get(skuId.toString());
	 if (StringUtils.isEmpty(res)){
            //购物车无此商品
            CartItem cartItem = new CartItem();
            //异步编排,作用:多个任务使用同一线程
            CompletableFuture<Void> futureSkuInfo = CompletableFuture.runAsync(() -> {
                //1.远程查询当前要添加的商品信息。product的SkuInfo
                R r = productFeignService.info(skuId);
                SkuInfoVo data = r.getData("skuInfo", new TypeReference<SkuInfoVo>() {
                });
                //2.商品信息添加到购物车
                cartItem.setSkuId(skuId);
                cartItem.setCheck(true);
                cartItem.setImage(data.getSkuDefaultImg());
                cartItem.setTitle(data.getSkuTitle());
                cartItem.setPrice(data.getPrice());
                cartItem.setCount(num);
            }, executor);

            CompletableFuture<Void> futureSkuSaleAttrValues = CompletableFuture.runAsync(() -> {
                //3.远程查询属组合信息.product的SkuSaleAttrValue
                List<String> values = productFeignService.getSkuSaleAttrValues(skuId);
                cartItem.setSkuAttr(values);
            }, executor);

            CompletableFuture.allOf(futureSkuInfo,futureSkuSaleAttrValues).get();
            String s = JSON.toJSONString(cartItem);
            cartOps.put(skuId.toString(),s);
            return cartItem;
        }
第2种购物车没有商品
	else{
            //购物车有此商品,修改数量
            CartItem cartItem = JSON.parseObject(res, CartItem.class);
            cartItem.setCount(cartItem.getCount()+num);
            cartOps.put(skuId.toString(),JSON.toJSONString(cartItem));
            return cartItem;
        }

5).问题描述:刷新加入购物车成功页面success.html,不重复添加数据到购物车。

解决方法:RedirectAttributes。使用重定向。
	 RedirectAttributes:重定向并保存数据
      	 addFlashAttribute:把数据放在session里面,可以在页面取出,但只能取一次
     	 addAttribute:将数据放在url后面,可重复取数据
a.更新逻辑:刷新成功页面success.html,跳转到某个方法。此方法从redis获取数据,再显示到购物车页面。
b.代码的controller
    //添加商品到购物车
    @GetMapping("/addCart")
    public String addCart(@RequestParam("skuId") Long skuId,
                          @RequestParam("num") Integer num,
                          RedirectAttributes redirectAttributes) throws ExecutionException, InterruptedException {
        //1.快速得到用户信息。id,user-key
        System.out.println("添加到购物车!");
        cartService.addCart(skuId, num);
//        model.addAttribute("skuId", skuId);
        redirectAttributes.addAttribute("skuId", skuId);
        return "redirect:http://cart.gulimall.com/addToCartSuccess.html";
    }

    //获取购物车的购物项
    @GetMapping("addToCartSuccess.html")
    public String addToCartSuccessPage(@RequestParam("skuId") Long skuId, Model model) {
        //重定向到成功页面,再次查询购物车数据即可
        CartItem item = cartService.getCartItem(skuId);
        model.addAttribute("item", item);
        return "success";
    }
c.service实现类
    //获取购物车中某个购物项
    @Override
    public CartItem getCartItem(Long skuId) {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        String res = (String) cartOps.get(skuId.toString());
        CartItem cartItem = JSON.parseObject(res, CartItem.class);
        return cartItem;
    }

2.获取 并 合并购物车

0).合并购物车:登录、未登录的合并

1). CartController-获取合并后购物车信息

    @GetMapping("/cart.html")
    public String cartPage(Model model) throws ExecutionException, InterruptedException {
        //1.快速得到用户信息。id,user-key
        //获取、合并购物车信息
        Cart cart = cartService.getCart();
        model.addAttribute("cart", cart);
        System.out.println("合并后购物车信息  size= "+cart.getCountNum());
        return "cartList";
    }

2).实现类-获取、合并购物车信息

    //获取购物车
    //未登录时,把商品数据服务购物车。
    //登录后,把登录的数据和临时购物车合并 
    @Override
    public Cart getCart() throws ExecutionException, InterruptedException {
        Cart cart = new Cart();
        UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
        if (userInfoTo.getUserId()!=null){
            //1.已登录
            String cartKey = CartConstant.CART_PREFIX + userInfoTo.getUserId();
            System.out.println("用户登录后的购物车key: "+cartKey);
            //1.2.如果临时购物车的数据还没有合并。合并购物车
            String tempCartKey = CartConstant.CART_PREFIX + userInfoTo.getUserKey();
            List<CartItem> tempCartItems = getCartItems(tempCartKey);
            if (tempCartItems!=null){
                System.out.println("临时购物车数据 size= "+tempCartItems.size());
                //临时购物车有数据,需要合并.
                for (CartItem item : tempCartItems) {
                  addCart(item.getSkuId(), item.getCount());
                }
                //合并后,清除临时购物车数据
                clearCart(tempCartKey);
            }
            //1.3.登录的购物车的购物项[包含 合并来的临时购物车、登录购物车的购物项]
            List<CartItem> cartItems = getCartItems(cartKey);
            if (cartItems!=null){
                System.out.println("合并后购物车数据 size= "+cartItems.size());
                cart.setItems(cartItems);
            }
        }else {
            //2.未登录
            String cartKey = CartConstant.CART_PREFIX + userInfoTo.getUserKey();
            System.out.println("用户未登录后的临时用户key: "+cartKey);
            //获取临时购物车的所有购物项
            List<CartItem> cartItems = getCartItems(cartKey);
            cart.setItems(cartItems);
        }
        return cart;
    }

3).实现类-登录后,合并购物车后,清除临时购物车数据

    //清空购物车
    @Override
    public void clearCart(String cartKey) {
        redisTemplate.delete(cartKey);
    }
		2).的合并后,清除购物车
        //合并后,清除临时购物车数据
        clearCart(tempCartKey);

4).页面数据的渲染

 <div class="One_ShopCon">
            <h1 th:if="${cart.items ==null}">购物车还没商品,<a href="http://gulimall.com/">去购物</a></h1>
            <ul th:if="${cart.items !=null}">
                <li th:each="item :${cart.items}">
                    <div>
                    </div>
                    <div>
                        <ol>
                            <li><input type="checkbox" class="check" th:checked="${item.check}"></li>
                            <li>
                                <dt><img th:src="${item.image}" alt=""></dt>
                                <dd style="width: 300px">
                                    <p>
                                        <span th:text="${item.title}">TCL 55A950C 55英寸32核</span>
                                        </br>
                                        <span th:each="attr:${item.skuAttr}" th:text="${attr}">人工智能 HDR曲面超薄4K电视金属机</span>
                                    </p>
                                </dd>
                            </li>
                            <li>
                                <p class="dj" th:text="''+${#numbers.formatDecimal(item.price,3,2)}">¥4599.00</p>
                            </li>
                            <li>
                                <p>
                                    <span>-</span>
                                    <span th:text="${item.count}">5</span>
                                    <span>+</span>
                                </p>

                            </li>
                            <li style="font-weight:bold">
                                <!--								<p class="zj" th:text="'¥'+${#numbers.formatDecimal(item.totalPrice,3,2)}">¥22995.00</p></li>-->
                                <p class="zj">¥ [[${#numbers.formatDecimal(item.totalPrice,3,2)}]]</p></li>
                            <li>
                                <p>删除</p>
                            </li>
                        </ol>
                    </div>
                </li>
            </ul>
        </div>
            <div>
                <ol>
                    <li>总价:<span style="color:#e64346;font-weight:bold;font-size:16px;" class="fnt">[[${#numbers.formatDecimal(cart.totalAmount,3,2)}]]</span>
                    </li>
                    <li>优惠:<span></span>[[${#numbers.formatDecimal(cart.reduce,1,2)}]]</li>
                </ol>
            </div>

3. 购物车的选中功能

1).选中购物项功能

逻辑:选中购物项后,后台处理后,再重定向到购物车
a.页面渲染,cartList.html
  <li>
  	<input type="checkbox" class="itemCheck" th:attr="skuId=${item.skuId}" th:checked="${item.check}">
  </li>
    //点击选择框
    $(".itemCheck").click(function () {
        var skuId = $(this).attr("skuId")
        var check = $(this).prop("checked")
        location.href = "http://cart.gulimall.com/checkItem?skuId="+skuId+"&check="+(check?1:0);
    })

b.CartController
    //勾选购物车购物项
    @GetMapping("/checkItem")
    public String checkItem(@RequestParam("skuId") Long skuId, @RequestParam("check") Integer check) {
        cartService.checkItem(skuId, check);
        return "redirect:http://cart.gulimall.com/cart.html";
    }
c.实现类-CartServiceImpl
    //勾选购物车购物项
    @Override
    public void checkItem(Long skuId, Integer check) {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        //获取购物车数据
        CartItem cartItem = getCartItem(skuId);
        cartItem.setCheck(check==1?true:false);
        String s = JSON.toJSONString(cartItem);
        //再把数据放入购物车
        cartOps.put(skuId.toString(),s);
    }

2).修改购物车商品的数量

逻辑:点击购物项的数量+、-。处理后,重定向到购物车页面
a.页面渲染
   <li>
     <p th:attr="skuId=${item.skuId}">
       <span class="countOpsBtn">-</span>
       <span class="countOpsNum" th:text="${item.count}">5</span>
       <span class="countOpsBtn">+</span>
    </p>
  </li>
    //购物项的加减
    $(".countOpsBtn").click(function () {
        //1.skuId
        var skuId = $(this).parent().attr("skuId");
        var num = $(this).parent().find(".countOpsNum").text();
        location.href = "http://cart.gulimall.com/countItem?skuId=" + skuId + "&num=" + num;

    })
b.CartController
    //修改购物项的数量
    @GetMapping("/countItem")
    public String countItem(@RequestParam("skuId") Long skuId, @RequestParam("num") Integer num) {
        cartService.ChangeCountItem(skuId, num);
        return "redirect:http://cart.gulimall.com/cart.html";
    }
c.实现类-CartServiceImpl
    //修改购物项的数量
    @Override
    public void ChangeCountItem(Long skuId, Integer num) {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        //获取购物车数据
        CartItem cartItem = getCartItem(skuId);
        cartItem.setCount(num);
        String s = JSON.toJSONString(cartItem);
        //再把数据放入购物车
        cartOps.put(skuId.toString(),s);
    }

3.删除购物项

逻辑:点击删除购物项目,后台处理后,重定向到购物车。
a.页面渲染,cartList.html
   <li>
      <p class="deleteItemBtn" th:attr="skuId=${item.skuId}">删除</p>
   </li>
<div class="One_isDel">
    <p>
        <span >删除</span><span><img src="/static/cart/img/错误.png" alt=""></span>
    </p>
    <div>
        <dl>
            <dt><img src="/static/cart/img/感叹三角形 (2).png" alt=""></dt>
            <dd>
                <li>删除商品?</li>
                <li>您可以选择移到关注,或删除商品。</li>
            </dd>
        </dl>
    </div>
    <div>
        <button type="button" onclick="deleteItem()">删除</button>
    </div>
</div>
    //删除购物项
    var deleteId=0;
    $(".deleteItemBtn").click(function () {
        deleteId = $(this).attr("skuId")
    })

    function deleteItem(){
        location.href = "http://cart.gulimall.com/deleteItem?skuId=" + deleteId;
    }
b.CartController
//删除购物项
@GetMapping("/deleteItem")
public String countItem(@RequestParam("skuId") Long skuId) {
    cartService.deleteItem(skuId);
    return "redirect:http://cart.gulimall.com/cart.html";
}
c.实现类-CartServiceImpl
    //删除购物项
    @Override
    public void deleteItem(Long skuId) {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        Long delete = cartOps.delete(skuId.toString());
        if (delete==1){
            System.out.println("删除购物项菜单成功。。。");
        }else {
            System.out.println("删除购物项菜单失败!!!");
        }
    }
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值