最近在公司做的一个项目我负责的模块之一,做的时候可谓是踩了无数个坑,所以我决定重写捋一捋然后写出来,在为了下次再做类似业务的时候更加方便的同时,希望也能帮到大家。里面很多字段都是我举例随便编的,就像一个现编的小demo,不涉及公司任何事务。

一、未登录的购物车实现:

新建购物车目录对象: CartItem 属性 int productId; int count;date updateTime;

​ CartItemVO 属性 TProduct product; int count;date updateTime;

(一)加入购物车:

1)当用户访问客 户端时,判断其uuid是否存在,不存在的话,则创建一个uuid给当前用户;

2)在添加商品的时候, 执行addToCart方法把uuid作为key和cartItem作为value存入redis 此时方法中有三种情况:

①当前redis中没有此uuid对应的集合 :

new一个list,再创建CartItem对象存入到该集合中,再存入到redis中然后设置超时时间为一个星期 ; 即return ResultVO(true,”添加购物车成功“,list)

②当前redis中有uuid对应的集合 :

先遍历集合list

1)如果集合中有和当前要存的product的id是一样的,就将其product的数量和更新时间更新;

   cartItemVO.setUpdateTime(cartItem.getUpdateTime());cartItemVO.setCount(cartItem.getCount());

再把list重新更新到redis中(覆盖原来list);

2)如果集合中的每一条CartItem的produtId都和传进来的productId不一样的话,就表示是一个新商品,则重新创建一个CartItem,存入list集合,更新redis;

上面完成后,在controller中new一个cookie( key:user:cart value:uuid) 发送给客户端 response.addCookie,完成添加;

(二)查看购物车

Controller:getCart(@CookieValue(name=CookieConstant.USER_CART,required = false)String uuid,HttpServletRequest request,HttpServletResponse response)://获取购物车中的信息
Service:
先写一个方法:
cartService.getCart(String key)  //此方法通过传入的cart_uuid去redis中查找对应的value也就是List<Item>
再通过:
cartService.getCartVO(String key) //在此方法内先调用上面的方法获得cartItem集合,再遍历集合,拿到每个productId所对应的的TProduct对象,再存入List<cartItemVO>中
这样,就可以拿到cart展示所需所有信息

getCartVO(String key)中需要注意:

对于通过productId查询对应商品,先做一个商品预热,遵循2/8定律,把热门商品先加载到缓存中,即查询过程是:
1、先去redis中拿

                String product_key = RedisUtil.getProductKey(productId);``Object o = redisTemplate.opsForValue().get(product_key);`TProduct product = null;

此时注意要加上分布式锁避免数据库压力过大导致缓存击穿

2、若redis中没有该商品对象,去数据库查然后存入cartItemVO然后再存入redis中

                if(o==null){
​                    product = tProductMapper.selectByPrimaryKey(productId);​                    cartItemVO.setProduct(product);​                    redisTemplate.opsForValue().set(product_key,product);`
​                }else{
​                    product = (TProduct) o;
​                    cartItemVO.setProduct(product);
​                }

之后记得更新一下redis中该key的超时时间

redisTemplate.expire(product_key,30,TimeUnit.MINUTES);

再把该对象存入到集合中,并更新redis

           cartItemVOS.add(cartItemVO);

让商品按照更新时间进行排序

        //让cartItems处于排序状态  -1  0  1Collections.sort(cartItems, new Comparator<CartItem>() {@Overridepublic int compare(CartItem o1, CartItem o2) {return (int)(o2.getUpdateTime().getTime()-o1.getUpdateTime().getTime());}});

这样,才算真正完成了查看购物车信息的功能。

(三)改变购物车中商品的数量

Controller:public ResultVO resetCart(@PathVariable Long productId,@PathVariable Integer count,@CookieValue(name=CookieConstant.USER_CART,required =                                false)String uuid,HttpServletRequest request,HttpServletResponse response)
//通过uuid拿到对应的key值,然后传入并调用 resetCart方法
Service:public ResultVO resetCart(Long productId, Integer count, String key)
//通过key在redis中找到对应的value即List<cartItem>,然后遍历通过productId找到要修改的商品,重新set其count值
//重置数量后
redistemplete.opsForValue.set(key,List<CartItems>)
//进行覆盖
//重置过期时间

(四)删除购物车中的商品

这一部分很简单,通过uuid找到List,然后再遍历通过productId找到对应商品删除掉就可以了,删除完要更新redis数据和重新设置过期时间。

以上就是未登录购物车CRUD的实现

=======================================================================================

二、已登录的购物车的实现

一开始拟定了两种实现已登录购物车CRUD的方案:

方案一:将商品的CRUD存入数据库中

弊端:当每一次修改或则删除商品时,都要访问db,会导致db压力过大,db可能会崩

方案二:将添加的商品存到redis缓存中,redis也有持久化机制,可以订制一个定时任务,定时往数据库中添加购物车中的商品信息

为了减轻数据库的压力,我选择了第二种方案;

具体业务实现逻辑:

(一)添加商品到购物车

同样在addToCart中在将商品添加到购物车之前调用userService中的IsLogin方法进行判断用户是否登录,

如果未登录的话,就按未登录购物车添加商品流程走;

如果是已登录状态的话,就可以拿到当前用户的userId,然后以User:Cart:uid作为key, CartItem作为value存入redis中。

如何判断是否已登录:在购物车商品的添加修改和删除都需要判断是否已经登录,所以通过拦截器来达到只需要一次请求的判断就可以的通用的效果

一次请求request到拦截器(HandlerInterceptor),验证是否登录(拦截器在preHandler方法中对cookie进行遍历,得到key为USER_TOKEN的cookie的value,即为用户的uuid,再通过调用Uerservice中的checkLogin来验证用户是否登录),无论是否登录,都予以放行,拦截器收到请求后request.setAttribute(“user”,user),

然后requset到达添加购物车接口request.getAttribute(“user”)就可以拿到user对象

得到的user对象如果是空的,就代表未登录,非空则为已登录

(二)增删改购物车

有上面的添加购物车为例子,再执行业务前判断下用户是否已登陆。若已登录的话,就将用户的uid代替uuid生成属于已登录的用户的专属key值,把对应的cartItem作为value存到以此key为键的redis中,实现流程都是一样的,就不再重复描述了。

最后,对已登录和未登录的购物车进行合并

合并的时机:在登录成功的时候,会把(USER_TOKEN,用户的uuid)存入redis中,这时调用CartController中的merge方法去实现对已登录和未登录的购物车进行合并

为什么调用的是Controller层的方法实现合并呢?

因为Controller层的merge方法声明是这样的:

public ResultVO merge(@CookieValue(name=CookieConstant.USER_CART,required = false)String uuid,HttpServletRequest request,HttpServletResponse response){方法体}

在参数列表中有当前未登录的cart所对应的的cookie值,

如果通过调用service层的merge方法

cartService.merge(loginedKey,noLoginKey);

要去拿cookie的值的话是要改动很多的,要在checkLogin()里拿到未登录的cart的uuid

所以,我决定用httpClient去直接调用Controller中的merge,它会自动拿到Cookie

Controller中merge的具体实现:

1.通过从拦截器放行过来的request中get到user对象是否为空来判断用户是否登录

2.如果没有登录的话则直接返回一个ResultVO(false);

3.如果已登录的话则调用cartService中的merge方法进行购物车合并

public ResultVO merge(@CookieValue(name=CookieConstant.USER_CART,required = false)String                         uuid,HttpServletRequest request,HttpServletResponse response)//该方法中提供已登录的loginedKey,和未登录用户的noLoginKey

4.合并完成后,通过未登录对应的uuid删除对应的cookie

cartService中merge方法的具体实现:

//1.获取已登录状态下的购物车List<CartItem> loginedCartItems = (List<CartItem>)                         redisTemplate.opsForValue().get(loginedKey);
//2.获取未登录状态下的购物车List<CartItem> noLoginCartItems = (List<CartItem>) redisTemplate.opsForValue().get(noLoginKey);
//3.未登录状态下没有购物车情况if(noLoginCartItems==null||noLoginCartItems.size()==0){return new ResultVO(true,"未登录状态下没有购物车,合并成功",loginedCartItems);}
//4.已登录状态下没有购物车:未登录状态下的购物车直接交给已登录状态下的购物车if(loginedCartItems==null||loginedCartItems.size()==0){redisTemplate.opsForValue().set(loginedKey,noLoginCartItems);redisTemplate.expire(loginedKey,30,TimeUnit.DAYS);//删除未登录状态下的购物车redisTemplate.delete(noLoginKey);return new ResultVO(true,"合并成功",noLoginCartItems);}
//5.彼此都在。HashMap<Long,CartItem> map = new HashMap<>();
//1)先把未登录的存进去for (CartItem cartItem : noLoginCartItems) {map.put(cartItem.getProductId(),cartItem);}
//2)再存已登录for (CartItem loginedCartItem : loginedCartItems) {
//当前loginedCartItem是否在map中已经存在CartItem currentCartItem = map.get(loginedCartItem.getProductId());if(currentCartItem==null){
//不存在map.put(loginedCartItem.getProductId(),loginedCartItem);}else{
//存在
//数量相加          currentCartItem.setCount(currentCartItem.getCount()+loginedCartItem.getCount());map.put(currentCartItem.getProductId(),currentCartItem);}}
//6.获取map中的List<CartItem>==合并结果Collection<CartItem> values = map.values();List<CartItem> mergedList = new ArrayList<>(values);
//7.存入到redisredisTemplate.opsForValue().set(loginedKey,mergedList);redisTemplate.expire(loginedKey,30,TimeUnit.DAYS);
//8.删除未登录状态下的购物车redisTemplate.delete(noLoginKey);return new ResultVO(true,"合并成功",mergedList);}ues = map.values();List<CartItem> mergedList = new ArrayList<>(values);
//7.存入到redisredisTemplate.opsForValue().set(loginedKey,mergedList);redisTemplate.expire(loginedKey,30,TimeUnit.DAYS);
//8.删除未登录状态下的购物车redisTemplate.delete(noLoginKey);return new ResultVO(true,"合并成功",mergedList);}

这样整个购物车就完成了,它可以在未登录的时候添加商品,当你登录成功时,购物车中的商品会自动添加到你的账号中。有什么不足的地方请多多指教。

购物车的实现(未登录时也可以使用)相关推荐

  1. Vue/vant——未登陆时清空购物车以及拦截未登录的状态拒绝进入购物车页面

    cart.js // 清空购物车clearCart(state){state.list = [],state.selectAll = []} axios.js "use strict&quo ...

  2. 经典用例设计(纸杯、购物车、电梯、登录框)

    如何测试一个杯子 这类的面试题目,是考察面试者是否熟悉各种软件测试方法, 设计test case的能力, 以及test sense. 首先应该反问下面试官, 需求是什么样的,比如大概是个什么样的杯子. ...

  3. SpringBoot实现未登录时不能访问数据页面

    从所周知,如果从开始阶段大家实现未登录时只能访问首页和登录页面的基本都是通过servlet + Filter(过滤器)来实现的,而在SSM整合时也是大同小异,现在介绍一下通过SpringBoot来实现 ...

  4. 查询长期未登录AD的用户

    可以使用dsquery命令查询,并使用管道dsrm删除. 但是还是建议你生产环境下先查询,确认无误后再删除.比较保险. dsquery命令用法. dsquery computer -inactive ...

  5. shiro 同时实现url和按钮的拦截_Shiro是如何拦截未登录请求的(一)

    问题描述 之前在公司搭项目平台的时候权限框架采用的是shiro,由于系统主要面向的是APP端的用户,PC端仅仅是公司内部人员在使用,而且考虑到系统的可用性和扩展性,服务端首先基于shiro做了一些改造 ...

  6. Spring Filter过滤器,Spring拦截未登录用户权限限制

    实现的功能:判断用户是否已登录,未登录用户禁止访问任何页面或action,自动跳转到登录页面. 比较好的做法是不管什么人都不能直接访问jsp页面,要访问就通过action,这样就变成了一个实实在在的权 ...

  7. php未登录跳到登陆页面,vue实现未登录跳转到登录页面的方法

    环境:vue 2.9.3; webpack;vue-router 目的:实现未登录跳转 例子:直接在url地址栏输入...../home,但是这个页面要求需要登陆之后才能进入,判断的值就通过登陆之后给 ...

  8. rtsp协议_如何在RTSP协议视频智能平台EasyNVR未登录的情况下调用通道直播的接口?...

    原标题:如何在RTSP协议视频智能平台EasyNVR未登录的情况下调用通道直播的接口? TSINGSEE青犀视频云边端架构全线都提供了丰富的API接口,用户可以自由调用进行二次开发.在本文之前,我们博 ...

  9. jsp中未登录用户也可以浏览页面的功能实现代码

    jsp中未登录用户也可以浏览页面的功能实现代码 <%!                int count=0;               %>                  < ...

最新文章

  1. ES6笔记(4)-- Symbol类型
  2. Android开发更改应用图标无效的问题
  3. java级联添加_JavaWeb学习记录(十三)——商城购物之添加订单的数据库级联操作...
  4. 如何修改python代码,如何更改默认的python版本?
  5. Thinkphp框架中D()和M()的区别
  6. 自适应lasso_线性回归模型优化算法(Lasso)
  7. Scala 函数式编程_部分应用函数_Partially Applied Functions
  8. Report Style
  9. MySQL 瓶颈分析及优化
  10. SWPUACM第一届程序设计大赛
  11. iTOP4412 gdbserver安装
  12. Objective--C Practice and source code
  13. 上海车牌拍牌辅助工具
  14. Vue 爬坑之旅 -- 用自定义指令解决 IOS 12 中键盘收起后页面底部有留白的问题
  15. 云免流usb共享电脑_手机怎么使用USB数据线共享PC网络
  16. Camera ITS当中的test_param_exposure_time测试
  17. 200佳优秀的精美网页欣赏网站推荐(系列八)
  18. 每日一题——整数除法
  19. 百度地图API详解之地图标注
  20. 深度学习训练营第6周好莱坞明星人脸识别

热门文章

  1. 【Linux】SSH协议 SSH登录和SCP传文件指令 ssh命令中变量的空格问题
  2. 解读apk分包-32位安装包、32位64位兼容包和64位安装包
  3. 自媒体1000W+推荐爆文分析,这么写标题,不火也难
  4. vscode编辑器,相对路径文件读取失败
  5. 【科普】和尚为何要吃素不吃肉
  6. JVM面试题 常用的JVM命令 超简单
  7. 内部推荐!陌陌深度学习实验室招聘算法实习生
  8. 【2023版】基于部标JT808JT1078车载视频位置监控平台介绍-开源项目
  9. 安装Windows8.1与Ubuntu14.10双系统遇到的各种“坑”
  10. java鼠标js触发事件吗,JavaScript常见鼠标事件与用法分析