学习笔记(19)购物车
购物车有两种情况:
在线购物车;
离线购物车;
离线购物车:
只要每次看购物车。都有一个user-key=uuid;如果这个user-key丢了,购物车就没了。
如果我们要看购物车肯定会带上这个user-key的cookie;如果有就用自带的,没有就新建一个以后用这个。
登录以后离线的和在线的合并了,并且清空了离线购物车里面的数据。
京东怎么做购物车?
购物车有一个cookie对应的key关联,即使没有登录也有,登录以后进行购物车合并,合并完成后删除购物车数据;该系统也设计离线在线购物车。
离线购物车:永远用 cart-key=uuid;购物车数据都在redis中提升系统的性能。cart:temp: uuid;
在线购物车:cart:1={};放在redis中;cart:user:1;
抽取购物项:
private BigDecimal totalPrice;//当前购物项总价
//算价格用BigDecimal ,否则带小数的运算有问题
用hash存储,key为skuid,value为json字符串。
想改1号商品数量,拿Cart:user:1 查到整个购物车,然后get出1号商品,得到json字符串,反序列化成CartItem,把CartItem中的数量改成5,然后json重新覆盖。
用k-v最大的另一个好处,可以多加一个字段checked ,value是数组,这样不用遍历购物车就可获取选中商品。
最终购物车的结构:
reddison 的分布式集合:
做分布式一定要序列化接口
测试Reddsion:
@Test
public void userRedissonMap(){
RMap<String, String> cart = redissonClient.getMap("cart");
CartItem item = new CartItem();
item.setPrice(new BigDecimal("12.98"));
item.setSkuId(1L);
item.setCount(1);
String s = JSON.toJSONString(item);
cart.put("2",s);
}
测试后redis如下:
/**
* 根据accessToken查询用户信息
*/
public Member getMemberByAccessToken(String accessToken){
String userJson = redisTemplate.opsForValue().get(SysCacheConstant.LOGIN_MEMBER + accessToken);
return JSON.parseObject(userJson, Member.class);
}
//系统中存用户的json
//经常要用,封装成组件
@Component
public class MemberComponent {
@Autowired
StringRedisTemplate redisTemplate;
public UserCartKey getCartKey(String accessToken, String cartKey){
UserCartKey userCartKey = new UserCartKey();
Member member = null;
if(!StringUtils.isEmpty(accessToken)){
member = getMemberByAccessToken(accessToken);
}
if(member!=null){
//获取到了在线用户;用户登录用这个
userCartKey.setLogin(true);
userCartKey.setUserId(member.getId());
userCartKey.setFinalCartKey(CartConstant.USER_CART_KEY_PREFIX+member.getId());
return userCartKey;
}else if(!StringUtils.isEmpty(cartKey)){
//用户有临时的用这个
userCartKey.setLogin(false);
userCartKey.setFinalCartKey(CartConstant.TEMP_CART_KEY_PREFIX+cartKey);
return userCartKey;
}else {
//用户既没有登录也没有零时购物车
String replace = UUID.randomUUID().toString().replace("-", "");
userCartKey.setLogin(false);
userCartKey.setFinalCartKey(CartConstant.TEMP_CART_KEY_PREFIX+replace);
userCartKey.setTempCartKey(replace);
return userCartKey;
}
}
/**
* 根据accessToken查询用户信息
*/
public Member getMemberByAccessToken(String accessToken){
String userJson = redisTemplate.opsForValue().get(SysCacheConstant.LOGIN_MEMBER + accessToken);
return JSON.parseObject(userJson, Member.class);
}
}
几种购物车:
String oldCartKey = CartConstant.TEMP_CART_KEY_PREFIX+cartKey;
String userCartKey = CartConstant.USER_CART_KEY_PREFIX+id.toString();
if(member!=null){
//获取到了在线用户;用户登录用这个
userCartKey.setLogin(true);
userCartKey.setUserId(member.getId());
userCartKey.setFinalCartKey(CartConstant.USER_CART_KEY_PREFIX+member.getId());
return userCartKey;
}else if(!StringUtils.isEmpty(cartKey)){
//用户有临时的用这个
userCartKey.setLogin(false);
userCartKey.setFinalCartKey(CartConstant.TEMP_CART_KEY_PREFIX+cartKey);
return userCartKey;
}else {
//用户既没有登录也没有零时购物车
String replace = UUID.randomUUID().toString().replace("-", "");
userCartKey.setLogin(false);
userCartKey.setFinalCartKey(CartConstant.TEMP_CART_KEY_PREFIX+replace);
userCartKey.setTempCartKey(replace);
return userCartKey;
}
购物车postman测试:
直接写类似于双写,写map其实是在写缓存。
回写,把map写完后,开个异步任务,写缓存。
//自动续期 ,防止购物车撑爆
//redisTemplate.expire(finalCartKey,30L, TimeUnit.DAYS);
HashSet改成LinkedHashSet,也是有序的了,添加物品时有序,如上图的1,2,3,4各行按操作顺序排列。
//删除购物车先维护购物车状态,再删json。若反,删除了json也没有了,再改json数据有问题
@Override
public CartResponse delCartItem(Long skuId, String cartKey, String accessToken) {
UserCartKey userCartKey = memberComponent.getCartKey(accessToken, cartKey);
String finalCartKey = userCartKey.getFinalCartKey();
//维护购物项的checked状态
checkItem(Arrays.asList(skuId),false,finalCartKey);
//获取购物车删除购物项
RMap<String, String> map = redissonClient.getMap(finalCartKey);
map.remove(skuId.toString());
//整个购物车再返回出去
CartResponse cartResponse = listCart(cartKey, accessToken);
return cartResponse;
}
1)、购物车数据保存在redis中,使用分布式集合;【redisson.getMap】
2)、用户对于购物车的所有操作,都需要传入cart-key,如果用户登录了,还需要传入token
购物车需要提供的所有方法;
1)、添加到购物车
2)、修改购物项
3)、删除购物项
4)、选中/选不中购物项
5)、返回整个购物车
6)、点击去结算-获取购物车中需要结算的数据