电商项目 - 购物车功能分析

一、拦截器功能

拦截器:在执行目标方法之前判断用户的登录状态,并封装传递给controller目标请求
用户在调用把商品添加到购物车接口之前,会先进入拦截器中,来判断当前用户登录的状态。如果没有登录,则为临时用户,后台会随机生成一个随机数分配给user-key,并存放到ThreadLocal中。

/**
 * @author aaa
 * @date 2021-11-04 16:20
 * @description 拦截器:在执行目标方法之前判断用户的登录状态,并封装传递给controller目标请求
 */
@Slf4j
public class CartInterceptor implements HandlerInterceptor {

    /**
     * ThreadLocal:同一个线程共享数据,每一个线程进来tomcat都会分配一个线程执行
     *             核心原理是一个Map<Thread,Object> :Thread是线程,Object是该线程的共享数据
     */
    public static ThreadLocal<UserInfoTo> threadLocal = new ThreadLocal<>();

    /**
     * 目标方法执行之前执行拦截
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        UserInfoTo userInfoTo = new UserInfoTo();
        HttpSession session = request.getSession();
        MemberRespVo member = (MemberRespVo) session.getAttribute(AuthServerConstant.LOGIN_USER);
        if(member != null){
            // 用户登录
            userInfoTo.setUserId(member.getId());
        }

        Cookie[] cookies = request.getCookies();
        if(cookies != null && cookies.length > 0){
            for (Cookie cookie : cookies) {
                String name = cookie.getName();
                if(CartConstant.TEMP_USER_COOKIE_NAME.equals(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;
    }

    /**
     * 业务执行之后执行的方法,分配临时用户,让浏览器保存
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        UserInfoTo userInfoTo = threadLocal.get();
        // 如果没有临时用户,一定要保存一个临时用户
        if(!userInfoTo.isTempUser()){
            Cookie cookie = new Cookie(CartConstant.TEMP_USER_COOKIE_NAME, userInfoTo.getUserKey());
            // 作用域
            cookie.setDomain("gulimall.com");
            // 过期时间
            cookie.setMaxAge(CartConstant.TEMP_USER_COOKIE_TIMEOUT);
            // 将cookie信息范湖给浏览器
            response.addCookie(cookie);
        }
        log.info("业务执行之后执行postHandle方法");
    }
}

二、购物车功能

1、添加商品到购物车的流程
/**
     * 使用多线程异步编排的方式处理
     *
     * @param skuId
     * @param num
     * @return
     */
    @Override
    public CartItem addToCart(Long skuId, Integer num) throws ExecutionException, InterruptedException {
        // 1、获取购物车
        BoundHashOperations<String, Object, Object> ops = getCartOPs();

        String res = (String) ops.get(skuId.toString());
        if (StringUtils.isBlank(res)) {
            // 2、商品添加到购物车
            CartItem cartItem = new CartItem();
            CompletableFuture<Void> getSkuInfoTask = CompletableFuture.runAsync(() -> {
                // 远程调用当前要添加的商品的详细信息
                R skuInfo = productFeignService.getSkuInfo(skuId);
                SkuInfoVo data = skuInfo.getData("skuInfo", new TypeReference<SkuInfoVo>() {
                });

                cartItem.setCheck(true);
                cartItem.setCount(num);
                cartItem.setImage(data.getSkuDefaultImg());
                cartItem.setTitle(data.getSkuTitle());
                cartItem.setPrice(data.getPrice());
                cartItem.setSkuId(skuId);
            }, executor);

            // 3、通过skuId获取销售属性
            CompletableFuture<Void> getSkuSaleAttrValuesTask = CompletableFuture.runAsync(() -> {
                List<String> skuSaleAttrValues = productFeignService.getSkuSaleAttrValues(skuId);
                cartItem.setSkuAttr(skuSaleAttrValues);
            }, executor);

            // 这里是阻塞等待,等以上两个任务都执行完了才执行下面的代码
            CompletableFuture.allOf(getSkuInfoTask, getSkuSaleAttrValuesTask).get();

            String cartItemJson = JSON.toJSONString(cartItem);
            // 将商品信息存放到redis中
            ops.put(skuId.toString(), cartItemJson);
            return cartItem;
        } else {
            // 购物车有此商品,修改商品数量即可
            CartItem cartItem = JSON.parseObject(res, CartItem.class);
            cartItem.setCount(cartItem.getCount() + num);
            // 将修改后的商品信息再存到redis中
            ops.put(skuId.toString(), JSON.toJSONString(cartItem));
            return cartItem;
        }
    }

获取购物车功能:

    /**
     * 获取购物车
     * @return
    */
/**
     * ThreadLocal:同一个线程共享数据,每一个线程进来tomcat都会分配一个线程执行
     *             核心原理是一个Map<Thread,Object> :Thread是线程,Object是该线程的共享数据
     */
    public static ThreadLocal<UserInfoTo> threadLocal = new ThreadLocal<>();
    
    private BoundHashOperations<String,Object,Object> getCartOPs(){
        UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
        String cartKey = "";
        if(userInfoTo.getUserId() != null){
            // 登录用户
            cartKey = CART_PREFIX + userInfoTo.getUserId();
        }else{
            // 临时用户
            cartKey = CART_PREFIX + userInfoTo.getUserKey();
        }
        BoundHashOperations<String, Object, Object> operations = redisTemplate.boundHashOps(cartKey);
        return operations;
    }
2、获取用户购物车的商品信息
 /**
     * 获取该用户整个购物车的信息
     * 需要判断当前用户是否登录
     * @return
     */
    @Override
    public Cart getCart() throws ExecutionException, InterruptedException {
        UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
        Cart cart = new Cart();
        if(userInfoTo.getUserId() != null){
            // 登录
            String cartKey = CART_PREFIX + userInfoTo.getUserId();
            String tempCartKey = CART_PREFIX + userInfoTo.getUserKey();
            // 查看临时购物车中是否有数据
            List<CartItem> cartItems = getCartItems(tempCartKey);
            // 如果存在数据,则将临时购物车中的数据存入到购物车中【合并】
            if(cartItems != null && !cartItems.isEmpty()){
                for (CartItem item : cartItems) {
                    addToCart(item.getSkuId(),item.getCount());
                }
                // 清空临时购物车
                clearCart(tempCartKey);
            }
            // 查询购物车中的购物项,获取登录后的购物车的数据【包含合并过来的临时购物车的数据,和登录后的购物车的数据】
            List<CartItem> items = getCartItems(cartKey);
            cart.setItems(items);
        }else{
            // 未登录
            String cartKey = CART_PREFIX + userInfoTo.getUserKey();
            // 获取临时购物车的所有购物项
            List<CartItem> cartItems = getCartItems(cartKey);
            cart.setItems(cartItems);
        }
        return cart;
    }

通过cartKey从redis中获取商品购物项的信息

/**
     * 通过cartKey从redis中获取商品购物项的信息
     * @param cartKey
     * @return
     */
    private List<CartItem> getCartItems(String cartKey){
        BoundHashOperations<String, Object, Object> hashOps = redisTemplate.boundHashOps(cartKey);
        // 获取购物车里面的商品信息
        List<Object> values = hashOps.values();
        if(values != null && !values.isEmpty()){
            List<CartItem> collect = values.stream().map((item) -> {
                CartItem cartItem = JSON.parseObject(item.toString(), CartItem.class);
                return cartItem;
            }).collect(Collectors.toList());
            return collect;
        }
        return null;
    }

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值