購物車
離線購物車
- 離線的時候儲存着使用者沒有登入時的購物車資訊
- 等使用者登入後,離線購物車的内容自動合并到登入使用者的購物車内
- 離線購物車清空
vo封裝
購物車的各個屬性都需要計算
@Datapublic class Cart {
List<CartItem> items;private Integer countNum; // 商品數量private Integer countType; // 商品類型的個數private BigDecimal totalAmount; // 目前購物車總價格private BigDecimal reduce = new BigDecimal(0); // 優惠價格public Integer getCountNum() {int count = 0;if (items != null && items.size() > 0) {for (CartItem item : items) {
count += item.getCount();
}
}
setCountNum(count);return count;
}public void setCountNum(Integer countNum) {this.countNum = countNum;
}public Integer getCountType() {int count = 0;if (items != null && items.size() > 0) {for (CartItem item : items) {
count += 1;
}
}
setCountType(count);return countType;
}public void setCountType(Integer countType) {this.countType = countType;
}public BigDecimal getTotalAmount() {
BigDecimal count = new BigDecimal(0);if (items != null && items.size() > 0) {for (CartItem item : items) {
count = count.add(item.getTotalPrice();
}
}
count = count.subtract(reduce);
setTotalAmount(count);return totalAmount;
}public void setTotalAmount(BigDecimal totalAmount) {this.totalAmount = totalAmount;
}public BigDecimal getReduce() {return reduce;
}public void setReduce(BigDecimal reduce) {this.reduce = reduce;
}
}複制代碼
@Datapublic class CartItem {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 BigDecimal getTotalPrice() {
totalPrice = price.multiply(new BigDecimal(count));return totalPrice;
}
}複制代碼
攔截器判斷使用者是否登入(threadLocal)
- 攔截器判斷使用者是否登入
- 登入儲存使用者id
- 沒登入儲存使用者user-key
- 儲存使用者資訊,共享出去
攔截器
@Componentpublic class CartInterceptor implements HandlerInterceptor {// 共享資料public static ThreadLocal<UserInfo> userInfoLocal = new ThreadLocal<>();/**
* 方法執行前
*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
UserInfo userInfo = new UserInfo();// 封裝userInfoHttpSession session = request.getSession();
MemberVo user = (MemberVo) session.getAttribute(AuthConstant.LOGIN_USER);if (user != null) {// 擷取登入使用者的購物車 -> userIduserInfo.setUserId(user.getId());
}// 擷取離線購物車 -> user-keyCookie[] cookies = request.getCookies();if (cookies != null && cookies.length > 0) {for (Cookie cookie : cookies) {if (cookie.getName().equals(CartConstant.User_COOKIE_NAME)) {
userInfo.setUserKey(cookie.getValue());
userInfo.setTemp(true);break;
}
}
}// 使用者第一次登入配置設定一個随機的user-keyif (StringUtils.isBlank(userInfo.getUserKey())) {
userInfo.setUserKey(UUID.randomUUID().toString());
}// 目标方法執行前userInfoLocal.set(userInfo);return true;
}/**
* 方法執行後
*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
UserInfo userInfo = userInfoLocal.get();// 如果是false就表明是第一次if (!userInfo.isTemp()) {
Cookie cookie = new Cookie(CartConstant.User_COOKIE_NAME, userInfo.getUserKey());
cookie.setDomain("localhost");
cookie.setMaxAge(CartConstant.COOKIE_TTL);
response.addCookie(cookie);
}
}
}複制代碼
注冊攔截器
@Configurationpublic class WebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 注冊攔截器 -> 攔截所有請求registry.addInterceptor(new CartInterceptor()).addPathPatterns("/**");
}
}複制代碼
購物車功能(redis儲存,異步編排)
controller方法
@GetMapping("/addToCart")public String addToCart(@RequestParam String skuId, @RequestParam Integer num, Model model) throws ExecutionException, InterruptedException {
CartItem cartItem = cartService.addToCart(skuId, num);
model.addAttribute("item", cartItem);return "success";
}複制代碼
service
運用了線程池以及異步編排
@Overridepublic CartItem addToCart(String skuId, Integer num) throws ExecutionException, InterruptedException {
BoundHashOperations<String, Object, Object> ops = getCartOps();
CartItem cartItem; // 判斷這個商品在購物車中是否存在Object o = ops.get(JSON.toJSONString(skuId)); // fix 儲存格式為json 是以讀取格式也要是jsonif (Objects.isNull(o)) {
cartItem = new CartItem();// 添加新商品:// 1.查詢目前要添加的商品資訊CompletableFuture<Void> getSkuInfoTask = CompletableFuture.runAsync(() -> {
R r = productFeignService.info(Long.parseLong(skuId)); // 遠端調用SkuInfoEntity info = BeanUtil.toBean(r.get("skuInfo"), SkuInfoEntity.class);
cartItem.setSkuId(info.getSkuId());
cartItem.setCheck(true);
cartItem.setTitle(info.getSkuTitle());
cartItem.setImage(info.getSkuDefaultImg());
cartItem.setPrice(info.getPrice());
cartItem.setCount(num);
cartItem.setTotalPrice(info.getPrice().multiply(new BigDecimal(num)));
}, thread);// 2.查詢屬性資訊CompletableFuture<Void> getAttrTask = CompletableFuture.runAsync(() -> {
List<String> value = productFeignService.getSkuSaleAttrValue(skuId.toString()); // 遠端調用cartItem.setSkuAttr(value);
}, thread);
CompletableFuture.allOf(getAttrTask, getSkuInfoTask).get();
} else {// 1.修改數量cartItem = (CartItem) o;
cartItem.setCount(cartItem.getCount() + num);
cartItem.setTotalPrice(cartItem.getTotalPrice());
}// 3.儲存到redis中ops.put(JSON.toJSONString(skuId), cartItem); return cartItem;
}複制代碼
擷取購物車功能
private static final String cart_prefix = "cart:";/**
* 擷取購物車
*
* @return {@link BoundHashOperations<String, Object, Object>}
*/private BoundHashOperations<String, Object, Object> getCartOps() {
UserInfo user = CartInterceptor.userInfoLocal.get();// 1.生成redis中的keyStringBuilder cartKey = new StringBuilder(cart_prefix);if (user.getUserId() != null) {
cartKey.append(user.getUserId());
} else {
cartKey.append(user.getUserKey());
}
BoundHashOperations<String, Object, Object> ops = redisTemplate.boundHashOps(cartKey.toString());return ops;
}複制代碼
功能測試
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5SYwMTN3czMiBTZlJmMxEWN3EDMmVWMxM2YwIzM4UTOy8CX3IzLcJTMwIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL2M3Lc9CX6MHc0RHaiojIsJye.png)
發送請求後:
解決頁面重新整理,再次發送請求的問題
@Overridepublic CartItem getCartItem(String skuId) {
BoundHashOperations<String, Object, Object> ops = getCartOps();
String s = (String) ops.get(JSON.toJSONString(skuId));return JSON.parseObject(s, new TypeReference<CartItem>() {});
}複制代碼
增加使用者登入後合并購物車功能
/**
* 購物車清單
* 浏覽器有一個cookie:user-key,用來表示使用者的身份
* 登入:按session
* 沒有登入:user-key
* 第一次:建立user-key
*
* @return {@link String}
*/@GetMapping("/cartList.html")public String cartList(Model model) throws ExecutionException, InterruptedException {// 擷取目前登入使用者的資訊Cart cart = cartService.getCart();
model.addAttribute("cart",cart);return "cartList";
}複制代碼
@Overridepublic Cart getCart() throws ExecutionException, InterruptedException {
UserInfo user = CartInterceptor.userInfoLocal.get();
Cart cart = new Cart();// 1.擷取離線購物車List<CartItem> items = getCartItems(cart_prefix+user.getUserKey());// 判斷離線購物車中是否有内容if (items != null && items.size() > 0) {// 2.擷取登入購物車Long userId = user.getUserId();if (userId != null) {// 3.用戶已經登入->合并購物車->清空離線購物車for (CartItem cartItem : items) {
addItemToCart(cartItem.getSkuId().toString(),cartItem.getCount()); // 合并購物車}
deleteCart(cart_prefix+ user.getUserKey()); // 清空離線購物車items = getCartItems(cart_prefix + userId); // 擷取合并後的購物車内容}
}
cart.setItems(items);return cart;
}/**
* 删除購物車
*
* @param key user key
*/private void deleteCart(String key) {
redisTemplate.delete(key);
}/**
* 根據購物項的key,擷取對應購物項
*
* @param key 關鍵
* @return {@link List<CartItem>}
*/private List<CartItem> getCartItems(String key) {
BoundHashOperations<String, Object, Object> ops = redisTemplate.boundHashOps(key);
List<Object> values = ops.values();if (values != null && values.size() > 0)return values.stream()
.map(s -> (CartItem) s)
.collect(Collectors.toList());return null;
}複制代碼
修複使用者登入後擷取購物車失敗
@Overridepublic Cart getCart() throws ExecutionException, InterruptedException {
UserInfo user = CartInterceptor.userInfoLocal.get();
System.out.println(user);
Cart cart = new Cart();// 1.擷取離線購物車List<CartItem> items = getCartItems(cart_prefix + user.getUserKey());// 判斷離線購物車中是否有内容// 2.擷取登入購物車Long userId = user.getUserId();if (userId != null) {// 3.用戶已經登入->合并購物車->清空離線購物車if (items != null && items.size() > 0) {for (CartItem cartItem : items) {
addItemToCart(cartItem.getSkuId().toString(), cartItem.getCount()); // 合并購物車}
}
deleteCart(cart_prefix + user.getUserKey()); // 清空離線購物車items = getCartItems(cart_prefix + userId); // 擷取合并後的購物車内容}
cart.setItems(items);return cart;
}複制代碼