练手项目1笔记 day16

目标

  • 说出品优购购物车的实现思路
  • 运用cookie存储购物车
  • 编写购物车前端代码
  • 运用redis存储购物车

1. 购物车需求分析与解决方案

1. 需求分析

用户在商品详情页点击加入购物车,提交商品SKU编号和购买数量,添加到购物车,购物车展示页面如下:

在这里插入图片描述

2. 实现思路

购物车数据的存储结构如下

在这里插入图片描述

用户未登录时,将此购物车存入cookies,在用户登录的时候,将购物车数据存入redis,如果用户登录时,cookie中存在购物车,需要将cookie的购物车合并到redis存储。

3. 工程搭建

  1. 创建cart-interface,依赖pojo
  2. 创建cart-service,同其他service,tomcat端口9007,dubbo端口20887
  3. 创建cart-web,web.xml和spring配置参考其他web,tomcat端口为9107,拷贝页面资源和UserDetailsServiceImpl
  4. 将资源文件夹的Cookie工具类拷贝到common工程,该工程引入servlet-api依赖

4. 购物车实体类

购物车的明细其实就是订单的明细,只是没有订单id

在pojo的pojogroup创建购物车实体类

public class Cart implements Serializable {

    private String sellerId;// 商家ID
    private String sellerName;// 商家名称

    private List<TbOrderItem> orderItemList;//购物车明细
//getter/setter...

该类是对每个商家的购物车进行的封装。

2. Cookie 存储购物车

1. 需求分析

使用cookie存储购物车数据,服务层负责逻辑,控制层负责读写cookie

在服务层不要涉及到cookie操作

2. 服务接口层

  1. 服务层接口 cart-interface新建com.pinyougou.cart.service包,新建接口CartService
// cartList –> 原来得购物车列表   返回新得购物车列表
public List<Cart> addGoodsToCartList(List<Cart> cartList,Long itemId,Integer num);

3. 服务实现层

实现的思路:

//1.根据商品SKU ID查询SKU商品信息
//2.获取商家ID		
//3.根据商家ID判断购物车列表中是否存在该商家的购物车	-> Cart	
//4.如果购物车列表中不存在该商家的购物车 Cart = null
//4.1 新建购物车对象  new Cart()
//4.2 新建一个购物车明细对象设置商品id 数量  金额 tbOrderItem
//4.3 将新建的购物车对象添加到购物车列表	cartList.add()	
//5.如果购物车列表中存在该商家的购物车	Cart != null	
// 查询购物车明细列表中是否存在该商品
//5.1. 如果没有,新增购物车明细		
//5.2. 如果有,在原购物车明细上添加数量,更改金额

代码实现 cart-service工程创建CartServiceImpl

@Service
public class CartServiceImpl implements CartService {

  @Autowired
  private TbItemMapper itemMapper;

  @Override
  public List<Cart> addGoodsToCartList(List<Cart> cartList, Long itemId, Integer num) {
    // 1. 根据skuID查询商品明细sku的对象
    TbItem item = itemMapper.selectByPrimaryKey(itemId);
    if(item==null){
      throw new RuntimeException("商品不存在");
    }
    if(!"1".equals(item.getStatus())){
      throw new RuntimeException("商品状态不合法");
    }
    // 2. 根据SKU对象获取商家id
    String sellerId = item.getSellerId();
    // 3. 根据商家id在购物车列表中查询购物车对象
    Cart cart = findCartBySellerId(cartList, sellerId);
    if(cart==null){
      // 4. 如果购物车列表中不存在该商家的购物车
      // 4.1 创建一个新的购物车对象
      cart = new Cart();
      cart.setSellerId(sellerId);// 商家id
      cart.setSellerName(item.getSeller());//商家名称
      List<TbOrderItem> orderItemList = new ArrayList<>();//创建购物车明细列表
      // 创建新的购物车明细对象
      TbOrderItem orderItem = createOrderItem(item, num);
      orderItemList.add(orderItem);
      cart.setOrderItemList(orderItemList);
      // 4.2 将新的购物车对象添加到购物车列表中
      cartList.add(cart);
    }else{
      // 5. 如果购物车列表存在该商家的购物车
      // 判断该商品是否在该购物车的明细列表中存在
      TbOrderItem orderItem = findOrderItemByItemId(cart.getOrderItemList(), itemId);
      if(orderItem==null){
        // 5.1 如果不存在,创建新的购物车明细对象,并添加到该购物车的明细列表中
        orderItem = createOrderItem(item, num);
        cart.getOrderItemList().add(orderItem);
      }else{
        // 5.2 如果存在,在原有的数量上添加数量,并且更新金额
        orderItem.setNum(orderItem.getNum()+num);
        orderItem.setTotalFee(new BigDecimal(orderItem.getPrice().doubleValue()*orderItem.getNum()));
        // 当明细的数量小于等于0,移除该明细
        if(orderItem.getNum()<=0){
          cart.getOrderItemList().remove(orderItem);
        }
        // 当购物车的明细数量为0,在购物车列表中移除该购物车
        if(cart.getOrderItemList().size()==0){
          cartList.remove(cart);
        }
      }
    }
    return cartList;
  }

  // 根据商家id在购物车列表中查询购物车对象
  private Cart findCartBySellerId(List<Cart> cartList,String sellerId){
    for (Cart cart : cartList) {
      if(sellerId.equals(cart.getSellerId())){
        return cart;
      }
    }
    return null;
  }

  // 根据SKUid在购物车明细列表中查询购物车明细对象
  private TbOrderItem findOrderItemByItemId(List<TbOrderItem> orderItemList,Long itemId){
    for (TbOrderItem orderItem : orderItemList) {
      if(orderItem.getItemId().longValue()==itemId.longValue()){
        return orderItem;
      }
    }
    return null;
  }

  private TbOrderItem createOrderItem(TbItem item,Integer num){
    // 创建新的购物车明细对象
    TbOrderItem orderItem = new TbOrderItem();
    orderItem.setGoodsId(item.getGoodsId());
    orderItem.setItemId(item.getId());
    orderItem.setTitle(item.getTitle());
    orderItem.setPrice(item.getPrice());
    orderItem.setNum(num);
    orderItem.setPicPath(item.getImage());
    orderItem.setSellerId(item.getSellerId());
    orderItem.setTotalFee(new BigDecimal(item.getPrice().doubleValue()*num));
    return orderItem;
  }
}

4. 后端控制层

实现思路:

  1. 从cookie中取出购物车
  2. 向购物车添加商品
  3. 将购物车存入cookie

cart-web工程新建CartController.java

@RestController
@RequestMapping("/cart")
public class CartController {

    @Autowired
    private HttpServletRequest request;

    @Autowired
    private HttpServletResponse response;

    @Reference
    private CartService cartService;

    @RequestMapping("/findCartList")
    public List<Cart> findCartList(){
        // 从cookie中提取购物车
        String cartListString = CookieUtil.getCookieValue(request, "cartList", "UTF-8");
        if(cartListString==null || cartListString.equals("")){
            cartListString="[]";
        }
        List<Cart> cartList_cookie = JSON.parseArray(cartListString, Cart.class);
        return cartList_cookie;
    }

    @RequestMapping("/addGoodsToCartList")
    public Result addGoodsToCartList(Long itemId,Integer num){
        try {
            // 从cookie中提取购物车
            List<Cart> cartList = findCartList();
            // 调用服务方法操作购物车
            cartList = cartService.addGoodsToCartList(cartList, itemId, num);

            String cartListString = JSON.toJSONString(cartList);
            // 将新的购物车存入cookie
            CookieUtil.setCookie(request,response,"cartList",cartListString,3600*24,"UTF-8");
            return new Result(true,"存入购物车成功");
        } catch (Exception e) {
            e.printStackTrace();
            return new Result(false,"存入购物车失败");
        }

    }
}

浏览器测试

查看购物车:localhost:9105/cart/findCartList.do

添加商品到购物车:localhost:9105/cart/addGoodsToCartList.do?itemId=1369284&num=2

3. 购物车前端代码

1. 需求分析

实现购物车页面的展示和相关操作

在这里插入图片描述

可实现购物车列表、数量的增减与移除以及合计数统计

2. 购物车列表

1. 前端服务层

cart-web增加cartService.js

// 购物车服务层
app.service('cartService',function ($http) {
    // 购物车列表
    this.findCartList=function () {
        return $http.get('cart/findCartList.do');
    }
2. 前端控制层

cart-web增加cartController.js

// 购物车控制层
app.controller('cartController',function ($scope, cartService) {
    // 查询购物车列表
    $scope.findCartList = function () {
        cartService.findCartList().success(
            function (response) {
                $scope.cartList = response;
            }
        );
    };
3. 页面

修改cart.html,引入js

<script type="text/javascript" src="plugins/angularjs/angular.min.js">  </script>
<script type="text/javascript" src="js/base.js">  </script>
<script type="text/javascript" src="js/service/cartService.js">  </script>
<script type="text/javascript" src="js/controller/cartController.js">  </script> 

添加相关指令,指令控制器和调用初始化方法

<body ng-app="pinyougou" ng-controller="cartController" ng-init="findCartList()">

循环显示购物车列表

<div class="cart-main">
  <div class="yui3-g cart-th">
    <div class="yui3-u-1-4"><input type="checkbox" name="" id="" value="" /> 全部</div>
    <div class="yui3-u-1-4">商品</div>
    <div class="yui3-u-1-8">单价(元)</div>
    <div class="yui3-u-1-8">数量</div>
    <div class="yui3-u-1-8">小计(元)</div>
    <div class="yui3-u-1-8">操作</div>
  </div>
  <div class="cart-item-list" ng-repeat="cart in cartList">
    <div class="cart-shop">
      <input type="checkbox" name="" id="" value="" />
      <span class="shopname self">{{cart.sellerName}} 【商家名称】{{cart.sellerId}}</span>
    </div>
    <div class="cart-body">
      <div class="cart-list" ng-repeat="orderItem in cart.orderItemList">
        <ul class="goods-list yui3-g">
          <li class="yui3-u-1-24">
            <input type="checkbox" name="" id="" value="" />
          </li>
          <li class="yui3-u-11-24">
            <div class="good-item">
              <div class="item-img"><img src="{{orderItem.picPath}}" /></div>
              <div class="item-msg">{{orderItem.title}}</div>
            </div>
          </li>

          <li class="yui3-u-1-8"><span class="price">{{orderItem.price.toFixed(2)}}</span></li>
          <li class="yui3-u-1-8">
            <a href="javascript:void(0)" ng-click="addGoodsToCartList(orderItem.itemId,-1)" class="increment mins">-</a>
            <input autocomplete="off" type="text" value="1" minnum="1" class="itxt" ng-model="orderItem.num"/>
            <a href="javascript:void(0)" ng-click="addGoodsToCartList(orderItem.itemId,1)" class="increment plus">+</a>
          </li>
          <li class="yui3-u-1-8"><span class="sum">{{orderItem.totalFee.toFixed(2)}}</span></li>
          <li class="yui3-u-1-8">
            <a href="#none" ng-click="addGoodsToCartList(orderItem.itemId,-orderItem.num)">删除</a><br />
            <a href="#none">移到我的关注</a>
          </li>
        </ul>
      </div>
    </div>
  </div>
</div>

3. 购物车数量增减和移除

1. 前端服务层

cart-web的cartService.js

// 添加商品到购物车
this.addGoodsToCartList = function (itemId,num) {
  return $http.get('cart/addGoodsToCartList.do?itemId='+itemId+'&num='+num);
}
2. 前端控制层

cart-web的cartController.js

// 数量加减
$scope.addGoodsToCartList = function (itemId,num) {
  cartService.addGoodsToCartList(itemId,num).success(
    function (response) {
      if(response.success){
        // 如果成功,刷新列表
        $scope.findCartList();
      }else{
        alert(response.message);
      }
    }
  );
}
3. 页面

修改cart-web的cart.html,实现数量增减

见之前代码

4. 合计数

1. 前端服务层

cartService.js

// 求合计
this.sum = function (cartList) {
  var totalValue = {totalNum:0,totalMoney:0.00};
  for(var i=0;i<cartList.length;i++){
    var cart = cartList[i];
    for(var j=0;j<cart.orderItemList.length;j++){
      var orderItem = cart.orderItemList[j];//购物车明细
      totalValue.totalNum += orderItem.num;//累加数量
      totalValue.totalMoney += orderItem.totalFee;//累加金额
    }
  }
  return totalValue;
}
2. 前端控制层
// 查询购物车列表
$scope.findCartList = function () {
  cartService.findCartList().success(
    function (response) {
      $scope.cartList = response;
      $scope.totalValue = cartService.sum($scope.cartList);//求合计数
      /* sum();*/
    }
  );
};
3. 页面
<div class="toolbar">
  <div class="chosed">已选择<span>{{totalValue.totalNum}}</span>件商品</div>
  <div class="sumprice">
    <span><em>总价(不含运费) :</em><i class="summoney">¥{{totalValue.totalMoney.toFixed(2)}}</i></span>

4. Redis存储购物车

1. 需求分析

判断当前用户是否登录,如果未登录采用cookie存储,如果登录用redis存储。登陆后将cookie购物车与redis购物车合并,清除cookie购物车。

2. 获取当前登录人的账号

1. 配置文件

spring-security更改配置

去掉

<!--<http pattern="/cart/*.do" security="none"></http>-->

添加

<!--匿名角色-->
<intercept-url pattern="/cart/*.do" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/**" access="ROLE_USER"/>   

设置资源可以再不登录时可以访问。

此时登录人账号为anonymousUser

2. 代码实现

在cart-web的findCartList和addGoodsToCartList方法中,获取用户名

String name = SecurityContextHolder.getContext().getAuthentication().getName();
System.out.println("current registrant is: "+name);

3. 远程购物车的存取

1. 服务接口层

cart-interface中CartService.java定义方法

// 从redis中提取购物车列表
public List<Cart> findCartListFromRedis(String username);

// 将购物车列表存入redis
public void saveCartListToRedis(String username,List<Cart> cartList);
2. 服务实现层

cart-service中CartServiceImpl.java

@Autowired
private RedisTemplate redisTemplate;

@Override
public List<Cart> findCartListFromRedis(String username) {
  System.out.println("get the cart from redis "+username);
  List<Cart> cartList = (List<Cart>) redisTemplate.boundHashOps("cartList").get(username);
  if(cartList==null){
    cartList=new ArrayList<>();
  }
  return cartList;
}

@Override
public void saveCartListToRedis(String username, List<Cart> cartList) {
  System.out.println("put the cart to redis "+username);
  redisTemplate.boundHashOps("cartList").put(username,cartList);
}
3. 控制层

修改findCartList方法和addGoodsToCartList方法

@Reference(timeout = 6000)
private CartService cartService;

@RequestMapping("/findCartList")
public List<Cart> findCartList(){
  // 获取当前登录人
  String name = SecurityContextHolder.getContext().getAuthentication().getName();
  System.out.println("current registrant is: "+name);
  if(name.equals("anonymousUser")){//如果没登录
    // 从cookie中提取购物车
    System.out.println("get cart from cookie");
    String cartListString = CookieUtil.getCookieValue(request, "cartList", "UTF-8");
    if(cartListString==null || cartListString.equals("")){
      cartListString="[]";
    }
    List<Cart> cartList_cookie = JSON.parseArray(cartListString, Cart.class);
    return cartList_cookie;
  }else{//如果已登录
    List<Cart> cartList_redis = cartService.findCartListFromRedis(name);
    return cartList_redis;
  }

}

@RequestMapping("/addGoodsToCartList")
public Result addGoodsToCartList(Long itemId,Integer num){
  try {
    // 获取当前登录人
    String name = SecurityContextHolder.getContext().getAuthentication().getName();
    System.out.println("current registrant is: "+name);

    // 从cookie中提取购物车
    List<Cart> cartList = findCartList();
    // 调用服务方法操作购物车
    cartList = cartService.addGoodsToCartList(cartList, itemId, num);
    if(name.equals("anonymousUser")){//如果没登录
      String cartListString = JSON.toJSONString(cartList);
      // 将新的购物车存入cookie
      System.out.println("put cart to cookie");
      CookieUtil.setCookie(request,response,"cartList",cartListString,3600*24,"UTF-8");
    }else{// 登录
      cartService.saveCartListToRedis(name,cartList);
    }
    return new Result(true,"存入购物车成功");
  } catch (Exception e) {
    e.printStackTrace();
    return new Result(false,"存入购物车失败");
  }
}

为了避免调用远程服务超时,可以将过期时间改为6秒

4. 跳板页

创建跳板页:cart-web工程新建login.html,页面添加脚本

<script type="text/javascript">
    location.href='cart.html';
</script>

购物车页面链接到跳板页

<a href="login.html">登录</a> 

4. 购物车合并

1. 服务接口层
public List<Cart> mergeCartList(List<Cart> cartList1,List<Cart> cartList2);
2. 服务实现层

cart-service的CartServiceImpl

@Override
public List<Cart> mergeCartList(List<Cart> cartList1, List<Cart> cartList2) {
  // cartList1.addAll(cartList2); //不能简单合并
  for (Cart cart : cartList2) {
    for (TbOrderItem orderItem : cart.getOrderItemList()) {
      cartList1 = addGoodsToCartList(cartList1,orderItem.getItemId(),orderItem.getNum());
    }
  }
  return cartList1;
}
3. 控制层

修改findCartList方法

@RequestMapping("/findCartList")
public List<Cart> findCartList(){
  // 获取当前登录人
  String username = SecurityContextHolder.getContext().getAuthentication().getName();
  System.out.println("current registrant is: "+username);

  String cartListString = CookieUtil.getCookieValue(request, "cartList", "UTF-8");
  if(cartListString==null || cartListString.equals("")){
    cartListString="[]";
  }
  List<Cart> cartList_cookie = JSON.parseArray(cartListString, Cart.class);
  if(username.equals("anonymousUser")){//如果没登录
    // 从cookie中提取购物车
    System.out.println("get cart from cookie");

    return cartList_cookie;
  }else{//如果已登录
    // 获取redis购物车
    List<Cart> cartList_redis = cartService.findCartListFromRedis(username);
    if(cartList_cookie.size()>0){//判断本地购物车中存在数据
      // 得到合并后的购物车
      List<Cart> cartList = cartService.mergeCartList(cartList_cookie, cartList_redis);
      // 将合并后的购物车存入redis
      cartService.saveCartListToRedis(username,cartList);

      // 清除本地购物车
      util.CookieUtil.deleteCookie(request,response,"cartList");
      System.out.println("merge cart...");
      return cartList;
    }

    return cartList_redis;
  }

}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值