• 添加购物车:先拿到userid(登录则拿登陆了的,未登录检查cookie,cookie没有生成随机数),再把userid,skuid,num作为参数传到cartService.addCart(userId, skuId, num)。从购物车列表里面查数量。1、先去读缓存,如果缓存存在这个cartkey,则将ttl过期时间加10秒,不存在的话就通过userid拿到购物车列表,再用map保存(skuid,cartinfo)。再hmset(cartkey,map)。2、再看数据库,如果cart_info表已经有这个skuid,userid对应的信息,则是更新操作,cartInfoExists.setSkuNum(cartInfoExists.getSkuNum()+num);没有的话就是插入操作cartInfoMapper.insertSelective(cartInfo);3、最后将userid及对应的cartmap(skuid,cartInfo)加载到最新的缓存里面

    • controller

      @PostMapping("addToCart")
      @LoginRequire(autoRedirect = false)
      public  String  addCart(@RequestParam("skuId") String skuId, @RequestParam("num") int num, HttpServletRequest request, HttpServletResponse response){
          String userId =(String) request.getAttribute("userId");
      
          if(userId==null){
              //如果用户未登录  检查cookie用户是否有token 如果有token  用token 作为id 加购物车 如果没有生成一个新的token放入cookie
              userId = CookieUtil.getCookieValue(request, "user_tmp_id", false);
              if(userId==null){
                  userId = UUID.randomUUID().toString();
                  CookieUtil.setCookie(request,response,"user_tmp_id",userId,60*60*24*7,false);
              }
      
          }
          CartInfo cartInfo = cartService.addCart(userId, skuId, num);//实例化,参数
      //userid用户名,skuId订单名,num数量
          request.setAttribute("cartInfo",cartInfo);
          request.setAttribute("num",num);
      
          return "success";
      }
    • service

      @Override
      public CartInfo addCart(String userId, String skuId, Integer num) {
          // 为了防止 更新购物车前 缓存过期
          loadCartCacheIfNotExists(  userId) ;
          // 加数据库
          // 尝试取出已有的数据
          CartInfo cartInfoQuery=new CartInfo();
          cartInfoQuery.setSkuId(skuId);
          cartInfoQuery.setUserId(userId);
          CartInfo cartInfoExists=null;
            cartInfoExists = cartInfoMapper.selectOne(cartInfoQuery);
          SkuInfo skuInfo = manageService.getSkuInfo(skuId);
          if(cartInfoExists!=null){//如果有  把数量更新 update
              cartInfoExists.setSkuName(skuInfo.getSkuName());
              cartInfoExists.setCartPrice(skuInfo.getPrice());
              cartInfoExists.setSkuNum(cartInfoExists.getSkuNum()+num);
              cartInfoExists.setImgUrl(skuInfo.getSkuDefaultImg());
              cartInfoMapper.updateByPrimaryKeySelective(cartInfoExists);
          }else{// 如果没有insert
              CartInfo cartInfo = new CartInfo();
              cartInfo.setSkuId(skuId);
              cartInfo.setUserId(userId);
              cartInfo.setSkuNum(num);
              cartInfo.setImgUrl(skuInfo.getSkuDefaultImg());
              cartInfo.setSkuName(skuInfo.getSkuName());
              cartInfo.setCartPrice(skuInfo.getPrice());
              cartInfo.setSkuPrice(skuInfo.getPrice());
      
              cartInfoMapper.insertSelective(cartInfo);
              cartInfoExists=cartInfo;
          }
      
          loadCartCache(userId);
            return cartInfoExists;
      }
    • 缓存数据库一致性操作

      public void  loadCartCacheIfNotExists(String userId){//缓存数据库的一致性
          String cartkey="cart:"+userId+":info";
          Jedis jedis = redisUtil.getJedis();
          Long ttl = jedis.ttl(cartkey);
          int ttlInt = ttl.intValue();
          jedis.expire(cartkey,ttlInt+10);
          Boolean exists = jedis.exists(cartkey);
          jedis.close();
          if( !exists){//缓存没有的情况
               loadCartCache( userId);//查数据库,同时加载到缓存里面
          }
      
      }
      
* 缓存没有的情况下,查数据库 ,同时加载到缓存中

```java
/**
     *  缓存没有查数据库 ,同时加载到缓存中
     * @param userId
     * @return
     */
    public List<CartInfo>  loadCartCache(String userId){
        // 读取数据库
        List<CartInfo> cartInfoList = cartInfoMapper.selectCartListWithSkuPrice(userId);
        //加载到缓存中
        //为了方便插入redis  把list --> map
        if(cartInfoList!=null&&cartInfoList.size()>0) {
            Map<String, String> cartMap = new HashMap<>();
            for (CartInfo cartInfo : cartInfoList) {
                cartMap.put(cartInfo.getSkuId(), JSON.toJSONString(cartInfo));
            }
            Jedis jedis = redisUtil.getJedis();
            String cartKey = "cart:" + userId + ":info";
            jedis.del(cartKey);
            jedis.hmset(cartKey, cartMap);  // hash
            jedis.expire(cartKey, 60 * 60 * 24);
            jedis.close();
        }
        return  cartInfoList;

    }
  • 购物车列表:有登陆,登录之前存在临时id,则进行合并,不需要的话则直接拿到userid的购物车列表。未登录的话直接拿到临时id的购物车列表。查询购物车列表的操作是先取缓存,拿到cartInfoJsonList,命中缓存后将遍历通过cartJson拿到cartInfo,加入cartList。并通过id的大小倒叙返回。如果缓存中没拿到,先查询数据库拿到userid对应的所有的cartInfoList,再将将其包装为map(skuid,cartInfo),其加载到缓存中String cartKey = "cart:" + userId + ":info"; jedis.hmset(cartKey, cartMap);。

    • controller

      @GetMapping("cartList")
      @LoginRequire(autoRedirect = false)
      public  String  cartList(HttpServletRequest request){
          String userId =(String) request.getAttribute("userId");  //查看用户登录id
      
          if(userId!=null){   //有登录
              List<CartInfo> cartList=null;   //如果登录前(未登录)时,存在临时购物车 ,要考虑合并
              String userTmpId=CookieUtil.getCookieValue(request, "user_tmp_id", false); //取临时id
              if(userTmpId!=null){
                  List<CartInfo> cartTempList =  cartService.cartList(  userTmpId);  //如果有临时id ,查是否有临时购物车
                  if( cartTempList!=null&&cartTempList.size()>0){
                      cartList=  cartService.mergeCartList(userId,userTmpId); // 如果有临时购物车 ,那么进行合并 ,并且获得合并后的购物车列表
                  }
              }
              if(cartList==null||cartList.size()==0){
                  cartList =  cartService.cartList(  userId);  //如果不需要合并 ,再取登录后的购物车
              }
              request.setAttribute("cartList",cartList);
          }else {   //未登录 直接取临时购物车
              String userTmpId=CookieUtil.getCookieValue(request, "user_tmp_id", false);
              if(userTmpId!=null) {
                  List<CartInfo> cartTempList = cartService.cartList(userTmpId);
                  request.setAttribute("cartList",cartTempList);
              }
      
          }
      
          return "cartList";
      }
    • service

      @Override
      public List<CartInfo> cartList(String userId) {
          //先查缓存
          Jedis jedis = redisUtil.getJedis();
          String cartKey="cart:"+userId+":info";
          List<String> cartJsonList = jedis.hvals(cartKey);//取缓存
          List<CartInfo> cartList=new ArrayList<>();
          if(cartJsonList!=null&&cartJsonList.size()>0){  //缓存命中
              for (String cartJson : cartJsonList) {
                  CartInfo cartInfo = JSON.parseObject(cartJson, CartInfo.class);
                  cartList.add(cartInfo);
              }
              cartList.sort(new Comparator<CartInfo>() {//根据时间排序(id的一个倒叙排序)
                  @Override
                  public int compare(CartInfo o1, CartInfo o2) {
                      return o2.getId().compareTo(o1.getId());
                  }
              });
              return    cartList;
          }else {
              //缓存未命中  
              return loadCartCache(userId);//先查数据库 ,再加载到缓存中
          }
      
      }
      /**
       * 合并购物车
       * @param userIdDest
       * @param userIdOrig
       * @return
       */
      @Override
      public List<CartInfo> mergeCartList(String userIdDest, String userIdOrig) {
          //1 先做合并
          cartInfoMapper.mergeCartList(userIdDest,userIdOrig);//数据库
          // 2 合并后把临时购物车删除
          CartInfo cartInfo = new CartInfo();
          cartInfo.setUserId(userIdOrig);
          cartInfoMapper.delete(cartInfo);
          Jedis jedis = redisUtil.getJedis();
          jedis.del("cart:"+userIdOrig+":info");
          jedis.close();
          // 3 重新读取数据 加载缓存
          List<CartInfo> cartInfoList = loadCartCache(userIdDest);
      
          return cartInfoList;
      }
  • 购物车列表的选中

    • controller

      @PostMapping("checkCart")
      @LoginRequire(autoRedirect = false)
      @ResponseBody
      public void checkCart(@RequestParam("isChecked") String isChecked ,@RequestParam("skuId") String skuId,HttpServletRequest request){
          String userId =(String)request.getAttribute("userId");
          if(userId==null){
              userId = CookieUtil.getCookieValue(request, "user_tmp_id", false);
          }
      
          cartService.checkCart(userId,skuId,isChecked);
      }
    • service

      @Override
      public void checkCart(String userId, String skuId, String isChecked) {
          loadCartCacheIfNotExists(userId);// 检查一下缓存是否存在 避免因为缓存失效造成 缓存和数据库不一致
      
          //  isCheck数据 值保存在缓存中
          //保存标志
          String cartKey = "cart:" + userId + ":info";
          Jedis jedis = redisUtil.getJedis();
          String cartInfoJson = jedis.hget(cartKey, skuId);
          CartInfo cartInfo = JSON.parseObject(cartInfoJson, CartInfo.class);
          cartInfo.setIsChecked(isChecked);
          String cartInfoJsonNew = JSON.toJSONString(cartInfo);
          jedis.hset(cartKey,skuId,cartInfoJsonNew);
          // 为了订单结账 把所有勾中的商品单独 在存放到一个checked购物车中
          String cartCheckedKey = "cart:" + userId + ":checked";
          if(isChecked.equals("1")){  //勾中加入到待结账购物车中, 取消勾中从待结账购物车中删除
              jedis.hset(cartCheckedKey,skuId,cartInfoJsonNew);
              jedis.expire(cartCheckedKey,60*60);
          }else{
              jedis.hdel(cartCheckedKey,skuId);
          }
          jedis.close();
      
      }