Day425&426.购物车服务 -谷粒商城

购物车服务

一、环境搭建

  • springboot初始化

Day425&426.购物车服务 -谷粒商城_第1张图片

Day425&426.购物车服务 -谷粒商城_第2张图片

  • 新增本地域名cart.achangmall.com

Day425&426.购物车服务 -谷粒商城_第3张图片

  • 修改对应pom配置,与此次服务间版本对应
    • 引入公共依赖
    • 修改springboot版本
    • 修改springcloud版本

Day425&426.购物车服务 -谷粒商城_第4张图片

  • 将提供的静态资源导入nginx服务,做到动静分离

Day425&426.购物车服务 -谷粒商城_第5张图片

Day425&426.购物车服务 -谷粒商城_第6张图片

  • 修改页面对应的资源地址,修改只选nginx的静态地址,如:

Day425&426.购物车服务 -谷粒商城_第7张图片

  • 主启动类com.achang.achangmall.cart.AchangmallCartApplication
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableFeignClients
@EnableDiscoveryClient
public class AchangmallCartApplication {
     
    public static void main(String[] args) {
     
        SpringApplication.run(AchangmallCartApplication.class, args);
    }
}
  • 配置文件achangmall-cart/src/main/resources/application.yaml
spring:
  cloud:
    loadbalancer:
      ribbon:
        enabled: false
    nacos:
      discovery:
        server-addr: localhost:8848
  thymeleaf:
    cache: false
  redis:
    host: 192.168.109.101
    port: 6379
  application:
    name: achangmall-cart

server:
  port: 30000
  • 网关配置路由映射achangmall-gateway/src/main/resources/application.yml
        - id: cart_route
          uri: lb://achangmall-cart
          predicates:
            - Host=cart.achangmall.com
  • 启动服务测试,访问:http://cart.achangmall.com/

Day425&426.购物车服务 -谷粒商城_第8张图片


二、业务

Day425&426.购物车服务 -谷粒商城_第9张图片

  • com.achang.achangmall.cart.vo.CartVo
/**
 * 购物车1级
 */
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 Integer getCountType() {
     
        int count = 0;
        if (items != null && items.size() > 0) {
     
            for (CartItemVo item : items) {
     
                count += 1;
            }
        }
        return count;
    }

    public BigDecimal getTotalAmount() {
     
        BigDecimal amount = new BigDecimal("0");
        // 计算购物项总价
        if (!CollectionUtils.isEmpty(items)) {
     
            for (CartItemVo cartItem : items) {
     
                if (cartItem.getCheck()) {
     
                    amount = amount.add(cartItem.getTotalPrice());
                }
            }
        }
        // 计算优惠后的价格
        return amount.subtract(getReduce());
    }

    public BigDecimal getReduce() {
     
        return reduce;
    }

    public void setReduce(BigDecimal reduce) {
     
        this.reduce = reduce;
    }
}
  • com.achang.achangmall.cart.vo.CartItemVo
/**
 * 购物车2级--购物项
 */
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 Long getSkuId() {
     
        return skuId;
    }

    public void setSkuId(Long skuId) {
     
        this.skuId = skuId;
    }

    public Boolean getCheck() {
     
        return check;
    }

    public void setCheck(Boolean check) {
     
        this.check = check;
    }

    public String getTitle() {
     
        return title;
    }

    public void setTitle(String title) {
     
        this.title = title;
    }

    public String getImage() {
     
        return image;
    }

    public void setImage(String image) {
     
        this.image = image;
    }

    public List<String> getSkuAttrValues() {
     
        return skuAttrValues;
    }

    public void setSkuAttrValues(List<String> skuAttrValues) {
     
        this.skuAttrValues = skuAttrValues;
    }

    public BigDecimal getPrice() {
     
        return price;
    }

    public void setPrice(BigDecimal price) {
     
        this.price = price;
    }

    public Integer getCount() {
     
        return count;
    }

    public void setCount(Integer count) {
     
        this.count = count;
    }

    /**
     * 计算当前购物项总价
     * @return
     */
    public BigDecimal getTotalPrice() {
     
        return this.price.multiply(new BigDecimal("" + this.count));
    }

    public void setTotalPrice(BigDecimal totalPrice) {
     
        this.totalPrice = totalPrice;
    }
}
  • com.achang.achangmall.cart.vo.SkuInfoVo
@Data
public class SkuInfoVo {
     

    private Long skuId;
    /**
     * spuId
     */
    private Long spuId;
    /**
     * sku名称
     */
    private String skuName;
    /**
     * sku介绍描述
     */
    private String skuDesc;
    /**
     * 所属分类id
     */
    private Long catalogId;
    /**
     * 品牌id
     */
    private Long brandId;
    /**
     * 默认图片
     */
    private String skuDefaultImg;
    /**
     * 标题
     */
    private String skuTitle;
    /**
     * 副标题
     */
    private String skuSubtitle;
    /**
     * 价格
     */
    private BigDecimal price;
    /**
     * 销量
     */
    private Long saleCount;
}
  • 引入依赖
<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-data-redisartifactId>
dependency>

<dependency>
    <groupId>org.springframework.sessiongroupId>
    <artifactId>spring-session-data-redisartifactId>
dependency>
  • com.achang.achangmall.cart.config.AchangmallSessionConfig
@Configuration
@EnableRedisHttpSession
public class AchangmallSessionConfig {
     
    @Bean
    public CookieSerializer cookieSerializer() {
     
        DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
        //放大作用域
        cookieSerializer.setDomainName("achangmall.com");
        cookieSerializer.setCookieName("ACHANGSESSION");
        return cookieSerializer;
    }

    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
     
        //使用json存储redis,而不是默认的序列化存储
        return new GenericJackson2JsonRedisSerializer();
    }
}
  • com.achang.achangmall.cart.config.MyThreadConfig
@EnableConfigurationProperties(ThreadPoolConfigProperties.class)
@Configuration
public class MyThreadConfig {
     
    @Bean
    public ThreadPoolExecutor executor(ThreadPoolConfigProperties pool) {
     
        return new ThreadPoolExecutor(
                pool.getCoreSize(),
                pool.getMaxSize(),
                pool.getKeepAliveTime(),
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(100000),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );
    }
}
  • com.achang.achangmall.cart.config.ThreadPoolConfigProperties
@ConfigurationProperties(prefix = "achangmall.thread")
@Data
public class ThreadPoolConfigProperties {
     
    private Integer coreSize;
    private Integer maxSize;
    private Integer keepAliveTime;
}
  • com.achang.achangmall.cart.exception.RuntimeExceptionHandler
@ControllerAdvice
public class RuntimeExceptionHandler {
     
    /**
     * 全局统一异常处理
     */
    @ExceptionHandler(RuntimeException.class)
    @ResponseBody
    public R handler(RuntimeException exception) {
     
        return R.error(exception.getMessage());
    }
    @ExceptionHandler(CartExceptionHandler.class)
    public R userHandler(CartExceptionHandler exception) {
     
        return R.error("购物车无此商品");
    }
}
  • com.achang.achangmall.cart.exception.CartExceptionHandler
public class CartExceptionHandler extends RuntimeException {
     }
  • com.achang.achangmall.cart.feign.ProductFeignService
@FeignClient("achangmall-product")
public interface ProductFeignService {
     
    /**
     * 根据skuId查询sku信息
     */
    @RequestMapping("/product/skuinfo/info/{skuId}")
    R getInfo(@PathVariable("skuId") Long skuId);

    /**
     * 根据skuId查询pms_sku_sale_attr_value表中的信息
     */
    @GetMapping(value = "/product/skusaleattrvalue/stringList/{skuId}")
    List<String> getSkuSaleAttrValues(@PathVariable("skuId") Long skuId);

    /**
     * 根据skuId查询当前商品的最新价格
     */
    @GetMapping(value = "/product/skuinfo/{skuId}/price")
    BigDecimal getPrice(@PathVariable("skuId") Long skuId);
}
  • com.achang.achangmall.cart.intercept.CartIntercept
@Data
public class UserInfoTo {
     
    private Long userId;
    private String userKey;//一定存在
    /**
     * 是否临时用户
     */
    private Boolean tempUser = false;
}
  • 拦截器com.achang.achangmall.cart.intercept.CartIntercept
@Component
public class CartIntercept implements HandlerInterceptor {
     

    public static ThreadLocal<UserInfoTo> toThreadLocal = new ThreadLocal<>();

    //在目标方法执行之前
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
     
        UserInfoTo userInfoTo = new UserInfoTo();

        HttpSession session = request.getSession();
        MemberResponseVo memberResponseVo = (MemberResponseVo) session.getAttribute(LOGIN_USER);
        if (memberResponseVo != null) {
     
            //用户登录了
            userInfoTo.setUserId(memberResponseVo.getId());
        }
        Cookie[] cookies = request.getCookies();
        if (cookies != null && cookies.length > 0) {
     
            for (Cookie cookie : cookies) {
     
                //user-key
                String name = cookie.getName();
                if (name.equals(TEMP_USER_COOKIE_NAME)) {
     
                    userInfoTo.setUserKey(cookie.getValue());
                    //标记为已是临时用户
                    userInfoTo.setTempUser(true);
                }
            }
        }
        //如果没有临时用户一定分配一个临时用户
        if (StringUtils.isEmpty(userInfoTo.getUserKey())) {
     
            String uuid = UUID.randomUUID().toString();
            userInfoTo.setUserKey(uuid);
        }

        //目标方法执行之前
        toThreadLocal.set(userInfoTo);
        return true;
    }


    //业务执行之后,分配临时用户来浏览器保存
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
     
        //获取当前用户的值
        UserInfoTo userInfoTo = toThreadLocal.get();

        //如果没有临时用户一定保存一个临时用户
        if (!userInfoTo.getTempUser()) {
     
            //创建一个cookie
            Cookie cookie = new Cookie(TEMP_USER_COOKIE_NAME, userInfoTo.getUserKey());
            //扩大作用域
            cookie.setDomain("achangmall.com");
            //设置过期时间
            cookie.setMaxAge(TEMP_USER_COOKIE_TIMEOUT);
            response.addCookie(cookie);
        }
        toThreadLocal.remove();
    }
}
  • com.achang.achangmall.cart.to.UserInfoTo
@Data
public class UserInfoTo {
     
    private Long userId;
    private String userKey;//一定存在
    /**
     * 是否临时用户
     */
    private Boolean tempUser = false;
}
  • com.achang.common.constant.CartConstant
public class CartConstant {
     
    public final static String TEMP_USER_COOKIE_NAME = "user-key";
    public final static int TEMP_USER_COOKIE_TIMEOUT = 60*60*24*30;
    public final static String CART_PREFIX = "achangmall:cart:";
}
  • com.achang.achangmall.cart.intercept.AchangmallWebConfig配置拦截器,让其生效
@Configuration
public class AchangmallWebConfig implements WebMvcConfigurer {
     
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
     
        registry.addInterceptor(new CartIntercept())//注册拦截器
            .addPathPatterns("/**");
    }
}
  • achangmall-product/src/main/resources/mapper/product/SkuSaleAttrValueDao.xml
<select id="getSkuSaleAttrValuesAsStringList" resultType="java.lang.String">
    SELECT
    CONCAT( attr_name, ":", attr_value )
    FROM
    pms_sku_sale_attr_value
    WHERE
    sku_id = #{skuId}
select>
  • com.achang.achangmall.product.service.impl.SkuSaleAttrValueServiceImpl
@Override
public List<String> getSkuSaleAttrValuesAsStringList(Long skuId) {
     

    SkuSaleAttrValueDao baseMapper = this.baseMapper;
    List<String> stringList = baseMapper.getSkuSaleAttrValuesAsStringList(skuId);

    return stringList;
}
- com.achang.achangmall.product.app.SkuSaleAttrValueController
@GetMapping(value = "/stringList/{skuId}")
public List<String> getSkuSaleAttrValues(@PathVariable("skuId") Long skuId) {
     
    List<String> stringList = skuSaleAttrValueService.getSkuSaleAttrValuesAsStringList(skuId);
    return stringList;
}
  • com.achang.achangmall.cart.controller.CartController
@Controller
public class CartController {
     
    @Resource
    private CartService cartService;
    /**
     * 获取当前用户的购物车商品项
     * @return
     */
    @GetMapping(value = "/currentUserCartItems")
    @ResponseBody
    public List<CartItemVo> getCurrentCartItems() {
     

        List<CartItemVo> cartItemVoList = cartService.getUserCartItems();

        return cartItemVoList;
    }

    /**
     * 去购物车页面的请求
     * 浏览器有一个cookie:user-key 标识用户的身份,一个月过期
     * 如果第一次使用jd的购物车功能,都会给一个临时的用户身份:
     * 浏览器以后保存,每次访问都会带上这个cookie;
     *
     * 登录:session有
     * 没登录:按照cookie里面带来user-key来做
     * 第一次,如果没有临时用户,自动创建一个临时用户
     *
     */
    @GetMapping(value = "/cart.html")
    public String cartListPage(Model model) throws ExecutionException, InterruptedException {
     
        //快速得到用户信息:id,user-key
        // UserInfoTo userInfoTo = CartInterceptor.toThreadLocal.get();
        CartVo cartVo = cartService.getCart();
        model.addAttribute("cart",cartVo);
        return "cartList";
    }

    /**
     * 添加商品到购物车
     * attributes.addFlashAttribute():将数据放在session中,可以在页面中取出,但是只能取一次
     * attributes.addAttribute():将数据放在url后面
     */
    @GetMapping(value = "/addCartItem")
    public String addCartItem(@RequestParam("skuId") Long skuId,
                              @RequestParam("num") Integer num,
                              RedirectAttributes attributes) throws ExecutionException, InterruptedException {
     
        cartService.addToCart(skuId,num);
        attributes.addAttribute("skuId",skuId);
        return "redirect:http://cart.achangmall.com/addToCartSuccessPage.html";
    }

    /**
     * 跳转到添加购物车成功页面
     */
    @GetMapping(value = "/addToCartSuccessPage.html")
    public String addToCartSuccessPage(@RequestParam("skuId") Long skuId,Model model) {
     
        //重定向到成功页面。再次查询购物车数据即可
        CartItemVo cartItemVo = cartService.getCartItem(skuId);
        model.addAttribute("cartItem",cartItemVo);
        return "success";
    }

    /**
     * 商品是否选中
     */
    @GetMapping(value = "/checkItem")
    public String checkItem(@RequestParam(value = "skuId") Long skuId,
                            @RequestParam(value = "checked") Integer checked) {
     
        cartService.checkItem(skuId,checked);
        return "redirect:http://cart.achangmall.com/cart.html";

    }

    /**
     * 改变商品数量
     */
    @GetMapping(value = "/countItem")
    public String countItem(@RequestParam(value = "skuId") Long skuId,
                            @RequestParam(value = "num") Integer num) {
     
        cartService.changeItemCount(skuId,num);
        return "redirect:http://cart.achangmall.com/cart.html";
    }

    /**
     * 删除商品信息
     */
    @GetMapping(value = "/deleteItem")
    public String deleteItem(@RequestParam("skuId") Integer skuId) {
     
        cartService.deleteIdCartInfo(skuId);
        return "redirect:http://cart.achangmall.com/cart.html";
    }
}
  • com.achang.achangmall.cart.service.impl.CartServiceImpl
@Slf4j
@Service("cartService")
public class CartServiceImpl implements CartService {
     

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    private ThreadPoolExecutor executor;

    @Override
    public CartItemVo addToCart(Long skuId, Integer num) throws ExecutionException, InterruptedException {
     

        //拿到要操作的购物车信息
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();

        //判断Redis是否有该商品的信息
        String productRedisValue = (String) cartOps.get(skuId.toString());
        //如果没有就添加数据
        if (StringUtils.isEmpty(productRedisValue)) {
     

            //2、添加新的商品到购物车(redis)
            CartItemVo cartItemVo = new CartItemVo();
            //开启第一个异步任务
            CompletableFuture<Void> getSkuInfoFuture = CompletableFuture.runAsync(() -> {
     
                //1、远程查询当前要添加商品的信息
                R productSkuInfo = productFeignService.getInfo(skuId);
                SkuInfoVo skuInfo = productSkuInfo.getData("skuInfo", new TypeReference<SkuInfoVo>() {
     });
                //数据赋值操作
                cartItemVo.setSkuId(skuInfo.getSkuId());
                cartItemVo.setTitle(skuInfo.getSkuTitle());
                cartItemVo.setImage(skuInfo.getSkuDefaultImg());
                cartItemVo.setPrice(skuInfo.getPrice());
                cartItemVo.setCount(num);
            }, executor);

            //开启第二个异步任务
            CompletableFuture<Void> getSkuAttrValuesFuture = CompletableFuture.runAsync(() -> {
     
                //2、远程查询skuAttrValues组合信息
                List<String> skuSaleAttrValues = productFeignService.getSkuSaleAttrValues(skuId);
                cartItemVo.setSkuAttrValues(skuSaleAttrValues);
            }, executor);

            //等待所有的异步任务全部完成
            CompletableFuture.allOf(getSkuInfoFuture, getSkuAttrValuesFuture).get();

            String cartItemJson = JSON.toJSONString(cartItemVo);
            cartOps.put(skuId.toString(), cartItemJson);

            return cartItemVo;
        } else {
     
            //购物车有此商品,修改数量即可
            CartItemVo cartItemVo = JSON.parseObject(productRedisValue, CartItemVo.class);
            cartItemVo.setCount(cartItemVo.getCount() + num);
            //修改redis的数据
            String cartItemJson = JSON.toJSONString(cartItemVo);
            cartOps.put(skuId.toString(),cartItemJson);

            return cartItemVo;
        }
    }

    @Override
    public CartItemVo getCartItem(Long skuId) {
     
        //拿到要操作的购物车信息
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();

        String redisValue = (String) cartOps.get(skuId.toString());

        CartItemVo cartItemVo = JSON.parseObject(redisValue, CartItemVo.class);

        return cartItemVo;
    }

    /**
     * 获取用户登录或者未登录购物车里所有的数据
     */
    @Override
    public CartVo getCart() throws ExecutionException, InterruptedException {
     

        CartVo cartVo = new CartVo();
        UserInfoTo userInfoTo = CartIntercept.toThreadLocal.get();
        if (userInfoTo.getUserId() != null) {
     
            //1、登录
            String cartKey = CART_PREFIX + userInfoTo.getUserId();
            //临时购物车的键
            String temptCartKey = CART_PREFIX + userInfoTo.getUserKey();

            //2、如果临时购物车的数据还未进行合并
            List<CartItemVo> tempCartItems = getCartItems(temptCartKey);
            if (tempCartItems != null) {
     
                //临时购物车有数据需要进行合并操作
                for (CartItemVo item : tempCartItems) {
     
                    addToCart(item.getSkuId(),item.getCount());
                }
                //清除临时购物车的数据
                clearCartInfo(temptCartKey);
            }

            //3、获取登录后的购物车数据【包含合并过来的临时购物车的数据和登录后购物车的数据】
            List<CartItemVo> cartItems = getCartItems(cartKey);
            cartVo.setItems(cartItems);

        } else {
     
            //没登录
            String cartKey = CART_PREFIX + userInfoTo.getUserKey();
            //获取临时购物车里面的所有购物项
            List<CartItemVo> cartItems = getCartItems(cartKey);
            cartVo.setItems(cartItems);
        }

        return cartVo;
    }

    /**
     * 获取到我们要操作的购物车
     */
    private BoundHashOperations<String, Object, Object> getCartOps() {
     
        //先得到当前用户信息
        UserInfoTo userInfoTo = CartIntercept.toThreadLocal.get();

        String cartKey = "";
        if (userInfoTo.getUserId() != null) {
     
            //gulimall:cart:1
            cartKey = CART_PREFIX + userInfoTo.getUserId();
        } else {
     
            cartKey = CART_PREFIX + userInfoTo.getUserKey();
        }

        //绑定指定的key操作Redis
        BoundHashOperations<String, Object, Object> operations = redisTemplate.boundHashOps(cartKey);

        return operations;
    }


    /**
     * 获取购物车里面的数据
     */
    private List<CartItemVo> getCartItems(String cartKey) {
     
        //获取购物车里面的所有商品
        BoundHashOperations<String, Object, Object> operations = redisTemplate.boundHashOps(cartKey);
        List<Object> values = operations.values();
        if (values != null && values.size() > 0) {
     
            List<CartItemVo> cartItemVoStream = values.stream().map((obj) -> {
     
                String str = (String) obj;
                CartItemVo cartItem = JSON.parseObject(str, CartItemVo.class);
                return cartItem;
            }).collect(Collectors.toList());
            return cartItemVoStream;
        }
        return null;

    }


    @Override
    public void clearCartInfo(String cartKey) {
     
        redisTemplate.delete(cartKey);
    }
    
    @Override
    public void checkItem(Long skuId, Integer check) {
     

        //查询购物车里面的商品
        CartItemVo cartItem = getCartItem(skuId);
        //修改商品状态
        cartItem.setCheck(check == 1?true:false);

        //序列化存入redis中
        String redisValue = JSON.toJSONString(cartItem);

        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        cartOps.put(skuId.toString(),redisValue);

    }

    /**
     * 修改购物项数量
     */
    @Override
    public void changeItemCount(Long skuId, Integer num) {
     

        //查询购物车里面的商品
        CartItemVo cartItem = getCartItem(skuId);
        cartItem.setCount(num);

        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        //序列化存入redis中
        String redisValue = JSON.toJSONString(cartItem);
        cartOps.put(skuId.toString(),redisValue);
    }

    /**
     * 删除购物项
     */
    @Override
    public void deleteIdCartInfo(Integer skuId) {
     

        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        cartOps.delete(skuId.toString());
    }

    @Override
    public List<CartItemVo> getUserCartItems() {
     

        List<CartItemVo> cartItemVoList = new ArrayList<>();
        //获取当前用户登录的信息
        UserInfoTo userInfoTo = CartIntercept.toThreadLocal.get();
        //如果用户未登录直接返回null
        if (userInfoTo.getUserId() == null) {
     
            return null;
        } else {
     
            //获取购物车项
            String cartKey = CART_PREFIX + userInfoTo.getUserId();
            //获取所有的
            List<CartItemVo> cartItems = getCartItems(cartKey);
            if (cartItems == null) {
     
                throw new CartExceptionHandler();
            }
            //筛选出选中的
            cartItemVoList = cartItems.stream()
                    .filter(items -> items.getCheck())
                    .map(item -> {
     
                        //更新为最新的价格(查询数据库)
                        BigDecimal price = productFeignService.getPrice(item.getSkuId());
                        item.setPrice(price);
                        return item;
                    })
                    .collect(Collectors.toList());
        }

        return cartItemVoList;
    }
}

你可能感兴趣的:(谷粒商城,谷粒商城,购物车,ThreadLocal)