The shopping cart
Offline shopping cart
- Offline stores shopping cart information when the user is not logged in
- After the user logs in, the contents of the offline shopping cart are automatically merged into the shopping car of the logged in user
- Offline shopping cart emptying
Vo encapsulation
The various attributes of the shopping cart need to be calculated
@Data
public class Cart {
List<CartItem> items;
private Integer countNum; // Quantity of goods
private Integer countType; // The number of item types
private BigDecimal totalAmount; // Total current shopping cart price
private BigDecimal reduce = new BigDecimal(0); // Good price
public Integer getCountNum(a) {
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(a) {
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(a) {
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(a) {
return reduce;
}
public void setReduce(BigDecimal reduce) {
this.reduce = reduce; }}Copy the code
@Data
public 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(a) {
totalPrice = price.multiply(new BigDecimal(count));
returntotalPrice; }}Copy the code
Interceptor determines whether the user is logged in (threadLocal)
- The interceptor determines whether the user is logged in
- Log in to save the user ID
- No login save user user-key
- Save user information and share it
The interceptor
@Component
public class CartInterceptor implements HandlerInterceptor {
// Share data
public static ThreadLocal<UserInfo> userInfoLocal = new ThreadLocal<>();
/** * before the method is executed */
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
UserInfo userInfo = new UserInfo();
/ / encapsulates the userInfo
HttpSession session = request.getSession();
MemberVo user = (MemberVo) session.getAttribute(AuthConstant.LOGIN_USER);
if(user ! =null) {
// Get the cart of the logged-in user -> userId
userInfo.setUserId(user.getId());
}
// Get the offline shopping cart -> user-key
Cookie[] 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; }}}// Assign a random user-key to the user's first login
if (StringUtils.isBlank(userInfo.getUserKey())) {
userInfo.setUserKey(UUID.randomUUID().toString());
}
// Before the target method is executed
userInfoLocal.set(userInfo);
return true;
}
/** * after the method is executed */
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
UserInfo userInfo = userInfoLocal.get();
// If false, it is the first time
if(! userInfo.isTemp()) { Cookie cookie =new Cookie(CartConstant.User_COOKIE_NAME, userInfo.getUserKey());
cookie.setDomain("localhost"); cookie.setMaxAge(CartConstant.COOKIE_TTL); response.addCookie(cookie); }}}Copy the code
Register interceptors
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// Register interceptor -> block all requests
registry.addInterceptor(new CartInterceptor()).addPathPatterns("/ * *"); }}Copy the code
Shopping cart function (Redis save, asynchronous arrangement)
The controller method
@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";
}
Copy the code
service
Thread pooling and asynchronous choreography are used
@Override
public CartItem addToCart(String skuId, Integer num) throws ExecutionException, InterruptedException {
BoundHashOperations<String, Object, Object> ops = getCartOps();
CartItem cartItem;
// Check if the item exists in the shopping cart
Object o = ops.get(JSON.toJSONString(skuId)); // the fix format is json, so the read format is also JSON
if (Objects.isNull(o)) {
cartItem = new CartItem();
// Add a new item:
// 1. Query the information about the product to be added
CompletableFuture<Void> getSkuInfoTask = CompletableFuture.runAsync(() -> {
R r = productFeignService.info(Long.parseLong(skuId)); // Remote call
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. Query attribute information
CompletableFuture<Void> getAttrTask = CompletableFuture.runAsync(() -> {
List<String> value = productFeignService.getSkuSaleAttrValue(skuId.toString()); // Remote call
cartItem.setSkuAttr(value);
}, thread);
CompletableFuture.allOf(getAttrTask, getSkuInfoTask).get();
} else {
// 1. Modify the quantity
cartItem = (CartItem) o;
cartItem.setCount(cartItem.getCount() + num);
cartItem.setTotalPrice(cartItem.getTotalPrice());
}
// 3. Save the file to redis
ops.put(JSON.toJSONString(skuId), cartItem);
return cartItem;
}
Copy the code
Get shopping cart function
private static final String cart_prefix = "cart:";
/** * get the shopping cart **@return {@link BoundHashOperations<String, Object, Object>}
*/
private BoundHashOperations<String, Object, Object> getCartOps(a) {
UserInfo user = CartInterceptor.userInfoLocal.get();
// 1. Generate the key in redis
StringBuilder 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;
}
Copy the code
A functional test
After sending the request:
Fix the page refresh and send the request again
@Override
public 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>() {});
}
Copy the code
Added the ability to merge shopping carts after user login
/** * Shopping cart list * The browser has a cookie: user-key, which indicates the user's identity * login: press session * No login: user-key * First time: create user-key **@return {@link String}
*/
@GetMapping("/cartList.html")
public String cartList(Model model) throws ExecutionException, InterruptedException {
// Get the information of the current logged-in user
Cart cart = cartService.getCart();
model.addAttribute("cart",cart);
return "cartList";
}
Copy the code
@Override
public Cart getCart(a) throws ExecutionException, InterruptedException {
UserInfo user = CartInterceptor.userInfoLocal.get();
Cart cart = new Cart();
// 1. Get the offline shopping cart
List<CartItem> items = getCartItems(cart_prefix+user.getUserKey());
// Determine if there are contents in the offline shopping cart
if(items ! =null && items.size() > 0) {
// 2. Get the login cart
Long userId = user.getUserId();
if(userId ! =null) {
// 3. The user has logged in -> Merge shopping cart -> empty offline shopping cart
for (CartItem cartItem : items) {
addItemToCart(cartItem.getSkuId().toString(),cartItem.getCount()); // Merge shopping carts
}
deleteCart(cart_prefix+ user.getUserKey()); Empty the offline shopping cart
items = getCartItems(cart_prefix + userId); // Get the merged shopping cart contents
}
}
cart.setItems(items);
return cart;
}
/** * delete the shopping cart **@param key user key
*/
private void deleteCart(String key) {
redisTemplate.delete(key);
}
/** * Obtain the corresponding shopping item ** according to the key of the shopping item@paramThe key 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;
}
Copy the code
Fix failed to get shopping cart after user login
@Override
public Cart getCart(a) throws ExecutionException, InterruptedException {
UserInfo user = CartInterceptor.userInfoLocal.get();
System.out.println(user);
Cart cart = new Cart();
// 1. Get the offline shopping cart
List<CartItem> items = getCartItems(cart_prefix + user.getUserKey());
// Determine if there are contents in the offline shopping cart
// 2. Get the login cart
Long userId = user.getUserId();
if(userId ! =null) {
// 3. The user has logged in -> Merge shopping cart -> empty offline shopping cart
if(items ! =null && items.size() > 0) {
for (CartItem cartItem : items) {
addItemToCart(cartItem.getSkuId().toString(), cartItem.getCount()); // Merge shopping carts
}
}
deleteCart(cart_prefix + user.getUserKey()); Empty the offline shopping cart
items = getCartItems(cart_prefix + userId); // Get the merged shopping cart contents
}
cart.setItems(items);
return cart;
}
Copy the code