人生在世如身处荆棘之中,心不动,人不妄动,不动则不伤;如心动则人妄动,伤其身痛其骨,于是体会到世间诸般痛苦。
1、购物车需求
1)、需求描述:
- 用户可以在登录状态下将商品添加到购物车【用户购物车/在线购物车】
- 放入数据库
- mongodb
- 放入 redis(采用)
登录以后,会将临时购物车的数据全部合并过来,并清空临时购物车;
- 用户可以在未登录状态下将商品添加到购物车【游客购物车/离线购物车/临时购物车】
- 放入 localstorage(客户端存储,后台不存)
- cookie - WebSQL
- 放入 redis(采用)
浏览器即使关闭,下次进入,临时购物车数据都在
- 用户可以使用购物车一起结算下单
- 给购物车添加商品
- 用户可以查询自己的购物车
- 用户可以在购物车中修改购买商品的数量。
- 用户可以在购物车中删除商品。
- 选中不选中商品
- 在购物车中展示商品优惠信息
- 提示购物车商品价格变化
2)、数据结构
因此每一个购物项信息,都是一个对象,基本字段包括:
另外,购物车中不止一条数据,因此最终会是对象的数组。即:
Redis 有 5 种不同数据结构,这里选择哪一种比较合适呢?
- 首先不同用户应该有独立的购物车,因此购物车应该以用户的作为 key 来存储,Value 是 用户的所有购物车信息。这样看来基本的`k-v`结构就可以了。
- 但是,我们对购物车中的商品进行增、删、改操作,基本都需要根据商品 id 进行判断, 为了方便后期处理,我们的购物车也应该是`k-v`结构,key 是商品 id,value 才是这个商品的 购物车信息。
综上所述,我们的购物车结构是一个双层 Map:Ma<string,map<string,string>>
- 第一层 Map,Key 是用户 id
- 第二层 Map,Key 是购物车中商品 id,值是购物项数据
3)、流程
参照京东
user-key 是随机生成的 id,不管有没有登录都会有这个 cookie 信息。
两个功能:新增商品到购物车、查询购物车。
新增商品:判断是否登录
- 是:则添加商品到后台 Redis 中,把 user 的唯一标识符作为 key。
- 否:则添加商品到后台 redis 中,使用随机生成的 user-key 作为 key。
查询购物车列表:判断是否登录
- 否:直接根据 user-key 查询 redis 中数据并展示
- 是:已登录,则需要先根据 user-key 查询 redis 是否有数据。
- 有:需要提交到后台添加到 redis,合并数据,而后查询。
- 否:直接去后台查询 redis,而后返回。
2、临时购物车
1. /** 2. * 获取到我们要操作的购物车 3. * @return 4. */ 5. private BoundHashOperations<String, Object, Object> getCartOps() { 6. UserInfoTo userInfoTo = CartInterceptor.threadLocal.get(); 7. //1. 8. String cartKey = ""; 9. if (userInfoTo.getUserId() != null) { 10. cartKey = CART_PREFIX + userInfoTo.getUserId(); 11. } else { 12. cartKey = CART_PREFIX + userInfoTo.getUserKey(); 13. } 14. BoundHashOperations<String, Object, Object> operations = redisTemplate.boundHashOps(cartKey); 15. return operations; 16. }
3、登录购物车
1. @Override 2. public CartItem addToCart(Long skuId, Integer num) { 3. 4. BoundHashOperations<String, Object, Object> cartOps = getCartOps(); 5. String res = (String) cartOps.get(skuId.toString()); 6. if(StringUtils.isEmpty(res)){ 7. CartItem cartItem = new CartItem(); 8. // 1.运程查询当前要添加的商品信息 9. CompletableFuture<Void> getSkuInfoTask = CompletableFuture.runAsync(() -> { 10. //2.商品添加到购物车(新商品) 11. R skuInfo = productFeignService.getSkuInfo(skuId); 12. SkuInfoVo data = skuInfo.getData("skuInfo", new TypeReference<SkuInfoVo>() { 13. }); 14. 15. cartItem.setCheck(true); 16. cartItem.setCount(1); 17. cartItem.setImage(data.getSkuDefaultImg()); 18. cartItem.setTitle(data.getSkuTitle()); 19. cartItem.setPrice(data.getPrice()); 20. cartItem.setSkuId(data.getSkuId()); 21. },executor); 22. 23. CompletableFuture<Void> getSkuSaleAttr = CompletableFuture.runAsync(() -> { 24. //运程查询sku的组合信息 25. List<String> values = productFeignService.getSkuSaleAttrValues(skuId); 26. cartItem.setSkuAttr(values); 27. }, executor); 28. 29. CompletableFuture<Void> allOf = CompletableFuture.allOf(getSkuInfoTask, getSkuSaleAttr); 30. try { 31. allOf.get(); 32. } catch (InterruptedException e) { 33. e.printStackTrace(); 34. } catch (ExecutionException e) { 35. e.printStackTrace(); 36. } 37. String s = JSON.toJSONString(cartItem); 38. 39. cartOps.put(skuId.toString(),s); 40. return cartItem; 41. } 42. else 43. { 44. //购物车有这个商品 45. CartItem cartItem = JSON.parseObject(res, CartItem.class); 46. cartItem.setCount(cartItem.getCount()+num); 47. cartOps.put(skuId.toString(),JSON.toJSONString(cartItem)); 48. 49. return cartItem; 50. 51. } 52. 53. 54. } 55. 56. @Override 57. public CartItem getCartItem(Long skuId) { 58. BoundHashOperations<String, Object, Object> cartOps = getCartOps(); 59. String str = (String) cartOps.get(skuId.toString()); 60. CartItem cartItem = JSON.parseObject(str, CartItem.class); 61. return cartItem; 62. }
1. @Override 2. public Cart getCart() { 3. UserInfoTo userInfoTo = CartInterceptor.threadLocal.get(); 4. Cart cart = new Cart(); 5. if(userInfoTo.getUserId()!=null){ 6. //登录 7. String cartKey = CART_PREFIX+userInfoTo.getUserId(); 8. //如果临时购物车的数据还没有合并【合并购物车】 9. String tempCartKey= CART_PREFIX + userInfoTo.getUserKey(); 10. List<CartItem> tempCartItems = getCartItems(tempCartKey); 11. if(tempCartItems!=null){ 12. //合并 13. for (CartItem item : tempCartItems) { 14. addToCart(item.getSkuId(),item.getCount()); 15. } 16. } 17. //3.获取登录后的数据 18. List<CartItem> cartItems = getCartItems(cartKey); 19. cart.setItems(cartItems); 20. //清除临时购物车数据 21. clearCart(tempCartKey); 22. 23. }else { 24. //没登陆 25. String cartKey = CART_PREFIX+userInfoTo.getUserKey(); 26. //获取临时购物车的所有购物项 27. List<CartItem> cartItems = getCartItems(cartKey); 28. cart.setItems(cartItems); 29. 30. 31. } 32. return cart; 33. }
1. 2. @Override 3. public void checkItem(Long skuId, Integer check) { 4. BoundHashOperations<String, Object, Object> cartOps = getCartOps(); 5. 6. CartItem cartItem = getCartItem(skuId); 7. cartItem.setCheck(check==1?true:false); 8. String s = JSON.toJSONString(cartItem); 9. cartOps.put(skuId.toString(),s); 10. 11. } 12. 13. @Override 14. public void changeItemCount(Long skuId, Integer num) { 15. BoundHashOperations<String, Object, Object> cartOps = getCartOps(); 16. CartItem cartItem = getCartItem(skuId); 17. cartItem.setCount(num); 18. String s = JSON.toJSONString(cartItem); 19. cartOps.put(skuId.toString(),s); 20. 21. } 22. 23. @Override 24. public void deleteItem(Long skuId) { 25. BoundHashOperations<String, Object, Object> cartOps = getCartOps(); 26. cartOps.delete(skuId.toString()); 27. }
人生在世如身处荆棘之中,心不动,人不妄动,不动则不伤;如心动则人妄动,伤其身痛其骨,于是体会到世间诸般痛苦。