购物车
1. 数据模型分析
1.1. 需求描述
- 用户可以在登录状态下将商品添加到购物车【用户购物车/在线购物车】
-
- 放入数据库
- mongodb
- 放入 redis(采用)
-
-
- 登录以后,会将临时购物车的数据全部合并过来,并清空临时购物车;
-
- 用户可以在未登录状态下将商品添加到购物车【游客购物车/离线购物车/临时购物车】
-
- 放入 localstorage(客户端存储,后台不存)
- cookie
- WebSQL
- 放入 redis(采用)
-
-
- 浏览器即使关闭,下次进入,临时购物车数据都在
-
- 用户可以使用购物车一起结算下单
- 给购物车添加商品
- 用户可以查询自己的购物车
- 用户可以在购物车中修改购买商品的数量。
- 用户可以在购物车中删除商品。
- 选中不选中商品
- 在购物车中展示商品优惠信息
-
- 提示购物车商品价格变化
1.2. 数据存储
购物车是一个读多写多的场景,因此放入数据库并不合适,但购物车又是需要持久化,因此这里我们选用redis存储购物车数据。
1.3. 数据结构
购物项
因此每一个购物项信息,都是一个对象,基本字段包括:
{
skuId: 2131241,
check: true,
title: "Apple iphone.....",
defaultImage: "...",
price: 4999,
count: 1,
totalPrice: 4999,
skuSaleVO: {...}
}
另外,购物车中不止一条数据,因此最终会是对象的数组。即:
[
{...},{...},{...}
]
Redis 有 5 种不同数据结构,这里选择哪一种比较合适呢?Map<String, List<String>>
- 首先不同用户应该有独立的购物车,因此购物车应该以用户的作为 key 来存储,Value 是用户的所有购物车信息。这样看来基本的`k-v`结构就可以了。
- 但是,我们对购物车中的商品进行增、删、改操作,基本都需要根据商品 id 进行判断,为了方便后期处理,我们的购物车也应该是`k-v`结构,key 是商品 id,value 才是这个商品的购物车信息。
综上所述,我们的购物车结构是一个双层 Map :Map<String,Map<String,String>>
- 第一层 Map,Key 是用户 id
- 第二层 Map,Key 是购物车中商品 id,值是购物项数据
一个购物车是由各个购物项组成的,但是我们用 List进行存储并不合适,因为使用 List查找某个购物项时需要挨个遍历每个购物项,会造成大量时间损耗,为保证查找速度,我们使用 hash进行存储。
1.4. 流程
参照京东
user-key 是随机生成的 id,不管有没有登录都会有这个 cookie 信息。
两个功能:新增商品到购物车、查询购物车。
新增商品:判断是否登录
- 是:则添加商品到后台 Redis 中,把 user 的唯一标识符作为 key。
- 否:则添加商品到后台 redis 中,使用随机生成的 user-key 作为 key。
查询购物车列表:判断是否登录
- 否:直接根据 user-key 查询 redis 中数据并展示
- 是:已登录,则需要先根据 user-key 查询 redis 是否有数据。
-
- 有:需要提交到后台添加到 redis,合并数据,而后查询。
- 否:直接去后台查询 redis,而后返回。
2. VO编写
2.1. 因此每一个购物项信息,都是一个对象,基本字段包括:
添加"com.cwh.gulimall.cart.vo.CartItem"类,代码如下:
package com.cwh.gulimall.cart.vo;
import java.math.BigDecimal;
import java.util.List;
/**
* @Description: 购物项
* @Date: 2024/5/19 19:04
* @Version 1.0
*/
public class CartItem {
/**
* 商品id
*/
private Long skuId;
/**
* 是否选中
*/
private Boolean check = true;
/**
* 标题
*/
private String title;
/**
* 图片
*/
private String image;
/**
* 商品套餐属性
*/
private List<String> skuAttr;
/**
* 价格
*/
private BigDecimal price;
/**
* 数量
*/
private Integer count;
/**
* 总价
*/
private BigDecimal totalPrice;
public Long getSkuId() {
return skuId;
}
public void setSkuId(Long skuId) {
this.skuId = skuId;
}
public Boolean getCheck() {
return check;
}
public void setCheck(Boolean check) {
this.check = check;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public List<String> getSkuAttr() {
return skuAttr;
}
public void setSkuAttr(List<String> skuAttr) {
this.skuAttr = skuAttr;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
/**
* 计算当前购物项总价
*
* @return
*/
public BigDecimal getTotalPrice() {
return this.price.multiply(new BigDecimal("" + this.count));
}
public void setTotalPrice(BigDecimal totalPrice) {
this.totalPrice = totalPrice;
}
}
添加"com.cwh.gulimall.cart.vo.Cart"类,代码如下:
package com.cwh.gulimall.cart.vo;
import java.math.BigDecimal;
import java.util.List;
/**
* @Description: 整体购物车 需要计算的属性,必须重写他的get方法,保证每次获取属性都会进行计算
* @Date: 2024/5/19 19:07
* @Version 1.0
*/
public class Cart {
/**
* 购物车子项信息
*/
List<CartItem> items;
/**
* 商品数量
*/
private Integer countNum;
/**
* 商品类型数量
*/
private Integer countType;
/**
* 商品总价
*/
private BigDecimal totalAmount;
/**
* 减免价格
*/
private BigDecimal reduce = new BigDecimal("0.00");
public List<CartItem> getItems() {
return items;
}
public void setItems(List<CartItem> items) {
this.items = items;
}
public Integer getCountNum() {
int count = 0;
if (items != null && items.size() > 0) {
for (CartItem item : items) {
count += item.getCount();
}
}
return count;
}
public Integer getCountType() {
int count = 0;
if (items != null && items.size() > 0) {
for (CartItem item : items) {
count += 1;
}
}
return count;
}
public BigDecimal getTotalAmount() {
BigDecimal amount = new BigDecimal("0");
// 1、计算购物项总价
if (items != null && items.size() > 0) {
for (CartItem item : items) {
if (item.getCheck()) {
BigDecimal totalPrice = item.getTotalPrice();
amount = amount.add(totalPrice);
}
}
}
// 2、减去优惠总价
BigDecimal subtract = amount.subtract(getReduce());
return subtract;
}
public BigDecimal getReduce() {
return reduce;
}
public void setReduce(BigDecimal reduce) {
this.reduce = reduce;
}
}
2.2. 导入redis和SpringSession的依赖
<!--整合SpringSession完成session共享问题-->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.3. 配置redis和SpringSession存储类型
#配置redis
spring.redis.host=192.168.119.127
spring.redis.port=6379
spring.session.store-type=redis
2.4. 添加SpringSession配置类
添加"com.cwh.gulimall.cart.config.GulimallSessionConfig"类,代码如下:
@EnableRedisHttpSession //自动开启RedisHttpSession
@Configuration
public class GulimallSessionConfig {
@Bean
public CookieSerializer cookieSerializer(){
DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
cookieSerializer.setDomainName("gulimall.com");
cookieSerializer.setCookieName("GULISESSION");
return cookieSerializer;
}
@Bean
public RedisSerializer<Object> springSessionDefaultRedisSerializer(){
return new GenericJackson2JsonRedisSerializer();
}
}
3. ThreadLocal用户身份鉴别
3.1. ThreadLocal同一个线程共享数据
3.2. 核心原理
Map<Thread,Object> threadLocal
3.3. 用户身份鉴别方式
参考京东,在点击购物车时,会为临时用户生成一个name为user-key的cookie临时标识,过期时间为一个月,如果手动清除user-key,那么临时购物车的购物项也被清除,所以user-key是用来标识和存储临时购物车数据的。
3.4. 使用ThreadLocal进行用户身份鉴别信息传递
- 在调用购物车的接口前,先通过session信息判断是否登录,并分别进行用户身份信息的封装,并把user-key放在cookie中
- 这个功能使用拦截器进行完成
添加"com.cwh.gulimall.cart.vo.UserInfoTo"类,代码如下:
@ToString
@Data
public class UserInfoTo {
private Long userId;
private String userKey; //一定封装
private boolean tempUser = false; //判断是否有临时用户
}
添加"com.cwh.gulimall.cart.interceptor.CartInterceptor"类,代码如下:
package com.cwh.gulimall.cart.interceptor;
import com.cwh.common.constant.AuthServerConstant;
import com.cwh.common.constant.CartConstant;
import com.cwh.common.vo.MemberResponseVO;
import com.cwh.gulimall.cart.vo.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;
/**
* @Description: 在执行目标方法之前,判断用户的登录状态。并封装传递给目标请求
* @Date: 2024/5/19 19:39
* @Version 1.0
*/
public class CartInterceptor implements HandlerInterceptor {
// ThreadLocal同一个线程共享数据
public static ThreadLocal<UserInfoTo> threadLocal = new ThreadLocal<>();
/**
* 在目标方法执行之前拦截
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
UserInfoTo userInfoTo = new UserInfoTo();
HttpSession session = request.getSession();
MemberResponseVO member = (MemberResponseVO) session.getAttribute(AuthServerConstant.LOGIN_USER);
// 1、用户登录,封装用户id
if (member != null) {
userInfoTo.setUserId(member.getId());
}
// 2、如果有临时用户,封装临时用户
Cookie[] cookies = request.getCookies();
if (cookies != null && cookies.length > 0) {
for (Cookie cookie : cookies) {
String name = cookie.getName();
if (name.equals(CartConstant.TEMP_USER_COOKIE_NAME)) {
userInfoTo.setUserKey(cookie.getValue());
userInfoTo.setTempUser(true);
}
}
}
// 3、如果没有临时用户,一定保存一个临时用户
if (StringUtils.isEmpty(userInfoTo.getUserKey())) {
String uuid = UUID.randomUUID().toString();
userInfoTo.setUserKey(uuid);
}
// 目标方法执行之前,将用户信息保存到ThreadLocal
threadLocal.set(userInfoTo);
return true;
}
/**
* 业务执行之后 分配临时用户,让浏览器保存
*
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
UserInfoTo userInfoTo = threadLocal.get();
// 如果没有临时用户,第一次访问购物车就添加临时用户
if (!userInfoTo.isTempUser()) {
// 持续的延长用户的过期时间
Cookie cookie = new Cookie(CartConstant.TEMP_USER_COOKIE_NAME, userInfoTo.getUserKey());
cookie.setDomain("gulimall.com");
cookie.setMaxAge(CartConstant.TEMP_USER_COOKIE_TIMEOUT);
response.addCookie(cookie);
}
}
}
添加拦截器的配置,不能只把拦截器加入容器中,不然拦截器不生效的
添加"com.cwh.gulimall.cart.config.GulimallWebConfig"类,代码如下:
@Configuration
public class GulimallWebConfig implements WebMvcConfigurer {
/**
* 添加拦截器的配置,不能只把拦截器加入容器中,不然拦截器不生效的
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new CartInterceptor()).addPathPatterns("/**");
}
}
添加"com.cwh.gulimall.cart.controller.CartController"类,代码如下:
@Controller
public class CartController {
/**
* 浏览器有一个cookie;user-key:标识用户身份,一个月后过期
* 如果第一次使用jd购物车功能,都会给一个临时的用户身份
* 浏览器保存,每次访问都会带上有这个cookies
*
* 登录 session有
* 没登录,按照cookie里面带来的user-key来做
* 第一次,如果没有临时用户,帮忙创建一个临时用户
*/
@GetMapping("/cart.html")
public String cartListPage(){
//快速得到用户信息,id,user-key
UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
System.out.println(userInfoTo);
return "cartList";
}
}
4. 添加商品到购物车
在gulimall-product模块,修改"加入购物车"按钮
修改"com.cwh.gulimall.cart.controller.CartController"类,代码如下:
/**
* 添加商品到购物车
*/
@GetMapping("/addToCart")
public String addToCart(@RequestParam("skuId") Long skuId,
@RequestParam("num") Integer num,
Model model) {
CartItem cartItem = cartService.addToCart(skuId,num);
model.addAttribute("item", cartItem);
return "success";
}
修改"com.cwh.gulimall.cart.service.CartService"类,代码如下:
public interface CartService {
CartItem addToCart(Long skuId, Integer num) throws ExecutionException, InterruptedException;
}
业务逻辑
- 若当前商品已经存在购物车,只需增添数量
- 否则需要查询商品购物项所需信息,并添加新商品至购物车
修改"com.cwh.gulimall.cart.service.impl.CartServiceImpl"类,代码如下:
@Slf4j
@Service
public class CartServiceImpl implements CartService {
private final String CART_PREFIX = "gulimall:cart";
@Autowired
StringRedisTemplate stringRedisTemplate;
@Autowired
ProductFeignService productFeignService;
@Autowired
ThreadPoolExecutor executor;
@Override
public CartItem addToCart(Long skuId, Integer num) throws ExecutionException, InterruptedException {
// 获取我们要操作的购物车,临时购物车、用户购物车
BoundHashOperations<String, Object, Object> cartOps = getCartOps();
String res = (String) cartOps.get(skuId.toString());
// 1、添加新商品到购物车(购物车无此商品)
if (StringUtils.isEmpty(res)) {
CartItem cartItem = new CartItem();
/**
* 异步查询
*/
CompletableFuture<Void> getSkuInfo = CompletableFuture.runAsync(() -> {
// 1.1、远程查询要添加的商品信息
R skuInfo = productFeignService.getSkuInfo(skuId);
SkuInfoVo data = skuInfo.getData("skuInfo", new TypeReference<SkuInfoVo>() {
});
cartItem.setCheck(true);
cartItem.setCount(1);
cartItem.setImage(data.getSkuDefaultImg());
cartItem.setTitle(data.getSkuTitle());
cartItem.setSkuId(skuId);
cartItem.setPrice(data.getPrice());
}, executor);
CompletableFuture<Void> getSkuSaleAttrValues = CompletableFuture.runAsync(() -> {
// 1.2、远程查询sku的组合信息
List<String> values = productFeignService.getSkuSaleAttrValues(skuId);
cartItem.setSkuAttr(values);
}, executor);
CompletableFuture.allOf(getSkuInfo, getSkuSaleAttrValues).get();
String jsonString = JSON.toJSONString(cartItem);
cartOps.put(skuId.toString(), jsonString);
return cartItem;
} else {
// 2、购物车有此商品,将数据取出修改数量即可
CartItem cartItem = JSON.parseObject(res, CartItem.class);
cartItem.setCount(cartItem.getCount() + num);
cartOps.put(skuId.toString(), JSON.toJSONString(cartItem));
return cartItem;
}
}
/**
* 获取我们要操作的购物车,临时购物车、用户购物车
*
* @return
*/
private BoundHashOperations<String, Object, Object> getCartOps() {
// 得到用户信息 账号用户 、临时用户
UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
// 1、userInfoTo.getUserId()不为空表示账号用户,反之临时用户 然后决定用临时购物车还是用户购物车
// 放入缓存的key
String cartKey = "";
if (userInfoTo.getUserId() != null) {
cartKey = CART_PREFIX + userInfoTo.getUserId();
} else {
cartKey = CART_PREFIX + userInfoTo.getUserKey();
}
BoundHashOperations<String, Object, Object> operations = stringRedisTemplate.boundHashOps(cartKey);
return operations;
}
}
4.1.1. 配置线程池
application.properties添加线程池配置
gulimall.thread.core= 20
gulimall.thread.max-size= 200
gulimall.thread.keep-alive-time= 10
添加"com.cwh.gulimall.cart.config.ThreadPoolConfigProperties"类,代码如下:
@ConfigurationProperties(prefix = "gulimall.thread")
@Component
@Data
public class ThreadPoolConfigProperties {
private Integer core;
private Integer maxSize;
private Integer keepAliveTime;
}
添加"com.cwh.gulimall.cart.config.MyThreadConfig"类,代码如下:
//如果ThreadPoolConfigProperties.class类没有加上@Component注解,那么我们在需要的配置类里开启属性配置的类加到容器中
//@EnableConfigurationProperties(ThreadPoolConfigProperties.class)
@Configuration
public class MyThreadConfig {
@Bean
public ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties pool){
return new ThreadPoolExecutor(pool.getCore(),
pool.getMaxSize(),
pool.getKeepAliveTime(),
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
}
}
4.1.2. 远程查询要添加的商品信息
添加"com.cwh.gulimall.cart.vo.SkuInfoVo"类,代码如下
@Data
public class SkuInfoVo {
private Long skuId;
/**
* spuId
*/
private Long spuId;
/**
* sku名称
*/
private String skuName;
/**
* sku介绍描述
*/
private String skuDesc;
/**
* 所属分类id
*/
private Long catalogId;
/**
* 品牌id
*/
private Long brandId;
/**
* 默认图片
*/
private String skuDefaultImg;
/**
* 标题
*/
private String skuTitle;
/**
* 副标题
*/
private String skuSubtitle;
/**
* 价格
*/
private BigDecimal price;
/**
* 销量
*/
private Long saleCount;
}
添加"com.cwh.gulimall.cart.feign.ProductFeignService"类,代码如下:
@FeignClient("gulimall-product")
public interface ProductFeignService {
@RequestMapping("/product/skuinfo/info/{skuId}")
R getSkuInfo(@PathVariable("skuId") Long skuId);
@GetMapping("product/skusaleattrvalue/stringlist/{skuId}")
public List<String> getSkuSaleAttrValues(@PathVariable("skuId") Long skuId);
}
4.1.3. 远程查询sku的组合信息
修改"com.cwh.gulimall.product.app.SkuSaleAttrValueController"类,代码如下:
@GetMapping("stringlist/{skuId}")
public List<String> getSkuSaleAttrValues(@PathVariable("skuId") Long skuId){
return skuSaleAttrValueService.getSkuSaleAttrValuesAsStringList(skuId);
}
修改"com.cwh.gulimall.product.service.SkuSaleAttrValueService",代码如下:
List<String> getSkuSaleAttrValuesAsStringList(Long skuId);
修改"com.cwh.gulimall.product.service.impl.SkuSaleAttrValueServiceImpl"类,代码如下:
@Override
public List<String> getSkuSaleAttrValuesAsStringList(Long skuId) {
return this.baseMapper.getSkuSaleAttrValuesAsStringList(skuId);
}
修改"com.cwh.gulimall.product.dao.SkuSaleAttrValueDao"类,代码如下:
List<String> getSkuSaleAttrValuesAsStringList(@Param("skuId") Long skuId);
<select id="getSkuSaleAttrValuesAsStringList" resultType="java.lang.String">
select CONCAT(attr_name,":",attr_value) from pms_sku_sale_attr_value
where sku_id = #{skuId}
</select>
4.1.4. bug:不断刷新页面会一直增加数量
不断刷新页面会一直增加数量,所以我们修改逻辑在controller的addToCart方法里添加商品,商品添加完跳转到成功页面我们改为改成重定向另一个方法,专门查询数据跳转到成功页面
修改"com.cwh.gulimall.cart.controller.CartController"类,代码如下:
/**
* 添加商品到购物车
* <p>
* RedirectAttributes attributes
* attributes.addFlashAttribute();将数据放在session里面可以在页面取出,但只能取一次
* attributes.addAttribute("skuId",skuId); 将数据放在url后面
*
* @return
*/
@GetMapping("/addToCart")
public String addToCart(@RequestParam("skuId") Long skuId,
@RequestParam("num") Integer num,
RedirectAttributes attributes) throws ExecutionException, InterruptedException {
cartService.addToCart(skuId, num);
attributes.addAttribute("skuId", skuId);
return "redirect:http://cart.gulimall.com/addToCartSuccess.html";
}
/**
* 跳转到成功页
*
* @param skuId
* @param model
* @return
*/
@GetMapping("/addToCartSuccess.html")
public String addToCartSuccessPage(@RequestParam("skuId") Long skuId, Model model) {
CartItem cartItem = cartService.getCartItem(skuId);
model.addAttribute("item", cartItem);
return "success";
}
修改"com.cwh.gulimall.cart.service.CartService"类,代码如下:
/**
* 获取购物车中某个购物项
*
* @param skuId
* @return
*/
CartItem getCartItem(Long skuId);
修改"com.cwh.gulimall.cart.service.impl.CartServiceImpl"类,代码如下:
@Override
public CartItem getCartItem(Long skuId) {
BoundHashOperations<String, Object, Object> cartOps = getCartOps();
String s = (String) cartOps.get(skuId.toString());
CartItem cartItem = JSON.parseObject(s, CartItem.class);
return cartItem;
}
5. 获取&合并购物车
5.1. 获取
- 若用户未登录,则直接使用user-key获取购物车数据
- 否则使用userId获取购物车数据,并将user-key对应临时购物车数据与用户购物车数据合并,并删除临时购物车
修改"com.cwh.gulimall.cart.controller.CartController"类,代码如下
@GetMapping("/cart.html")
public String cartListPage(Model model) throws ExecutionException, InterruptedException {
// 快速得到用户信息,id,user-key
// UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
Cart cart = cartService.getCart();
model.addAttribute("cart", cart);
return "cartList";
}
修改"com.cwh.gulimall.cart.service.CartService"类,代码如下:
/**
* 获取整个购物车
*
* @return
*/
Cart getCart() throws ExecutionException, InterruptedException;
修改"com.cwh.gulimall.cart.service.impl.CartServiceImpl"类,代码如下:
@Override
public Cart getCart() throws ExecutionException, InterruptedException {
Cart cart = new Cart();
// 1、登录
UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
if (userInfoTo.getUserId() != null) {
String cartKey = CART_PREFIX + userInfoTo.getUserId();
// 1.1、如果临时购物车的数据还没有合并【合并购物车】
String tempCartKey = CART_PREFIX + userInfoTo.getUserKey();
List<CartItem> tempsCartItems = getCartItems(tempCartKey);
if (tempsCartItems != null) {
// 临时购物车有数据,需要合并
for (CartItem item : tempsCartItems) {
addToCart(item.getSkuId(), item.getCount());
}
// 清除临时购物车的数据
clearCart(tempCartKey);
}
// 1.2、获取登录后的购物车数据【包含合并过来的临时购物车的数据,和登录后的购物车数据】
List<CartItem> cartItems = getCartItems(cartKey);
cart.setItems(cartItems);
} else {
// 2、没登录
String cartKey = CART_PREFIX + userInfoTo.getUserKey();
// 获取临时购物车的所有购物项
List<CartItem> cartItems = getCartItems(cartKey);
cart.setItems(cartItems);
}
return cart;
}
/**
* 获取购物项
*
* @param cartKey
* @return
*/
private List<CartItem> getCartItems(String cartKey){
BoundHashOperations<String, Object, Object> operations = stringRedisTemplate.boundHashOps(cartKey);
List<Object> values = operations.values();
if (values != null && values.size() > 0){
List<CartItem> collect = values.stream().map(obj -> {
String str = (String) obj;
CartItem cartItem = JSON.parseObject(str, CartItem.class);
return cartItem;
}).collect(Collectors.toList());
return collect;
}
return null;
}
5.2. 清空购物车数据
修改"com.cwh.gulimall.cart.service.CartService"类,代码如下:
/**
* 清空购物车数据
*
* @param cartKey
*/
void clearCart(String cartKey);
修改"com.cwh.gulimall.cart.service.CartService"类,代码如下:
@Override
public void clearCart(String cartKey) {
stringRedisTemplate.delete(cartKey);
}
6. 选中购物车项
修改"com.cwh.gulimall.cart.service.CartService"类,代码如下:
/**
* 勾选购物项
* @param skuId
* @param check
*/
void checkItem(Long skuId, Integer check);
修改"com.cwh.gulimall.cart.service.impl.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 jsonString = JSON.toJSONString(cartItem);
cartOps.put(skuId.toString(), jsonString);
}
/**
* 获取我们要操作的购物车,临时购物车、用户购物车
*
* @return
*/
private BoundHashOperations<String, Object, Object> getCartOps() {
// 得到用户信息 账号用户 、临时用户
UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
// 1、userInfoTo.getUserId()不为空表示账号用户,反之临时用户 然后决定用临时购物车还是用户购物车
// 放入缓存的key
String cartKey = "";
if (userInfoTo.getUserId() != null) {
cartKey = CART_PREFIX + userInfoTo.getUserId();
} else {
cartKey = CART_PREFIX + userInfoTo.getUserKey();
}
BoundHashOperations<String, Object, Object> operations = stringRedisTemplate.boundHashOps(cartKey);
return operations;
}
7. 修改购物项数量
修改"com.cwh.gulimall.cart.controller.CartController"类,代码如下:
@GetMapping("/changeItemCount")
public String changeItemCount(@RequestParam("skuId") Long skuId, @RequestParam("num") Integer num){
cartService.changeItemCount(skuId,num);
return "redirect:http://cart.gulimall.com/cart.html";
}
修改"com.cwh.gulimall.cart.service.CartService"类,代码如下:
/**
* 修改购物项数量
*
* @param skuId
* @param num
*/
void changeItemCount(Long skuId, Integer num);
修改"com.cwh.gulimall.cart.service.impl.CartServiceImpl"类,代码如下:
@Override
public void changeItemCount(Long skuId, Integer num) {
CartItem cartItem = getCartItem(skuId);
cartItem.setCount(num);
BoundHashOperations<String, Object, Object> cartOps = getCartOps();
cartOps.put(skuId.toString(),JSON.toJSONString(cartItem));
}
8. 删除购物车项
修改"com.cwh.gulimall.cart.controller.CartController"类,代码如下:
@GetMapping("/deleteItem")
public String deleteItem(@RequestParam("skuId") Long skuId){
cartService.deleteItem(skuId);
return "redirect:http://cart.gulimall.com/cart.html";
}
修改"com.cwh.gulimall.cart.service.CartService"类,代码如下:
/**
* 删除购物项
* @param skuId
*/
void deleteItem(Long skuId);
修改"com.cwh.gulimall.cart.service.impl.CartServiceImpl"类,代码如下:
@Override
public void deleteItem(Long skuId) {
BoundHashOperations<String, Object, Object> cartOps = getCartOps();
cartOps.delete(skuId.toString());
}