谷粒商城订单服务(四十)

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

演示如下:

posted @ 2020-08-06 10:20  天宇轩-王  阅读(810)  评论(3编辑  收藏  举报