• 这个根据动态计算服务费的功能让我有点奔溃
  • 发布于 2个月前
  • 339 热度
    0 评论
根据算法配置,动态计算服务费,其中设计第一个运算符为运费Y与服务费F的之间的计算关系结果为R,第二个为运费Y与R之间的运算关系,当场景为单趟时计算逻辑的R取值为R,如果场景为平台计算逻辑的R则取值为配置的F,其中税率计算规则为rate =F / (1+tax),用来计算每次规则的税率,但是最终计算的R值需要再次计算rate,逻辑如此,但描述因为业务关系可能不太明朗,因为我第一次开始做的时候也是如此

算法如下图配置:

计算逻辑代码初版
     if (CollectionUtils.isNotEmpty(serviceFeeDos)) {
            var transportAmount = transportSettleDetailDO.getTransportAmount();
            var ownCarServiceFee = new BigDecimal(0);
            var ownCarServiceFeeTaxEncluded = new BigDecimal(0);
            for (TmsTransportSupplierPlatformServiceFeeDo serviceFeeDo : serviceFeeDos) {
                // 堆代码 duidaima.com
                //获取当前算法配置运算符
                var operator = serviceFeeDo.getOperator();
                AlgorithmEnum algorithmEnum = AlgorithmEnum.tEnum(operator);
                switch (algorithmEnum) {
                    case ADD:
                        ownCarServiceFee = ownCarServiceFee.add(transportAmount.add(serviceFeeDo.getNumerical()));
                        break;
                    case SUBTRACT:
                        ownCarServiceFee = ownCarServiceFee.add(transportAmount.subtract(serviceFeeDo.getNumerical()));
                        break;
                    case MULTIPLY:
                        ownCarServiceFee = ownCarServiceFee.add(transportAmount.multiply(serviceFeeDo.getNumerical()));
                        break;
                    case DIVIDE:
                        ownCarServiceFee = ownCarServiceFee.add(transportAmount.divide(serviceFeeDo.getNumerical(), 4, RoundingMode.HALF_UP));
                        break;
                }
                ownCarServiceFeeTaxEncluded = ownCarServiceFeeTaxEncluded.add(ownCarServiceFee.divide(new BigDecimal(1).add(new BigDecimal(tmsTransportSupplierRateItemDO.getTax()).divide(new BigDecimal(100))), 4, RoundingMode.HALF_UP));

                var operationalLogic = serviceFeeDo.getOperationalLogic();
                //获取当前算法配置运算关系
                AlgorithmEnum operationalLogicEnum = AlgorithmEnum.tEnum(operationalLogic);
                switch (operationalLogicEnum) {
                    case ADD:
                        transportAmount = transportAmount.add(transportAmount.add(ownCarServiceFee));
                        AssertUtil.assertTrue(transportAmount.compareTo(BigDecimal.ZERO) < 0, ErrorCodeEnum.ZY100010004);
                        break;
                    case SUBTRACT:
                        transportAmount = transportAmount.add(transportAmount.subtract(ownCarServiceFee));
                        AssertUtil.assertTrue(transportAmount.compareTo(BigDecimal.ZERO) < 0, ErrorCodeEnum.ZY100010004);
                        break;
                    case MULTIPLY:
                        transportAmount = transportAmount.add(transportAmount.multiply(ownCarServiceFee));
                        AssertUtil.assertTrue(transportAmount.compareTo(BigDecimal.ZERO) < 0, ErrorCodeEnum.ZY100010004);
                        break;
                    case DIVIDE:
                        AssertUtil.assertTrue(ownCarServiceFee.compareTo(BigDecimal.ZERO) <= 0, ErrorCodeEnum.ZY100010004);
                        transportAmount = transportAmount.add(transportAmount.divide(ownCarServiceFee));
                        AssertUtil.assertTrue(transportAmount.compareTo(BigDecimal.ZERO) < 0, ErrorCodeEnum.ZY100010004);
                        break;
                }
            }
        }
初版的问题在于算法错误,当配置多个算法配置时,取服务费F的值应为本次的值,如上图则为每次的叠加值,其免税税率也应为本次的值计算税率

计算逻辑第二版修改
   if(CollectionUtils.isNotEmpty(serviceFeeDos)){
            var transportAmount = transportSettleDetailDO.getTotalAmount();
            var totalAmountTaxEncluded = transportSettleDetailDO.getTotalAmountTaxEncluded();
            var ownCarServiceFee = new BigDecimal(0);
            var ownCarServiceFeeTaxEncluded = new BigDecimal(0);
            for (TmsTransportSupplierPlatformServiceFeeDo serviceFeeDo : serviceFeeDos) {

                Integer operator = serviceFeeDo.getOperator();
                AlgorithmEnum algorithmEnum = AlgorithmEnum.tEnum(operator);
                switch (algorithmEnum) {
                    case ADD:
                        ownCarServiceFee = transportAmount.add(serviceFeeDo.getNumerical());
                        break;
                    case SUBTRACT:
                        ownCarServiceFee = transportAmount.subtract(serviceFeeDo.getNumerical());
                        break;
                    case MULTIPLY:
                        ownCarServiceFee = transportAmount.multiply(serviceFeeDo.getNumerical());
                        break;
                    case DIVIDE:
                        AssertUtil.assertTrue(serviceFeeDo.getNumerical().compareTo(BigDecimal.ZERO) <= 0, ErrorCodeEnum.ZY100010004);
                        ownCarServiceFee = transportAmount.divide(serviceFeeDo.getNumerical(),4, RoundingMode.HALF_UP);
                        break;
                }
                ownCarServiceFeeTaxEncluded = ownCarServiceFee.divide(new BigDecimal(1).add(new BigDecimal(tmsTransportSupplierRateItemDO.getTax()).divide(new BigDecimal(100))), 4, RoundingMode.HALF_UP);

                Integer operationalLogic = serviceFeeDo.getOperationalLogic();

                AlgorithmEnum operationalLogicEnum = AlgorithmEnum.tEnum(operationalLogic);
                switch (operationalLogicEnum) {
                    case ADD:
                        transportAmount = transportAmount.add(ownCarServiceFee);
                        totalAmountTaxEncluded = totalAmountTaxEncluded.add(ownCarServiceFeeTaxEncluded);
                        AssertUtil.assertTrue(transportAmount.compareTo(BigDecimal.ZERO) < 0 || totalAmountTaxEncluded.compareTo(BigDecimal.ZERO) < 0 , ErrorCodeEnum.ZY100010004);
                        break;
                    case SUBTRACT:
                        transportAmount = transportAmount.subtract(ownCarServiceFee);
                        totalAmountTaxEncluded = totalAmountTaxEncluded.subtract(ownCarServiceFeeTaxEncluded);
                        AssertUtil.assertTrue(transportAmount.compareTo(BigDecimal.ZERO) < 0 || totalAmountTaxEncluded.compareTo(BigDecimal.ZERO) < 0, ErrorCodeEnum.ZY100010004);
                        break;
                    case MULTIPLY:
                        transportAmount = transportAmount.multiply(ownCarServiceFee);
                        totalAmountTaxEncluded = totalAmountTaxEncluded.multiply(ownCarServiceFeeTaxEncluded);
                        AssertUtil.assertTrue(transportAmount.compareTo(BigDecimal.ZERO) < 0 || totalAmountTaxEncluded.compareTo(BigDecimal.ZERO) < 0, ErrorCodeEnum.ZY100010004);
                        break;
                    case DIVIDE:
                        AssertUtil.assertTrue(ownCarServiceFee.compareTo(BigDecimal.ZERO) <= 0 || ownCarServiceFeeTaxEncluded.compareTo(BigDecimal.ZERO) <= 0, ErrorCodeEnum.ZY100010004);
                        transportAmount = transportAmount.divide(ownCarServiceFee,4, RoundingMode.HALF_UP);
                        totalAmountTaxEncluded = totalAmountTaxEncluded.divide(ownCarServiceFeeTaxEncluded,4, RoundingMode.HALF_UP);
                        AssertUtil.assertTrue(transportAmount.compareTo(BigDecimal.ZERO) < 0 || totalAmountTaxEncluded.compareTo(BigDecimal.ZERO) < 0, ErrorCodeEnum.ZY100010004);
                        break;
                }
            }
如上逻辑已经可以适配,但此时逻辑变更图片,产品发现此算法配置无法直接配置某次运费,应适配某次费用为指定费用小改动如下
 if (PlatFormServiceFeeTypeEnum.平台服务费.desc().equals(serviceFeeDo.getFeeType())) {
     ownCarServiceFee = serviceFeeDo.getNumerical();
 }
但还没有进行测试,逻辑再次变更,需要兼容税率为小数,再次改多个字段逻辑后提交,但是此时的如上图算法配置,代码直接取值第二个服务费为100,最终的服务费计算错误,其余值计算正确,第二天又又讨论此计算逻辑错误图片图片图片跟产品测试吵了一架后不欢而散,静下心来开始思考如何提取公共逻辑

如果第一次为任何场景,则取值的计算逻辑是没问题的,但是当配置多个算法,计算第i+1个的时候,其计算逻辑的通配逻辑应为(本次费用  (运算符关系(+或-)) 上一次的运费)来计算,但每次的单独费用还是取单次的计算逻辑,最终费用的税率再次进行计算;说来容易,因为最终的解决方案出来了,但是思考的过程其实吵完了后也很难纠正,因为这个逻辑是在出现第二个场景后才出现的不适配,思考找到通用的代码逻辑才能好写。

最终代码如下:

   if (CollectionUtils.isNotEmpty(serviceFeeDos)) {
            var transportAmount = transportSettleDetailDO.getTotalAmount();

            var totalAmountTaxEncluded = transportSettleDetailDO.getTotalAmountTaxEncluded();
            var ownCarServiceFee = new BigDecimal(0);
            var ownCarServiceFeeTaxEncluded = new BigDecimal(0);
            var tempOwnCarServiceFee = new BigDecimal(0);
            var tempOwnCarServiceFeeTaxEncluded = new BigDecimal(0);

            for (TmsTransportSupplierPlatformServiceFeeDo serviceFeeDo : serviceFeeDos) {
                var operator = serviceFeeDo.getOperator();
                if(serviceFeeDo.getTransportSort() > 1) {
                    tempOwnCarServiceFee = ownCarServiceFee;
                }
                AlgorithmEnum algorithmEnum = AlgorithmEnum.tEnum(operator);
                if (PlatFormServiceFeeTypeEnum.单趟运费.desc().equals(serviceFeeDo.getFeeType())) {
                    switch (algorithmEnum) {
                        case ADD:
                            ownCarServiceFee = transportAmount.add(serviceFeeDo.getNumerical());
                            break;
                        case SUBTRACT:
                            ownCarServiceFee = transportAmount.subtract(serviceFeeDo.getNumerical());
                            break;
                        case MULTIPLY:
                            ownCarServiceFee = transportAmount.multiply(serviceFeeDo.getNumerical());
                            break;
                        case DIVIDE:
                            AssertUtil.assertTrue(serviceFeeDo.getNumerical().compareTo(BigDecimal.ZERO) <= 0, ErrorCodeEnum.ZY100010004);
                            ownCarServiceFee = transportAmount.divide(serviceFeeDo.getNumerical(), 4, RoundingMode.HALF_UP);
                            break;
                    }
                }
                var operationalLogic = serviceFeeDo.getOperationalLogic();

                AlgorithmEnum operationalLogicEnum = AlgorithmEnum.tEnum(operationalLogic);

                if (PlatFormServiceFeeTypeEnum.平台服务费.desc().equals(serviceFeeDo.getFeeType())) {
                    ownCarServiceFee = serviceFeeDo.getNumerical();
                }
                ownCarServiceFeeTaxEncluded = ownCarServiceFee.divide(new BigDecimal(1).add(tmsTransportSupplierRateItemDO.getTax().divide(new BigDecimal(100))), 4, RoundingMode.HALF_UP);

                switch (operationalLogicEnum) {
                    case ADD:
                        transportAmount = transportAmount.add(ownCarServiceFee);
                        totalAmountTaxEncluded = totalAmountTaxEncluded.add(ownCarServiceFeeTaxEncluded);
                        AssertUtil.assertTrue(transportAmount.compareTo(BigDecimal.ZERO) < 0 || totalAmountTaxEncluded.compareTo(BigDecimal.ZERO) < 0, ErrorCodeEnum.ZY100010004);
                        break;
                    case SUBTRACT:
                        transportAmount = transportAmount.subtract(ownCarServiceFee);
                        totalAmountTaxEncluded = totalAmountTaxEncluded.subtract(ownCarServiceFeeTaxEncluded);
                        AssertUtil.assertTrue(transportAmount.compareTo(BigDecimal.ZERO) < 0 || totalAmountTaxEncluded.compareTo(BigDecimal.ZERO) < 0, ErrorCodeEnum.ZY100010004);
                        break;
                    case MULTIPLY:
                        transportAmount = transportAmount.multiply(ownCarServiceFee);
                        totalAmountTaxEncluded = totalAmountTaxEncluded.multiply(ownCarServiceFeeTaxEncluded);
                        AssertUtil.assertTrue(transportAmount.compareTo(BigDecimal.ZERO) < 0 || totalAmountTaxEncluded.compareTo(BigDecimal.ZERO) < 0, ErrorCodeEnum.ZY100010004);
                        break;
                    case DIVIDE:
                        AssertUtil.assertTrue(ownCarServiceFee.compareTo(BigDecimal.ZERO) <= 0 || ownCarServiceFeeTaxEncluded.compareTo(BigDecimal.ZERO) <= 0, ErrorCodeEnum.ZY100010004);
                        transportAmount = transportAmount.divide(ownCarServiceFee, 4, RoundingMode.HALF_UP);
                        totalAmountTaxEncluded = totalAmountTaxEncluded.divide(ownCarServiceFeeTaxEncluded, 4, RoundingMode.HALF_UP);
                        AssertUtil.assertTrue(transportAmount.compareTo(BigDecimal.ZERO) < 0 || totalAmountTaxEncluded.compareTo(BigDecimal.ZERO) < 0, ErrorCodeEnum.ZY100010004);
                        break;
                }
                if(serviceFeeDo.getTransportSort() > 1){
                    switch (operationalLogicEnum) {
                        case ADD:
                            ownCarServiceFee = tempOwnCarServiceFee.add(ownCarServiceFee);
                            break;
                        case SUBTRACT:
                            ownCarServiceFee = tempOwnCarServiceFee.subtract(ownCarServiceFee);
                            break;
                        case MULTIPLY:
                            ownCarServiceFee = tempOwnCarServiceFee.multiply(ownCarServiceFee);
                            break;
                        case DIVIDE:
                            AssertUtil.assertTrue(ownCarServiceFee.compareTo(BigDecimal.ZERO) <= 0 || ownCarServiceFeeTaxEncluded.compareTo(BigDecimal.ZERO) <= 0, ErrorCodeEnum.ZY100010004);
                            ownCarServiceFee = tempOwnCarServiceFee.divide(ownCarServiceFee, 4, RoundingMode.HALF_UP);
                            AssertUtil.assertTrue(transportAmount.compareTo(BigDecimal.ZERO) < 0 || totalAmountTaxEncluded.compareTo(BigDecimal.ZERO) < 0, ErrorCodeEnum.ZY100010004);
                            break;
                    }
                    tempOwnCarServiceFeeTaxEncluded = ownCarServiceFee.divide(new BigDecimal(1).add(tmsTransportSupplierRateItemDO.getTax().divide(new BigDecimal(100))), 4, RoundingMode.HALF_UP);
                }
            }
这可能是我继最短路径算法以来最恶心的代码了,多次的逻辑变更,其计算场景根本不是适配的,细微点不得不考虑,但问题就是第二天要上线的,临时改需求真的让人头大
用户评论