zoukankan      html  css  js  c++  java
  • 谷粒商城订单服务(四十)

    261-284 订单服务

    代码提交到码云:https://gitee.com/dalianpai/gulimall

    感觉订单服务业务逻辑还是比较复杂的,改html改的头皮发麻。

    上个星期写的,没有时间简单的记录一下,让人感觉有点意思的也就Feign的请求头丢失的问题。

    ThreadLocal可以查看之前的博客:https://www.cnblogs.com/dalianpai/p/12623044.html

    /**
     * @author WGR
     * @create 2020/7/27 -- 21:52
     */
    @Component
    public class LoginUserInterceptor implements HandlerInterceptor {
    
        public static ThreadLocal<MemberRespVo> loginUser = new ThreadLocal<>();
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            MemberRespVo attribute = (MemberRespVo)request.getSession().getAttribute(AuthServerConstant.LOGIN_USER);
            if(attribute !=null){
                loginUser.set(attribute);
                return true;
            }else{
                request.getSession().setAttribute("msg","请先进行登录");
                response.sendRedirect("http://auth.gulimall.com/login.html");
                return false;
            }
        }
    }
    
    

    Feign的请求头丢失的问题:https://www.cnblogs.com/dalianpai/p/12426425.html

    /**
     * @author WGR
     * @create 2020/7/28 -- 16:01
     */
    @Configuration
    public class GuliFeignConfig {
    
        @Bean("requestInterceptor")
        public RequestInterceptor requestInterceptor(){
            return new RequestInterceptor() {
                @Override
                public void apply(RequestTemplate requestTemplate) {
                    ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                    if(attributes !=null){
                        HttpServletRequest request = attributes.getRequest();
                        if(request !=null){
                            requestTemplate.header("Cookie", request.getHeader("Cookie"));
                        }
    
                    }
                }
            };
        }
    }
    

    业务逻辑:

    
    /**
     * @author WGR
     * @create 2020/7/27 -- 22:05
     */
    @Controller
    public class OrderWenController {
    
        @Autowired
        OrderService orderService;
    
        @GetMapping("/toTrade")
        public String toTrade(Model model) throws ExecutionException, InterruptedException {
    
            OrderConfirmVo confirmVo =  orderService.confirmOrder();
    
            model.addAttribute("orderConfirmData",confirmVo);
            return "confirm";
        }
    
        @PostMapping("/submitOrder")
        public String submitOrder(OrderSubmitVo vo, Model model, RedirectAttributes redirectAttributes){
         System.out.println("订单提交的数据。。。"+vo);
         SubmitOrderResponseVo responseVo = orderService.submitOrder(vo);
         if(responseVo.getCode() ==0){
             System.out.println(responseVo);
             model.addAttribute("submitOrderResp",responseVo);
             return "pay";
         }else{
             String msg="下单失败:";
             switch (responseVo.getCode()){
                 case 1: msg += "订单信息过期,请刷新再次提交";break;
                 case 2: msg += "订单商品价格发生变化,请确认后再次提交";break;
                 case 3: msg += "库存锁定失败,商品库存不足";break;
             }
             redirectAttributes.addFlashAttribute("msg",msg);
             return "redirect:http://order.gulimall.com/toTrade";
         }
        }
    }
    
    
    @Service("orderService")
    public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {
    
        private ThreadLocal<OrderSubmitVo> confirmVoThreadLocal = new ThreadLocal<>();
        @Autowired
        MemberFeignService memberFeignService;
    
        @Autowired
        OrderItemService orderItemService;
    
        @Autowired(required = false)
        OrderDao orderDao;
    
        @Autowired(required = false)
        OrderItemDao orderItemDao;
    
        @Autowired
        CartFeignService cartFeignService;
    
        @Autowired
        WmsFeignService wmsFeignService;
    
        @Autowired
        StringRedisTemplate redisTemplate;
    
        @Autowired
        ThreadPoolExecutor executor;
    
        @Autowired
        ProductFeignService productFeignService;
    
        @Override
        public PageUtils queryPage(Map<String, Object> params) {
            IPage<OrderEntity> page = this.page(
                    new Query<OrderEntity>().getPage(params),
                    new QueryWrapper<OrderEntity>()
            );
    
            return new PageUtils(page);
        }
    
        @Override
        public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
            OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
            MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get();
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            //1.远程查询所有的收货地址列表
            CompletableFuture<Void> getCompletableFuture = CompletableFuture.runAsync(() -> {
                RequestContextHolder.setRequestAttributes(requestAttributes);
                List<MemberReceiveAddressEntity> address = memberFeignService.getAddress(memberRespVo.getId());
                System.out.println(address);
                orderConfirmVo.setAddress(address);
            }, executor);
    
            //2.远程查询购物车所有选中的购物项
            CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
                RequestContextHolder.setRequestAttributes(requestAttributes);
                List<CartItem> items = cartFeignService.getCurrentUserCartItems();
                orderConfirmVo.setItems(items);
            }, executor).thenRunAsync( () ->{
                List<CartItem> items = orderConfirmVo.getItems();
                List<Long> collect = items.stream().map(item -> item.getSkuId()).collect(Collectors.toList());
    
                R hasStock = wmsFeignService.getSkusHasStock(collect);
                List<SkuHasStockVo> data = hasStock.getData(new TypeReference<List<SkuHasStockVo>>() {
                });
                if(data !=null){
                    Map<Long, Boolean> map = data.stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, SkuHasStockVo::getHasStock));
                    orderConfirmVo.setStocks(map);
                }
            },executor);
    
            //3.查询用户积分
            Integer integration = memberRespVo.getIntegration();
            orderConfirmVo.setIntegration(integration);
    
            //5 防重令牌
            String token = UUID.randomUUID().toString().replace("-", "");
            System.out.println(OrderConstant.USER_ORDER_TOKEN_PREFIX+memberRespVo.getId());
            redisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX+memberRespVo.getId(),token,30, TimeUnit.MINUTES);
            orderConfirmVo.setOrderToken(token);
            CompletableFuture.allOf(completableFuture,getCompletableFuture).get();
            return orderConfirmVo;
        }
    
        @GlobalTransactional
        @Transactional
        @Override
        public SubmitOrderResponseVo submitOrder(OrderSubmitVo vo) {
            confirmVoThreadLocal.set(vo);
    
            SubmitOrderResponseVo responseVo = new SubmitOrderResponseVo();
            responseVo.setCode(0);
            MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get();
            String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
            String orderToken = vo.getOrderToken();
            Long result = redisTemplate.execute(new DefaultRedisScript<Long>(script,Long.class), Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PREFIX+memberRespVo.getId() ),orderToken);
            if(result ==0L){
                responseVo.setCode(1);
               return responseVo;
            }else{
                //令牌验证成功
                //下单: 去创建订单,验证令牌,验价格,锁库存
                OrderCreateTo order = createOrder();
                //2. 验价
                BigDecimal payAmount = order.getOrder().getPayAmount();
                BigDecimal payPrice = vo.getPayPrice();
                if(Math.abs(payAmount.subtract(payPrice).doubleValue())<0.01){
                    //3 保存订单
                    saveOrder(order);
                    //4 库存锁定
                    WareSkuLockVo lockVo = new WareSkuLockVo();
                    lockVo.setOrderSn(order.getOrder().getOrderSn());
                    List<CartItem> locks = order.getOrderItems().stream().map(item -> {
                        CartItem itemVo = new CartItem();
                        itemVo.setSkuId(item.getSkuId());
                        itemVo.setCount(item.getSkuQuantity());
                        itemVo.setTitle(item.getSkuName());
                        return itemVo;
                    }).collect(Collectors.toList());
                    lockVo.setLocks(locks);
                    R r = wmsFeignService.orderLockStock(lockVo);
                    if(r.getCode() == 0){
                        //锁定成功
                        responseVo.setOrder(order.getOrder());
                     //  int i = 1/0;
                        return responseVo;
                    }else{
                        //锁定失败
                        responseVo.setCode(3);
                        return responseVo;
                    }
                }else{
                    responseVo.setCode(2);
                    return responseVo;
                }
            }
        }
    
        @Transactional
        private void saveOrder(OrderCreateTo order) {
            OrderEntity orderEntity = order.getOrder();
            orderEntity.setModifyTime(new Date());
            this.save(orderEntity);
    
            List<OrderItemEntity> orderItems = order.getOrderItems();
            orderItems.forEach(l ->{
                orderItemService.save(l);
            });
         //   orderItemService.saveBatch(orderItems);
        }
    
        private OrderCreateTo createOrder(){
            OrderCreateTo ceateTo = new OrderCreateTo();
            //生成单号
            String orderSn = IdWorker.getTimeId();
            //创建订单号
            OrderEntity entity = buildOrder(orderSn);
            //2 获取到所有的订单项
            List<OrderItemEntity> itemEntities = buildOrderItems();
            //3.计算价格相关
            computePrice(entity,itemEntities);
    
            ceateTo.setOrder(entity);
            ceateTo.setOrderItems(itemEntities);
            return ceateTo;
    
        }
    
        private void computePrice(OrderEntity orderEntity, List<OrderItemEntity> itemEntities) {
            BigDecimal total = new BigDecimal("0.0");
    
            BigDecimal coupon = new BigDecimal("0.0");
            BigDecimal integration = new BigDecimal("0.0");
            BigDecimal promotion = new BigDecimal("0.0");
    
            BigDecimal gift = new BigDecimal("0.0");
            BigDecimal growth = new BigDecimal("0.0");
            for(OrderItemEntity entity:itemEntities){
                coupon = coupon.add(entity.getCouponAmount());
                integration = integration.add(entity.getIntegrationAmount());
                promotion = promotion.add(entity.getPromotionAmount());
                total = total.add(entity.getRealAmount());
                gift = gift.add(new BigDecimal(entity.getGiftIntegration().toString()));
                growth = growth.add(new BigDecimal(entity.getGiftGrowth().toString()));
            }
            orderEntity.setTotalAmount(total);
            orderEntity.setPayAmount(total.add(orderEntity.getFreightAmount()));
            orderEntity.setIntegrationAmount(integration);
            orderEntity.setCouponAmount(coupon);
    
            //设置积分等信息
            orderEntity.setIntegration(gift.intValue());
            orderEntity.setGrowth(growth.intValue());
            orderEntity.setDeleteStatus(0);
        }
    
        private OrderEntity buildOrder(String orderSn) {
            MemberRespVo respVo = LoginUserInterceptor.loginUser.get();
            OrderEntity entity = new OrderEntity();
            entity.setOrderSn(orderSn);
            entity.setMemberId(respVo.getId());
    
            OrderSubmitVo submitVo = confirmVoThreadLocal.get();
            //获取地址的收货信息
            R fare = wmsFeignService.getFare(submitVo.getAddrId());
            FareVo fareResp = fare.getData(new TypeReference<FareVo>() {
            });
            entity.setFreightAmount(fareResp.getFare());
            entity.setReceiverCity(fareResp.getAddress().getCity());
            entity.setReceiverDetailAddress(fareResp.getAddress().getDetailAddress());
            entity.setReceiverName(fareResp.getAddress().getName());
            entity.setReceiverPhone(fareResp.getAddress().getPhone());
            entity.setReceiverPostCode(fareResp.getAddress().getPostCode());
            entity.setReceiverProvince(fareResp.getAddress().getProvince());
            entity.setReceiverRegion(fareResp.getAddress().getRegion());
    
            //设置订单的相关状态信息
            entity.setStatus(1);
            entity.setAutoConfirmDay(7);
            return entity;
        }
    
        /**
         * 构建所有订单项数据
         */
        private List<OrderItemEntity> buildOrderItems(){
            List<CartItem> currentUserCartItems = cartFeignService.getCurrentUserCartItems();
            if(currentUserCartItems !=null && currentUserCartItems.size()>0){
                List<OrderItemEntity> itemEntities = currentUserCartItems.stream().map(cartItem ->{
                    OrderItemEntity itemEntity =buildOrderItem(cartItem);
                    return itemEntity;
                }).collect(Collectors.toList());
                return itemEntities;
            }
            return null ;
        }
    
        private OrderItemEntity buildOrderItem(CartItem cartItem) {
            OrderItemEntity itemEntity = new OrderItemEntity();
            Long skuId = cartItem.getSkuId();
    
            R r = productFeignService.getSpuInfoBySkuId(skuId);
            SpuInfoVo data = r.getData(new TypeReference<SpuInfoVo>() {
            });
            itemEntity.setSpuId(data.getId());
            itemEntity.setSpuBrand(data.getBrandId().toString());
            itemEntity.setSpuName(data.getSpuName());
            itemEntity.setCategoryId(data.getCatalogId());
            //3.商品的sku信息
            itemEntity.setSkuId(cartItem.getSkuId());
            itemEntity.setSkuName(cartItem.getTitle());
            itemEntity.setSkuPic(cartItem.getImages());
            itemEntity.setSkuPrice(cartItem.getPrice());
            String skuAttr = StringUtils.collectionToDelimitedString(cartItem.getSkuAttr(), ";");
            itemEntity.setSkuAttrsVals(skuAttr);
            itemEntity.setSkuQuantity(cartItem.getCount());
    
            //5.积分信息
            itemEntity.setGiftGrowth(cartItem.getPrice().multiply(new BigDecimal(cartItem.getCount().toString())).intValue());
            itemEntity.setGiftIntegration(cartItem.getPrice().multiply(new BigDecimal(cartItem.getCount().toString())).intValue());
    
            //6.订单项的价格信息
            itemEntity.setPromotionAmount(new BigDecimal("0"));
            itemEntity.setCouponAmount(new BigDecimal("0"));
            itemEntity.setIntegrationAmount(new BigDecimal("0"));
            //当前订单项的实际金额。总额-各种优惠
            BigDecimal orign = itemEntity.getSkuPrice().multiply(new BigDecimal(itemEntity.getSkuQuantity().toString()));
            BigDecimal subtract = orign.subtract(itemEntity.getCouponAmount())
                    .subtract(itemEntity.getPromotionAmount())
                    .subtract(itemEntity.getIntegrationAmount());
            itemEntity.setRealAmount(subtract);
            return itemEntity;
        }
    
    }
    

    锁库存的服务:

        @Override
        public List<SkuHasStockVo> getSkusHasStock(List<Long> skuIds) {
            List<SkuHasStockVo> hasStockVoList = skuIds.stream().map(id -> {
                SkuHasStockVo vo = new SkuHasStockVo();
                Long count = baseMapper.getSkuStock(id);
                vo.setSkuId(id);
                vo.setHasStock(count ==null ?false:count > 0);
                return vo;
            }).collect(Collectors.toList());
            return hasStockVoList;
        }
    
        @Transactional
        @Override
        public Boolean orderLockStock(WareSkuLockVo vo) {
            //1. 找到每个商品在哪个仓库都有库存
            List<CartItem> locks = vo.getLocks();
            List<SkuWareHasStock> collect = locks.stream().map(item -> {
                SkuWareHasStock stock = new SkuWareHasStock();
                Long skuId = item.getSkuId();
                stock.setSkuId(skuId);
                stock.setNum(item.getCount());
                //查询这个商品在哪里有库存
                List<Long> wareIds = wareSkuDao.listWareIdHasSkuStock(skuId);
                stock.setWareId(wareIds);
                return stock;
            }).collect(Collectors.toList());
    
            //2. 锁定库存
            for(SkuWareHasStock hasStock:collect){
                Boolean skuStocked = false;
                Long skuId = hasStock.getSkuId();
                List<Long> wareIds = hasStock.getWareId();
                if(wareIds == null || wareIds.size() ==0){
                    throw new RRException(skuId+"不存在");
                }
                for (Long wareId : wareIds){
                    //成功就是1,失败就是0
                    Long count = wareSkuDao.lockSkuStock(skuId, wareId, hasStock.getNum());
                    if(count == 1){
                        skuStocked = true;
                        break;
                    }else{
                        //当前仓库锁失败,重试下一个仓库
                    }
    
                }
    
                if(skuStocked == false){
                    throw new RRException(skuId+"不存在");
                }
            }
    
            return true;
        }
    
        @Data
        class SkuWareHasStock{
            private Long skuId;
            private List<Long> wareId;
            private Integer num;
        }
    

    注意演示的时候把feign的超时时间都改大点,不然老是报错,就很烦

    feign:
      client:
        config:
          gulimall-ware:
            readTimeout: 12000
            connectTimeout: 12000
            loggerLevel: FULL
          gulimall-member:
            readTimeout: 12000
            connectTimeout: 12000
            loggerLevel: FULL
          gulimall-product:
            readTimeout: 12000
            connectTimeout: 12000
            loggerLevel: FULL
          gulimall-cart:
            readTimeout: 12000
            connectTimeout: 12000
            loggerLevel: FULL
    

    演示如下:

  • 相关阅读:
    centos6.5安装mysql5.7.20
    redis错误总结
    批量杀死MySQL连接的四种方法详解
    VMware虚拟机宿主机与虚拟机通讯慢解决方法
    linux下ssh/scp无密钥登陆方法
    天兔3.8安装 centos7
    《TensorFlow+Keras自然语言处理实战》已出版
    几本技术图书资源下载
    2020年上半年新书
    《Neo4j 图数据库扩展指南:APOC和ALGO》
  • 原文地址:https://www.cnblogs.com/dalianpai/p/13444608.html
Copyright © 2011-2022 走看看