先说下临时购物车的场景:当我们在浏览商城的时,我们可能在没登陆的情况下把一些商品放入购物车中,当我们关闭浏览器再进到购物车页面,依然会有这个商品,然而我们登陆了账号后,购物车里面的商品就会自动进到我们的个人购物车中。

实现说明:

  1. 用户打开浏览器时,点击加入购物车,这时我们浏览器先判断是否有登录的session,如果没有则会生成一个"user-key"的cookie,并且作用域为我们的一级域名 ,如 “ xx.com ” , 然后在 redis 中以哈希的数据结构来存储临时用户的购物车信息:
//临时用户  user-key为临时用户ID ,并设置过期时间为30天
HashMap< user-key , HashMap< 商品ID , 商品信息(包括属性、数量等) > >//登录用户
HashMap< userID , HashMap< 商品ID , 商品信息(包括属性、数量等)> >

2.使用自定义拦截器拦截购物车前后请求:

  • 注册拦截器
package com.xunqi.gulimall.cart.interceptor;import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** @Description:注册拦截器* @Created: with IntelliJ IDEA.* @author: YL* @createTime: 2020-06-30 17:57**/@Configuration
public class GulimallWebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new CartInterceptor())//注册拦截器.addPathPatterns("/**");}
}
  • 注册好后定义拦截器:
package com.xunqi.gulimall.cart.interceptor;import com.xunqi.common.vo.MemberResponseVo;
import com.xunqi.gulimall.cart.to.UserInfoTo;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.UUID;import static com.xunqi.common.constant.AuthServerConstant.LOGIN_USER;
import static com.xunqi.common.constant.CartConstant.TEMP_USER_COOKIE_NAME;
import static com.xunqi.common.constant.CartConstant.TEMP_USER_COOKIE_TIMEOUT;/*** @Description: 在执行目标方法之前,判断用户的登录状态.并封装传递给controller目标请求* @Created: with IntelliJ IDEA.* @author: yl* @createTime: 2020-06-30 17:31**/public class CartInterceptor implements HandlerInterceptor {//获取当前线程的各种数据专用public static ThreadLocal<UserInfoTo> toThreadLocal = new ThreadLocal<>();/**** 目标方法执行之前* @param request* @param response* @param handler* @return* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {UserInfoTo userInfoTo = new UserInfoTo();HttpSession session = request.getSession();//获得当前登录用户的信息MemberResponseVo memberResponseVo = (MemberResponseVo) session.getAttribute(LOGIN_USER);if (memberResponseVo != null) {//用户登录了userInfoTo.setUserId(memberResponseVo.getId());}Cookie[] cookies = request.getCookies();if (cookies != null && cookies.length > 0) {for (Cookie cookie : cookies) {//user-keyString name = cookie.getName();if (name.equals(TEMP_USER_COOKIE_NAME)) {userInfoTo.setUserKey(cookie.getValue());//标记为已是临时用户userInfoTo.setTempUser(true);}}}//如果没有临时用户一定分配一个临时用户if (StringUtils.isEmpty(userInfoTo.getUserKey())) {String uuid = UUID.randomUUID().toString();userInfoTo.setUserKey(uuid);}//目标方法执行之前toThreadLocal.set(userInfoTo);return true;}/*** 业务执行之后,分配临时用户来浏览器保存* @param request* @param response* @param handler* @param modelAndView* @throws Exception*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {//获取当前用户的值UserInfoTo userInfoTo = toThreadLocal.get();//如果没有临时用户一定保存一个临时用户if (!userInfoTo.getTempUser()) {//创建一个cookieCookie cookie = new Cookie(TEMP_USER_COOKIE_NAME, userInfoTo.getUserKey());//扩大作用域cookie.setDomain("gulimall.com");//设置过期时间cookie.setMaxAge(TEMP_USER_COOKIE_TIMEOUT);response.addCookie(cookie);}}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
}
  • 购物车常量
package com.xunqi.common.constant;/*** @Description: 购物车常量* @Created: with IntelliJ IDEA.* @author: YL* @createTime: 2020-06-30 17:39**/
public class CartConstant {//临时用户key 名称public final static String TEMP_USER_COOKIE_NAME = "user-key";//过期时间public final static int TEMP_USER_COOKIE_TIMEOUT = 60*60*24*30;//存储redis 的前缀public final static String CART_PREFIX = "gulimall:cart:";}
  • 实体类vo : UserInfoTo
package com.xunqi.gulimall.cart.to;import lombok.Data;/*** @Description:* @Created: with IntelliJ IDEA.* @author: ly* @createTime: 2020-06-30 17:35**/@Data
public class UserInfoTo {private Long userId;private String userKey;/*** 是否临时用户*/private Boolean tempUser = false;}
  • 购物车操作CURD:
package com.xunqi.gulimall.cart.service.impl;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.xunqi.common.utils.R;
import com.xunqi.gulimall.cart.exception.CartExceptionHandler;
import com.xunqi.gulimall.cart.feign.ProductFeignService;
import com.xunqi.gulimall.cart.interceptor.CartInterceptor;
import com.xunqi.gulimall.cart.service.CartService;
import com.xunqi.gulimall.cart.to.UserInfoTo;
import com.xunqi.gulimall.cart.vo.CartItemVo;
import com.xunqi.gulimall.cart.vo.CartVo;
import com.xunqi.gulimall.cart.vo.SkuInfoVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;import static com.xunqi.common.constant.CartConstant.CART_PREFIX;/*** @Description:* @Created: with IntelliJ IDEA.* @author: 夏沫止水* @createTime: 2020-06-30 17:06**/@Slf4j
@Service("cartService")
public class CartServiceImpl implements CartService {@Autowiredprivate StringRedisTemplate redisTemplate;@Autowiredprivate ProductFeignService productFeignService;@Autowiredprivate ThreadPoolExecutor executor;@Overridepublic 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;}}@Overridepublic CartItemVo getCartItem(Long skuId) {//拿到要操作的购物车信息BoundHashOperations<String, Object, Object> cartOps = getCartOps();String redisValue = (String) cartOps.get(skuId.toString());CartItemVo cartItemVo = JSON.parseObject(redisValue, CartItemVo.class);return cartItemVo;}/*** 获取用户登录或者未登录购物车里所有的数据* @return* @throws ExecutionException* @throws InterruptedException*/@Overridepublic CartVo getCart() throws ExecutionException, InterruptedException {CartVo cartVo = new CartVo();UserInfoTo userInfoTo = CartInterceptor.toThreadLocal.get();if (userInfoTo.getUserId() != null) {//1、登录String cartKey = CART_PREFIX + userInfoTo.getUserId();//临时购物车的键String temptCartKey = CART_PREFIX + userInfoTo.getUserKey();//2、如果临时购物车的数据还未进行合并List<CartItemVo> tempCartItems = getCartItems(temptCartKey);if (tempCartItems != null) {//临时购物车有数据需要进行合并操作for (CartItemVo item : tempCartItems) {addToCart(item.getSkuId(),item.getCount());}//清除临时购物车的数据clearCartInfo(temptCartKey);}//3、获取登录后的购物车数据【包含合并过来的临时购物车的数据和登录后购物车的数据】List<CartItemVo> cartItems = getCartItems(cartKey);cartVo.setItems(cartItems);} else {//没登录String cartKey = CART_PREFIX + userInfoTo.getUserKey();//获取临时购物车里面的所有购物项List<CartItemVo> cartItems = getCartItems(cartKey);cartVo.setItems(cartItems);}return cartVo;}/*** 获取到我们要操作的购物车* @return*/private BoundHashOperations<String, Object, Object> getCartOps() {//先得到当前用户信息UserInfoTo userInfoTo = CartInterceptor.toThreadLocal.get();String cartKey = "";if (userInfoTo.getUserId() != null) {//gulimall:cart:1cartKey = CART_PREFIX + userInfoTo.getUserId();} else {cartKey = CART_PREFIX + userInfoTo.getUserKey();}//绑定指定的key操作RedisBoundHashOperations<String, Object, Object> operations = redisTemplate.boundHashOps(cartKey);return operations;}/*** 获取购物车里面的数据* @param cartKey* @return*/private List<CartItemVo> getCartItems(String cartKey) {//获取购物车里面的所有商品BoundHashOperations<String, Object, Object> operations = redisTemplate.boundHashOps(cartKey);List<Object> values = operations.values();if (values != null && values.size() > 0) {List<CartItemVo> cartItemVoStream = values.stream().map((obj) -> {String str = (String) obj;CartItemVo cartItem = JSON.parseObject(str, CartItemVo.class);return cartItem;}).collect(Collectors.toList());return cartItemVoStream;}return null;}@Overridepublic void clearCartInfo(String cartKey) {redisTemplate.delete(cartKey);}@Overridepublic void checkItem(Long skuId, Integer check) {//查询购物车里面的商品CartItemVo cartItem = getCartItem(skuId);//修改商品状态cartItem.setCheck(check == 1?true:false);//序列化存入redis中String redisValue = JSON.toJSONString(cartItem);BoundHashOperations<String, Object, Object> cartOps = getCartOps();cartOps.put(skuId.toString(),redisValue);}/*** 修改购物项数量* @param skuId* @param num*/@Overridepublic void changeItemCount(Long skuId, Integer num) {//查询购物车里面的商品CartItemVo cartItem = getCartItem(skuId);cartItem.setCount(num);BoundHashOperations<String, Object, Object> cartOps = getCartOps();//序列化存入redis中String redisValue = JSON.toJSONString(cartItem);cartOps.put(skuId.toString(),redisValue);}/*** 删除购物项* @param skuId*/@Overridepublic void deleteIdCartInfo(Integer skuId) {BoundHashOperations<String, Object, Object> cartOps = getCartOps();cartOps.delete(skuId.toString());}@Overridepublic List<CartItemVo> getUserCartItems() {List<CartItemVo> cartItemVoList = new ArrayList<>();//获取当前用户登录的信息UserInfoTo userInfoTo = CartInterceptor.toThreadLocal.get();//如果用户未登录直接返回nullif (userInfoTo.getUserId() == null) {return null;} else {//获取购物车项String cartKey = CART_PREFIX + userInfoTo.getUserId();//获取所有的List<CartItemVo> cartItems = getCartItems(cartKey);if (cartItems == null) {throw new CartExceptionHandler();}//筛选出选中的cartItemVoList = cartItems.stream().filter(items -> items.getCheck()).map(item -> {//更新为最新的价格(查询数据库)BigDecimal price = productFeignService.getPrice(item.getSkuId());item.setPrice(price);return item;}).collect(Collectors.toList());}return cartItemVoList;}
}

临时购物车用户购物车相关推荐

  1. 电商项目-购物车(合并购物车问题)

    1.添加购物车分析 1.商品详情页添加购物车2.商品详情页添加购物车时,先判断用户是否登陆,如果没有登录,在判断是否存在临时用户,如果cookie中也没有临时用户,则生成临时用户.3.购物车商品与临时 ...

  2. vue外卖十九:商家详情-底部购物车组件,购物车相关vuex状态设计、相关计算、清空购物车+滚动购物车

    一.购物车基础 1)购物车状态设计cartFoods+mutation store/state.js // 所有要管理的状态数据:从页面需求分析出来,最好和api/index.js里的命名相同 exp ...

  3. SpringBoot-项目4-购物车(添加入购物车,购物车列表,购物车商品数量加减操作)

    64. 购物车-创建数据表 # 注意:没有添加相应的not null约束 CREATE TABLE t_cart (cid INT AUTO_INCREMENT COMMENT '购物车数据id',u ...

  4. SpringBoot+Vue实现商品不能重复加入购物车、购物车中展示商品的信息、删除商品重点提示等操作。如何点击图片实现图片放大

    如何通过点击商品详情下方的加入购物车按钮,将商品信息加入到个人购物车下?如何阻止重复加入同一个商品到购物车?如何在删除购物车中的商品时,重点提示?后端的数据库如何设计来保证加入的商品在当前用户下? 前 ...

  5. Android商城开发----点击加入购物车,购物车商品的增删减

    Android商城开发----点击加入购物车,购物车商品的增删减 上一章节:[分类页面]点击左侧类别,实现右侧对应类别商品的展示 本文是在上一章节基础上开发的,点击[分类]页面商品的加入购物车按钮,实 ...

  6. python商家入口_购物车--用户和商户入口

    ''' 此模块需要使用用户登陆模块代码 用户入口: 1.输入用户名和密码 2.商品信息存在文件里 3.已购商品和余额记录在文件里 注:用户购买商品后退出,再次登陆会自动读取上次用户的余额 商户入口: ...

  7. 2022 智简魔方财务快云模板前台+购物车+用户中心模板 全解 无授权

    快云模板-魔方开发大赛最佳设计 https://download.csdn.net/download/qq_33747437/86265893 模板教程: 第一步:上传文件: 模板文件.zip到/pu ...

  8. python购物车_python购物车功能

    广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! print(商城货物暂时短缺,请输入正确的商品序号)elifgong_neng= ...

  9. 简单的购物车和购物车结算

    购买部分 HTML部分 <!DOCTYPE html> <html><head><meta charset="UTF-8">< ...

最新文章

  1. 黄聪:百度知道中对HTML字符实体、字符编号,开头字符的使用
  2. 前端学习(2193):vuex概念和作用分析
  3. 【python】编程学习练习题-2
  4. HttpsURLConnection 利用keepAlive特性进行优化一例
  5. mysql存储过程含义,MySQL存储过程定义中的特性(characteristic)的含义
  6. QQ浏览器下拉词推广是什么?
  7. 126. PHP 加密
  8. Rust: tokio,异步代码与运行速度初探
  9. webgote的例子(5)Sql注入(Blog)
  10. 翻译:控制容器的反转IoC和依赖注入模式DIP 概念发源地 Martin Fowler
  11. 英特尔固态硬盘测试软件,Intel SSD Toolbox
  12. Idea设置字体大小
  13. npm install 提示 path .../node_modules/node-sass command failed
  14. 影响因子,计算机科学,影响因子最高计算机科学期刊(前50种).doc
  15. android5.1修改默认锁屏方式(去除锁屏)
  16. MATLAB toc使用
  17. java list控件_java Swing中对于JList控件的使用(一)
  18. 【SpringBoot】SpringBoot @ConfigurationProperties 注解 用法与加载static静态属性
  19. Trados T-Window的使用方法
  20. 【技术美术美术部分】AO贴图的烘焙及应用

热门文章

  1. Linux基础-制作本地apt仓库(离线安装软件)
  2. 关于 编译QT项目时报错: error: cannot find -lGL 的解决方法
  3. 几个常用网络/服务器监控开源软件
  4. python外星人入侵飞船上下移动_Python 项目练习1 外星人入侵(武装飞船)
  5. 单摆c语言编程,单摆时钟
  6. 高精地图生产中的坐标系
  7. CoreOS发布Clair,容器镜像分析器
  8. 奶粉事件-不合格企业及奶粉批次
  9. 30个设计精美的Email Templates[邮件模版] (建议收藏)
  10. 2019求知讲堂Java基础教程 全网唯一高口碑 无废话 无尿点的就在这里