栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

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

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

购物车服务 一、环境搭建
  • springboot初始化

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

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

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

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

  • 主启动类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/


二、业务

  • com.achang.achangmall.cart.vo.CartVo
public class CartVo {

    
    List items;

    
    private Integer countNum;

    
    private Integer countType;

    
    private BigDecimal totalAmount;

    
    private BigDecimal reduce = new BigDecimal("0.00");;

    public List getItems() {
        return items;
    }

    public void setItems(List 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
public class CartItemVo {

    private Long skuId;

    private Boolean check = true;

    private String title;

    private String image;

    
    private List 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 getSkuAttrValues() {
        return skuAttrValues;
    }

    public void setSkuAttrValues(List 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;
    }

    
    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;
    
    private Long spuId;
    
    private String skuName;
    
    private String skuDesc;
    
    private Long catalogId;
    
    private Long brandId;
    
    private String skuDefaultImg;
    
    private String skuTitle;
    
    private String skuSubtitle;
    
    private BigDecimal price;
    
    private Long saleCount;
}
  • 引入依赖

    org.springframework.boot
    spring-boot-starter-data-redis



    org.springframework.session
    spring-session-data-redis

  • 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 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 {
    
    @RequestMapping("/product/skuinfo/info/{skuId}")
    R getInfo(@PathVariable("skuId") Long skuId);

    
    @GetMapping(value = "/product/skusaleattrvalue/stringList/{skuId}")
    List getSkuSaleAttrValues(@PathVariable("skuId") Long 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 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("
    @GetMapping(value = "/currentUserCartItems")
    @ResponseBody
    public List getCurrentCartItems() {

        List cartItemVoList = cartService.getUserCartItems();

        return cartItemVoList;
    }

    
    @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";
    }

    
    @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 cartOps = getCartOps();

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

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

            //开启第二个异步任务
            CompletableFuture getSkuAttrValuesFuture = CompletableFuture.runAsync(() -> {
                //2、远程查询skuAttrValues组合信息
                List 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 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 tempCartItems = getCartItems(temptCartKey);
            if (tempCartItems != null) {
                //临时购物车有数据需要进行合并操作
                for (CartItemVo item : tempCartItems) {
                    addToCart(item.getSkuId(),item.getCount());
                }
                //清除临时购物车的数据
                clearCartInfo(temptCartKey);
            }

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

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

        return cartVo;
    }

    
    private BoundHashOperations 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 operations = redisTemplate.boundHashOps(cartKey);

        return operations;
    }


    
    private List getCartItems(String cartKey) {
        //获取购物车里面的所有商品
        BoundHashOperations operations = redisTemplate.boundHashOps(cartKey);
        List values = operations.values();
        if (values != null && values.size() > 0) {
            List 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 cartOps = getCartOps();
        cartOps.put(skuId.toString(),redisValue);

    }

    
    @Override
    public void changeItemCount(Long skuId, Integer num) {

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

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

    
    @Override
    public void deleteIdCartInfo(Integer skuId) {

        BoundHashOperations cartOps = getCartOps();
        cartOps.delete(skuId.toString());
    }

    @Override
    public List getUserCartItems() {

        List cartItemVoList = new ArrayList<>();
        //获取当前用户登录的信息
        UserInfoTo userInfoTo = CartIntercept.toThreadLocal.get();
        //如果用户未登录直接返回null
        if (userInfoTo.getUserId() == null) {
            return null;
        } else {
            //获取购物车项
            String cartKey = CART_PREFIX + userInfoTo.getUserId();
            //获取所有的
            List 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;
    }
}
 

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号