一、创建微服务
新增 host
1 192.168.56.10 cart.gulimall.com
静态资源
修改资源路径
VO 编写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public class CartItemVo { private Long skuId; private Boolean check = true ; private String title; private String image; private List<String> skuAttrValues; private BigDecimal price; private Integer count; private BigDecimal totalPrice; public BigDecimal getTotalPrice () { return price.multiply(new BigDecimal (count)); } public void setTotalPrice (BigDecimal totalPrice) { this .totalPrice = totalPrice; }
购物车 vo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 public class CartVo { List<CartItemVo> items; private Integer countNum; private Integer countType; private BigDecimal totalAmount; private BigDecimal reduce = new BigDecimal ("0.00" ); public List<CartItemVo> getItems () { return items; } public void setItems (List<CartItemVo> items) { this .items = items; } public Integer getCountNum () { int count=0 ; if (items != null && items.size() > 0 ) { for (CartItemVo item : items) { count += item.getCount(); } } return count; } public void setCountNum (Integer countNum) { this .countNum = countNum; } public Integer getCountType () { int count=0 ; if (items != null && items.size() > 0 ) { for (CartItemVo item : items) { count += 1 ; } } return count; } public void setCountType (Integer countType) { this .countType = countType; } public BigDecimal getTotalAmount () { BigDecimal total = new BigDecimal (0 ); if (items != null && items.size() > 0 ) { for (CartItemVo item : items) { total.add(item.getTotalPrice()); } } total.subtract(reduce); return total; } public void setTotalAmount (BigDecimal totalAmount) { this .totalAmount = totalAmount; } public BigDecimal getReduce () { return reduce; } public void setReduce (BigDecimal reduce) { this .reduce = reduce; } }
二、ThreadLocal 用户身份鉴别 1、用户身份鉴别方式 参考京东,在点击购物车时,会为临时用户生成一个name
为user-key
的cookie
临时标识,过期时间为一个月,如果手动清除user-key
,那么临时购物车的购物项也被清除,所以 user-key
是用来标识和存储临时购物车数据的
2、使用 ThreadLocal 进行用户身份鉴别信息传递 在调用购物车的接口前,先通过 session 信息判断是否登录,并分别进行用户身份信息的封住,并把user-key
放在 cookie 中 这个功能使用拦截器进行完成 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 public class CartInterceptor implements HandlerInterceptor { public static ThreadLocal<UserInfoTo> threadLocal = new ThreadLocal <>(); @Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); MemberResponseVo memberResponseVo = (MemberResponseVo) session.getAttribute(AuthServerConstant.LOGIN_USER); UserInfoTo userInfoTo = new UserInfoTo (); if (memberResponseVo!=null ){ userInfoTo.setUserId(memberResponseVo.getId()); } Cookie[] cookies = request.getCookies(); if (cookies.length > 0 ){ for (Cookie cookie : cookies) { if (cookie.getName().equals(CartConstant.TEMP_USER_COOKIE_NAME)){ userInfoTo.setUserKey(cookie.getValue()); userInfoTo.setTempUser(true ); } } } if (StringUtils.isEmpty(userInfoTo.getUserKey())){ String uuid = UUID.randomUUID().toString(); userInfoTo.setUserKey(uuid); } threadLocal.set(userInfoTo); return true ; } @Override public void postHandle (HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { UserInfoTo userInfoTo = threadLocal.get(); if (!userInfoTo.getTempUser()){ 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); } } }
三、添加商品到购物车 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @RequestMapping("/addCartItem") public String addCartItem (@RequestParam("skuId") Long skuId, @RequestParam("num") Integer num, RedirectAttributes attributes) { cartService.addCartItem(skuId, num); attributes.addAttribute("skuId" , skuId); return "redirect:http://cart.gulimall.com/addCartItemSuccess" ; } @RequestMapping("/addCartItemSuccess") public String addCartItemSuccess (@RequestParam("skuId") Long skuId,Model model) { CartItemVo cartItemVo = cartService.getCartItem(skuId); model.addAttribute("cartItem" , cartItemVo); return "success" ; }
若当前商品已经存在购物车,只需增添数量 否则需要查询商品购物项所需信息,并添加新商品至购物车 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public CartItemVo addCartItem (Long skuId, Integer num) { BoundHashOperations<String, Object, Object> ops = getCartItemOps(); String cartJson = (String) ops.get(skuId.toString()); if (!StringUtils.isEmpty(cartJson)) { CartItemVo cartItemVo = JSON.parseObject(cartJson, CartItemVo.class); cartItemVo.setCount(cartItemVo.getCount() + num); String jsonString = JSON.toJSONString(cartItemVo); ops.put(skuId.toString(), jsonString); return cartItemVo; } else { CartItemVo cartItemVo = new CartItemVo (); CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> { R info = productFeignService.info(skuId); SkuInfoVo skuInfo = info.getData("skuInfo" , new TypeReference <SkuInfoVo>() { }); cartItemVo.setCheck(true ); cartItemVo.setCount(num); cartItemVo.setImage(skuInfo.getSkuDefaultImg()); cartItemVo.setPrice(skuInfo.getPrice()); cartItemVo.setSkuId(skuId); cartItemVo.setTitle(skuInfo.getSkuTitle()); }, executor); CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> { List<String> attrValuesAsString = productFeignService.getSkuSaleAttrValuesAsString(skuId); cartItemVo.setSkuAttrValues(attrValuesAsString); }, executor); try { CompletableFuture.allOf(future1, future2).get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } String toJSONString = JSON.toJSONString(cartItemVo); ops.put(skuId.toString(), toJSONString); return cartItemVo; } }
四、 获取购物车 若用户未登录,则直接使用user-key
获取购物车数据 否则使用userId
获取购物车数据,并将user-key
对应临时购物车数据与用户购物车数据合并,并删除临时购物车 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 @RequestMapping("/cart.html") public String getCartList (Model model) { CartVo cartVo=cartService.getCart(); model.addAttribute("cart" , cartVo); return "cartList" ; } @Override public CartVo getCart () { CartVo cartVo = new CartVo (); UserInfoTo userInfoTo = CartInterceptor.threadLocal.get(); List<CartItemVo> tempCart = getCartByKey(CartConstant.CART_PREFIX + userInfoTo.getUserKey()); if (StringUtils.isEmpty(userInfoTo.getUserId())) { List<CartItemVo> cartItemVos = tempCart; cartVo.setItems(cartItemVos); }else { List<CartItemVo> userCart = getCartByKey(CartConstant.CART_PREFIX + userInfoTo.getUserId()); if (tempCart!=null &&tempCart.size()>0 ){ BoundHashOperations<String, Object, Object> ops = redisTemplate.boundHashOps(CartConstant.CART_PREFIX + userInfoTo.getUserId()); for (CartItemVo cartItemVo : tempCart) { userCart.add(cartItemVo); addCartItem(cartItemVo.getSkuId(), cartItemVo.getCount()); } } cartVo.setItems(userCart); redisTemplate.delete(CartConstant.CART_PREFIX + userInfoTo.getUserKey()); } return cartVo; }
五、选中购物车项 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @RequestMapping("/checkCart") public String checkCart (@RequestParam("isChecked") Integer isChecked,@RequestParam("skuId") Long skuId) { cartService.checkCart(skuId, isChecked); return "redirect:http://cart.gulimall.com/cart.html" ; } @Override public void checkCart (Long skuId, Integer isChecked) { BoundHashOperations<String, Object, Object> ops = getCartItemOps(); String cartJson = (String) ops.get(skuId.toString()); CartItemVo cartItemVo = JSON.parseObject(cartJson, CartItemVo.class); cartItemVo.setCheck(isChecked==1 ); ops.put(skuId.toString(),JSON.toJSONString(cartItemVo)); }
六、修改购物项数量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @RequestMapping("/countItem") public String changeItemCount (@RequestParam("skuId") Long skuId, @RequestParam("num") Integer num) { cartService.changeItemCount(skuId, num); return "redirect:http://cart.gulimall.com/cart.html" ; } @Override public void changeItemCount (Long skuId, Integer num) { BoundHashOperations<String, Object, Object> ops = getCartItemOps(); String cartJson = (String) ops.get(skuId.toString()); CartItemVo cartItemVo = JSON.parseObject(cartJson, CartItemVo.class); cartItemVo.setCount(num); ops.put(skuId.toString(),JSON.toJSONString(cartItemVo)); }
七、删除购物车项 1 2 3 4 5 6 7 8 9 10 11 @RequestMapping("/deleteItem") public String deleteItem (@RequestParam("skuId") Long skuId) { cartService.deleteItem(skuId); return "redirect:http://cart.gulimall.com/cart.html" ; } @Override public void deleteItem (Long skuId) { BoundHashOperations<String, Object, Object> ops = getCartItemOps(); ops.delete(skuId.toString()); }