三级订单上的奖学金分摊错误,最后算出负数

问题复现:

 //总支付金额(不包含奖学金)
        BigDecimal sumPayedAmountNotScholarship = new BigDecimal(orderItems.stream().mapToInt(OrderAmountUtil::getOrderItemPayAmountNotScholarship).sum());

        //记录已经使用的奖学金
        BigDecimal useItemScholarShip = new BigDecimal("0");

        for (int i = 0; i < orderItems.size(); i++) {
            OrderItem orderItem = orderItems.get(i);

            //三级订单支付金额(不包含奖学金)
            BigDecimal itemPayedAmountNotScholarship = new BigDecimal(String.valueOf(OrderAmountUtil.getOrderItemPayAmountNotScholarship(orderItem)));

            if (itemPayedAmountNotScholarship.compareTo(BigDecimal.ZERO) < 0) {
                log.error("奖学金优惠后分摊金额小于0,{}", JsonUtil.toString(orderItem));
                return PlatformReturn.failure("优惠后分摊金额不能小于0,请重新下单");
            }

            //奖学金平摊金额 注意:这里必须先乘再除 否则除了之后直接变为0
            BigDecimal itemScholarShip = itemPayedAmountNotScholarship.multiply(sumScholarship).divide(sumPayedAmountNotScholarship, RoundingMode.HALF_UP);
            if (i == orderItems.size() - 1) {
                itemScholarShip = sumScholarship.subtract(useItemScholarShip);
            }

            orderItem.setScholarship(itemScholarShip.intValue());
            useItemScholarShip = useItemScholarShip.add(itemScholarShip);
        }

问题分析:

1.这里注意,BigDecimal 的 除法 和成法运算 顺序必须是 先乘后除,否则先除小数点都会直接变为0

2.这里数据库金额存的是分为单位的int类型,所以这里分摊不用BigDecaimal 参数来承接也可以,但是运算需要用BigDecimal 来进行乘除

3.由于四舍五入,itemSchlarShip 算出来的金额 可能比 useItemScholarShip 大,这也是最终算出负数的原因

4.注意这里的itemPayedAmountNotScholarship必须大于0,否则最后一笔0元订单可能分摊部分奖学金

解决方案:

//总支付金额(不包含奖学金)
        BigDecimal sumPayedAmountNotScholarship = new BigDecimal(orderItems.stream().mapToInt(OrderAmountUtil::getOrderItemPayAmountNotScholarship).sum());

        //记录已经使用的奖学金
        int useItemScholarShip = 0;

        for (int i = 0; i < orderItems.size(); i++) {
            OrderItem orderItem = orderItems.get(i);

            //三级订单支付金额(不包含奖学金)
            BigDecimal itemPayedAmountNotScholarship = new BigDecimal(String.valueOf(OrderAmountUtil.getOrderItemPayAmountNotScholarship(orderItem)));

            if (itemPayedAmountNotScholarship.compareTo(BigDecimal.ZERO) < 0) {
                log.error("奖学金优惠后分摊金额小于0,{}", JsonUtil.toString(orderItem));
                return PlatformReturn.failure("优惠后分摊金额不能小于0,请重新下单");
            }

            int itemScholarShip = 0;
            //最后一个三级订单
            if (i == orderItems.size() - 1) {
                itemScholarShip = sumScholarship.intValue() - useItemScholarShip;
            } else {
                //奖学金平摊金额 注意:这里必须先乘再除 否则除了之后直接变为0
                itemScholarShip = itemPayedAmountNotScholarship.multiply(sumScholarship).divide(sumPayedAmountNotScholarship, RoundingMode.HALF_UP).intValue();

                //判断当前三级订单平摊的奖学金,是否超出了总奖学金
                if (itemScholarShip > sumScholarship.intValue()) {
                    itemScholarShip = sumScholarship.intValue();
                }

                //判断所有三级订单合计平摊的奖学金,是否超出了总奖学金
                if (useItemScholarShip + itemScholarShip > sumScholarship.intValue()) {
                    itemScholarShip = sumScholarship.intValue() - useItemScholarShip;
                }
            }

            if (itemScholarShip < 0) {
                log.error("奖学金分摊异常,itemScholarShip < 0,context:{},orderItems:{},sumScholarship:{}",
                        JsonUtil.toString(context), JsonUtil.toString(orderItems), sumScholarship);
                return PlatformReturn.failure("网络异常,请稍候重试");
            }

            useItemScholarShip += itemScholarShip;
            orderItem.setScholarship(itemScholarShip);
        }


        //奖学金都为0,设置最后一个三级订单为奖学金总额
        if (orderItems.stream().noneMatch(a -> a.getScholarship() > 0)) {
            orderItems.get(orderItems.size() - 1).setScholarship(sumScholarship.intValue());
        }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值