提交订单到支付页功能实现

1.直接上流程图

提交pyspark 提交订单_数据库

2.代码实现

  • controller
/**
     * 下单功能
     *
     * @param vo
     * @return
     */
    @PostMapping(value = "/submitOrder")
    public String submitOrder(OrderSubmitVo vo, Model model, RedirectAttributes attributes) {
        try {
            SubmitOrderResponseVo responseVo = orderService.submitOrder(vo);
            // 下单成功
            if (responseVo.getCode() == 0) {
                //成功
                model.addAttribute("submitOrderResp", responseVo);
                return "pay";
            } else {
                // 下单失败
                String msg = "下单失败:";
                switch (responseVo.getCode()) {
                    case 1: msg += "令牌订单信息过期,请刷新再次提交"; break;
                    case 2: msg += "订单商品价格发生变化,请确认后再次提交"; break;
                    case 3: msg += "库存锁定失败,商品库存不足"; break;
                }
                attributes.addFlashAttribute("msg",msg);
                return "redirect:http://order.dreammall.com/toTrade";
            }
        } catch (Exception e) {
            if (e instanceof NoStockException) {
                String message = e.getMessage();
                attributes.addFlashAttribute("msg",message);
            }
            return "redirect:http://order.dreammall.com/toTrade";
        }
    }
}
  • service
@Override
    @Transactional(rollbackFor = Exception.class)
    public SubmitOrderResponseVo submitOrder(OrderSubmitVo vo) {
        submitVoThreadLocal.set(vo);
        // 获取当前用户登录的信息
        MemberResponseVo memberResponseVo = LoginUserInterceptor.loginUser.get();
        SubmitOrderResponseVo responseVo = new SubmitOrderResponseVo();
        responseVo.setCode(0);
        // 1.校验令牌token
        // 如果令牌验证通过,使用lua脚本删除redis中令牌信息,保证原子性
        String scriptStr = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(scriptStr, Long.class);
        Long result = stringRedisTemplate.execute(redisScript,
                Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberResponseVo.getId())
                , vo.getOrderToken());
        // 验证令牌失败
        if (result == 0L) {
            responseVo.setCode(1);
            return responseVo;
        } else {
            // 验证令牌成功
            // 2.创建订单
            // 2.1 创建订单数据
            OrderCreateTo orderCreateTo = createOrder();
            // 3. 验证价格 对比vo里面的价格
            if (Math.abs(vo.getPayPrice().subtract(orderCreateTo.getPayPrice()).doubleValue()) < 0.01) {
                // 3.1 对比成功,保证订单数据 order、orderItem
                saveOrder(orderCreateTo);
                // 4. 从orderItems中取出要锁定的订单
                WareSkuLockVo wareSkuLockVo = buildWareSkuLockVo(orderCreateTo);
                // 4.1 远程调用锁定库存
                R r = wareFeignService.orderLockStock(wareSkuLockVo);
                // 4.2 锁定成功 返回数据
                responseVo.setCode(r.getCode() != 0 ? 3 : 0);
                // 4.3 锁定失败 返回3
                return responseVo;
            } else {
                // 3.2 对比失败,返回2
                responseVo.setCode(2);
                return responseVo;
            }
        }
    }

    /**
     * 构建锁定订单
     *
     * @param orderCreateTo
     */
    private WareSkuLockVo buildWareSkuLockVo(OrderCreateTo orderCreateTo) {
        WareSkuLockVo wareSkuLockVo = new WareSkuLockVo();
        List<OrderItemVo> itemVoList = orderCreateTo.getOrderItems().stream().map((item) -> {
            OrderItemVo orderItemVo = new OrderItemVo();
            orderItemVo.setSkuId(item.getSkuId());
            // 购买商品数量
            orderItemVo.setCount(item.getSkuQuantity());
            orderItemVo.setTitle(item.getSkuName());
            return orderItemVo;
        }).collect(Collectors.toList());
        wareSkuLockVo.setOrderSn(orderCreateTo.getOrder().getOrderSn());
        wareSkuLockVo.setLocks(itemVoList);
        return wareSkuLockVo;
    }

    /**
     * 保存订单数据
     *
     * @param orderCreateTo
     */
    private void saveOrder(OrderCreateTo orderCreateTo) {
        save(orderCreateTo.getOrder());
        orderItemService.saveBatch(orderCreateTo.getOrderItems());
    }

    /**
     * 创建订单数据
     *
     * @return
     */
    private OrderCreateTo createOrder() {
        OrderCreateTo orderCreateTo = new OrderCreateTo();
        // 雪花算法生成订单号
        String orderSn = IdWorker.getTimeId();
        // 设置订单数据
        Order order = buildOrder(orderSn);
        orderCreateTo.setOrder(order);
        // 设置订单项数据
        List<OrderItem> orderItemList = buildOrderItems(orderSn);
        orderCreateTo.setOrderItems(orderItemList);
        // 设置运费
        orderCreateTo.setFare(order.getFreightAmount());
        // 计算价格 给order里面赋值
        computePrice(order, orderItemList);
        // 设置应付价格
        orderCreateTo.setPayPrice(order.getPayAmount());
        return orderCreateTo;
    }

    /**
     * 计算价格
     *
     * @param order
     * @param orderItemList
     */
    private void computePrice(Order order, List<OrderItem> orderItemList) {
        //总价
        BigDecimal total = new BigDecimal("0.0");
        //优惠价
        BigDecimal coupon = new BigDecimal("0.0");
        BigDecimal intergration = new BigDecimal("0.0");
        BigDecimal promotion = new BigDecimal("0.0");

        //积分、成长值
        Integer integrationTotal = 0;
        Integer growthTotal = 0;

        //订单总额,叠加每一个订单项的总额信息
        for (OrderItem orderItem : orderItemList) {
            //优惠价格信息
            coupon = coupon.add(orderItem.getCouponAmount());
            promotion = promotion.add(orderItem.getPromotionAmount());
            intergration = intergration.add(orderItem.getIntegrationAmount());

            //总价
            total = total.add(orderItem.getRealAmount());

            //积分信息和成长值信息
            integrationTotal += orderItem.getGiftIntegration();
            growthTotal += orderItem.getGiftGrowth();

        }
        //1、订单价格相关的
        order.setTotalAmount(total);
        //设置应付总额(总额+运费)
        order.setPayAmount(total.add(order.getFreightAmount()));
        order.setCouponAmount(coupon);
        order.setPromotionAmount(promotion);
        order.setIntegrationAmount(intergration);

        //设置积分成长值信息
        order.setIntegration(integrationTotal);
        order.setGrowth(growthTotal);

        //设置删除状态(0-未删除,1-已删除)
        order.setDeleteStatus(0);
    }

    /**
     * 创建订单项数据
     *
     * @param orderSn
     * @return
     */
    private List<OrderItem> buildOrderItems(String orderSn) {
        // 最后确定每个购物项的价格(最新的价格)
        List<OrderItemVo> currentCartItems = cartFeignService.getCurrentCartItems();
        return currentCartItems.stream().map(item -> {
            OrderItem orderItem = builderOrderItem(item);
            orderItem.setOrderSn(orderSn);
            return orderItem;
        }).collect(Collectors.toList());
    }

    private OrderItem builderOrderItem(OrderItemVo items) {

        OrderItem orderItemEntity = new OrderItem();

        //1、商品的spu信息
        Long skuId = items.getSkuId();
        //获取spu的信息
        R spuInfo = productFeignService.getSpuInfoBySkuId(skuId);
        SpuInfoVo spuInfoData = spuInfo.getData("data", new TypeReference<SpuInfoVo>() {});
        orderItemEntity.setSpuId(spuInfoData.getId());
        orderItemEntity.setSpuName(spuInfoData.getSpuName());
        orderItemEntity.setSpuBrand(spuInfoData.getBrandName());
        orderItemEntity.setCategoryId(spuInfoData.getCatalogId());

        //2、商品的sku信息
        orderItemEntity.setSkuId(skuId);
        orderItemEntity.setSkuName(items.getTitle());
        orderItemEntity.setSkuPic(items.getImage());
        orderItemEntity.setSkuPrice(items.getPrice());
        orderItemEntity.setSkuQuantity(items.getCount());

        //使用StringUtils.collectionToDelimitedString将list集合转换为String
        String skuAttrValues = StringUtils.collectionToDelimitedString(items.getSkuAttrValues(), ";");
        orderItemEntity.setSkuAttrsVals(skuAttrValues);

        //3、商品的优惠信息

        //4、商品的积分信息
        orderItemEntity.setGiftGrowth(items.getPrice().multiply(new BigDecimal(items.getCount())).intValue());
        orderItemEntity.setGiftIntegration(items.getPrice().multiply(new BigDecimal(items.getCount())).intValue());

        //5、订单项的价格信息
        orderItemEntity.setPromotionAmount(BigDecimal.ZERO);
        orderItemEntity.setCouponAmount(BigDecimal.ZERO);
        orderItemEntity.setIntegrationAmount(BigDecimal.ZERO);

        //当前订单项的实际金额.总额 - 各种优惠价格
        //原来的价格
        BigDecimal origin = orderItemEntity.getSkuPrice().multiply(new BigDecimal(orderItemEntity.getSkuQuantity().toString()));
        //原价减去优惠价得到最终的价格
        BigDecimal subtract = origin.subtract(orderItemEntity.getCouponAmount())
                .subtract(orderItemEntity.getPromotionAmount())
                .subtract(orderItemEntity.getIntegrationAmount());
        orderItemEntity.setRealAmount(subtract);

        return orderItemEntity;
    }


    /**
     * 创建订单信息
     *
     * @param orderSn
     * @return
     */
    private Order buildOrder(String orderSn) {

        //获取当前用户登录信息
        MemberResponseVo memberResponseVo = LoginUserInterceptor.loginUser.get();

        Order Order = new Order();
        Order.setMemberId(memberResponseVo.getId());
        Order.setOrderSn(orderSn);
        Order.setMemberUsername(memberResponseVo.getUsername());

        OrderSubmitVo orderSubmitVo = submitVoThreadLocal.get();

        //远程获取收货地址和运费信息
        R fareAddressVo = wareFeignService.getFare(orderSubmitVo.getAddrId());
        FareVo fareResp = fareAddressVo.getData(new TypeReference<FareVo>() {
        });

        //获取到运费信息
        BigDecimal fare = fareResp.getFare();
        Order.setFreightAmount(fare);

        //获取到收货地址信息
        MemberAddressVo address = fareResp.getAddress();
        //设置收货人信息
        Order.setReceiverName(address.getName());
        Order.setReceiverPhone(address.getPhone());
        Order.setReceiverPostCode(address.getPostCode());
        Order.setReceiverProvince(address.getProvince());
        Order.setReceiverCity(address.getCity());
        Order.setReceiverRegion(address.getRegion());
        Order.setReceiverDetailAddress(address.getDetailAddress());

        //设置订单相关的状态信息
        Order.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        Order.setAutoConfirmDay(7);
        Order.setConfirmStatus(0);
        return Order;
    }
  • feign远程调用锁库存
/**
     * 锁定库存
     * @param wareSkuLockVo
     * @return
     */
    @PostMapping("/lock/order")
    public R orderLockStock(@RequestBody WareSkuLockVo wareSkuLockVo){
        try {
            boolean lockStock = wareSkuService.orderLockStock(wareSkuLockVo);
            return R.ok().setData(lockStock);
        } catch (NoStockException e) {
            return R.error(BizCodeEnum.NO_STOCK_EXCEPTION.getCode(),BizCodeEnum.NO_STOCK_EXCEPTION.getMsg());
        }
    }
  • service
@Override
    @Transactional
    public boolean orderLockStock(WareSkuLockVo wareSkuLockVo) {

        List<SkuWareHasStock> collect = wareSkuLockVo.getLocks().stream().map(item -> {
            SkuWareHasStock skuWareHasStock = new SkuWareHasStock();
            skuWareHasStock.setSkuId(item.getSkuId());
            List<Long> wareIdList = baseMapper.listWareIdHasSkuStock(item.getSkuId());
            skuWareHasStock.setWareId(wareIdList);
            skuWareHasStock.setNum(item.getCount());
            return skuWareHasStock;
        }).collect(Collectors.toList());

        // 锁定库存
        for (SkuWareHasStock skuWareHasStock : collect) {
            boolean skuStocked = false;
            List<Long> wareIds = skuWareHasStock.getWareId();
            Long skuId = skuWareHasStock.getSkuId();
            // 没有库存信息 直接返回商品没有库存
            if (CollUtil.isEmpty(wareIds)) {
                throw new NoStockException(skuId);
            }
            // 扣减订单数量,如果当前仓库扣减失败,尝试下一个仓库
            for (Long wareId : wareIds) {
                Long count = baseMapper.lockSkuStock(skuId, wareId, skuWareHasStock.getNum());
                if (count == 1) {
                    skuStocked = true;
                    break;
                }
            }
            // 所有的库存都扣减失败
            if (!skuStocked) {
                throw new NoStockException(skuId);
            }
        }
        return true;
    }
  • sql
<select id="listWareIdHasSkuStock" resultType="java.lang.Long">
        SELECT ware_id FROM wms_ware_sku WHERE sku_id = #{skuId} and stock > 0
    </select>
    
        <update id="lockSkuStock">
        update wms_ware_sku set stock_locked = stock_locked + #{num} where sku_id = #{skuId} and ware_id = #{wareId} and stock-stock_locked >= #{num}
    </update>

3.缺陷

  • 保存订单数据后,redis购物车数据没有删除
  • 库存扣减后,数据没有回滚 订单数据没有被删除
  • for循环中太多远程调用,吞吐量太低