Skip to content

购物车 ​

1. 表结构分析 ​

当前项目购物车是没有表的,需要把购物车存储到Redis中,那么使用什么样的数据结构来存储购物车呢?

购物车不入MySQL数据库,所以没有表

购物车实现的方案:

  1. 存入MySQL

    优点:不会丢失数据

    缺点:速度慢,每一次操作购物车都需要读写数据库,IO操作频繁,对MySQL压力比较大

  2. 存入Redis

    优点:速度快,并发能力强

    缺点:可能丢失数据

    Redis什么时候会丢失数据呢?

    java
    // 服务器突然down机,RDB会丢失数据,AOF(出了always)之外,都会丢数据
    // 如何解决:集群-------> Redis有几种集群模式:主从模式、哨兵模式、集群模式
    
    // 内存淘汰(当前Redis内存不足的时候,再次需要存入新的数据的时候,会触发内存淘汰策略)
    // 如何解决:
    	// a,	内存淘汰策略配置为 no-evication
    	// b,	对于Redis中的一些可以设置过期时间的 key,应该尽量的主动设置过期时间,等到过期时间到了之后,主动淘汰这部分数据
  3. 先存入Redis,采用一些策略同步到MySQL

    优点:可靠性高,并发能力也强

    缺点:可能会出现MySQL和Redis数据不同步的问题

  4. 在用户没登录的时候存入cookie,在用户登录之后,把数据存入MySQL

存入Redis,考虑使用Hash数据结构存储用户的购物车。

shell
key:用户id

field:商品id

value:CartInfoDTO

image-20230322150810580

2. 业务功能 ​

2.1 添加购物车 ​

通过上图可知,在添加购物车的业务中,需要完成的接口如下

图中第一步。

功能描述:就是把商品添加到购物车,不用考虑是临时用户还是已经登录的用户

java
@RequestMapping("cart/add/{skuId}/{skuNum}")
public Result addCart(@PathVariable Long skuId, @PathVariable Integer skuNum, HttpServletRequest request) {

    // 获取用户id
    String userId = AuthContext.getUserId(request);
    if (StringUtils.isBlank(userId)) {
    	userId = AuthContext.getUserTempId(request);
    }
    
    // 添加商品到购物车
    cartService.addToCart(skuId,userId,skuNum);

    return Result.ok();
}

添加商品到购物车

java
public void addToCart(Long skuId, String userId, Integer skuNum) {
    // 这里可以使用我们定义好的前后缀常量构造购物车的key:RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX(user:userId:cart)
        
    // 获取用户购物车
    
    // 查看用户购物车中是否包含该商品
    
    // 如果包含,更新数量(根据skuId,获取购物车中的CartInfoDTO, 修改数量)
    
    // 如果不包含,添加商品到购物车(根据skuId调用商品服务(skuName, skuPrice, defaultImg) + skuNum ——> CartInfoDTO)
    
}

2.2 展示购物车列表 ​

查看用户的购物车列表

java
@GetMapping("/cart")
public Result cartList(HttpServletRequest request) {
    // 获取用户Id
    String userId = AuthContext.getUserId(request);
    // 获取临时用户Id
    String userTempId = AuthContext.getUserTempId(request);

    // 调用购物车服务,获取用户的购物车列表
    // 如果userId不为空且userTempId也不为空,那么意味着要合并购物车,并删除用户临时用户的购物车
    List<CartInfoDTO> cartInfoList = cartService.getCartList(userId, userTempId);

    return Result.ok(cartInfoList);
}

合并购物车思路:注意在获取用户购物车数据时,使用和添加购物车时的key相同

java
public List<CartInfoDTO> getCartList(String userId, String userTempId) {
    
    // 1. 如果userId为空,那么表示用户未登录,直接返回用户userTempId的临时购物车列表
    
    // 2. 如果userId不为空,那么表示用户已经登录
    
    // 3. 查询出已经登录的购物车、查询出未登录的购物车
    
    // 4. 如果未登录的购物车为空,那么直接返回已经登录的购物车
    
    // 5. 如果未登录的购物车不为空,那么需要合并 未登录和已登录的购物车,得到合并之后的购物车
    	 // 合并需要注意 同一个商品的数量和状态(合并之后的数据,放到用户登录之后的购物车)
 
    // 6. 把合并之后的购物车的商品进行排序,按照添加购物车的时间排序
    
    // 7. 更新用户userId的购物车
    
    // 8. 删除用户临时Id userTempId的购物车
    
    // 9. 返回合并之后的购物车
}

2.3 选中状态变更 &#8203;

image-20230314175053879

请求:PUT /cart/check/5/0

参数 5: 表示商品的skuId

参数 0: 表示商品在购物车中的状态,1表示选中,0表示未选中,默认为1

java
@PutMapping("cart/check/{skuId}/{isChecked}")
public Result checkCart(@PathVariable Long skuId, @PathVariable Integer isChecked,
HttpServletRequest request)
{	
    
    // TODO
    // 修改指定用户购物车中指定商品的 isChecked状态
    
	return Result.ok();
}

2.4 删除购物车 &#8203;

2.4.1 删除对应的商品 &#8203;

image-20230314180244957

请求: DELETE /cart/38

java
@DeleteMapping("/cart/{skuId}")
public Result deleteCart(@PathVariable("skuId") Long skuId, HttpServletRequest request) {
    //注意在获取用户购物车数据时,使用和添加购物车时的key相同
	// TODO 
    // 删除指定用户购物车中指定的商品
    
    return Result.ok();
    
}

2.4.2 删除已选中的商品 &#8203;

image-20230314215333009

请求: DELETE /cart/checked

java
@DeleteMapping("/cart/checked")
public Result deleteChecked(HttpServletRequest request) {
    
		// 注意在获取用户购物车数据时,使用和添加购物车时的key相同
        // 根据userId先获取购物车数据
    
       
    
        // TODO
        // 遍历购物车中的购物车商品数据,删除用户购物车中所有已经被选中的商品
        return Result.ok();
}