Bläddra i källkod

feat: 部门导入增加费率配置;

hanchaolong 1 vecka sedan
förälder
incheckning
2255f7e2e5

+ 15 - 0
jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/controller/BizInvoiceController.java

@@ -77,5 +77,20 @@ public class BizInvoiceController {
         }
     }
 
+    /**
+     * 一键回传票据(异步执行)
+     * 处理所有已开票(status=3)的月度账单,回传到商城
+     */
+    @PostMapping("/batchReturnShop")
+    public AjaxResult batchReturnShop() {
+        try {
+            invoiceService.batchReturnShop();
+            return AjaxResult.success("一键回传任务已启动,正在后台异步处理中...");
+        } catch (Exception e) {
+            logger.error("一键回传票据失败", e);
+            return AjaxResult.error("启动失败:" + e.getMessage());
+        }
+    }
+
 
 }

+ 88 - 42
jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/service/impl/SysDeptRateServiceImpl.java

@@ -149,10 +149,28 @@ public class SysDeptRateServiceImpl implements ISysDeptRateService
      * @throws ServiceException 如果存在区间重叠
      */
     private static void validateIntervalOverlap(List<SysDeptRate> rates) {
-        if (rates.size() <= 1) {
+        if (rates == null || rates.isEmpty()) {
+            return;
+        }
+
+        // 按起始金额排序,便于连续性校验
+        rates.sort(Comparator.comparing(SysDeptRate::getIntervalBegins));
+
+        String companyType = rates.get(0).getCompanyType();
+        String productType = rates.get(0).getProductType();
+        String companyName = getCompanyName(companyType);
+        String productName = getProductName(productType);
+
+        // 单个规则的校验
+        if (rates.size() == 1) {
             SysDeptRate rate = rates.get(0);
             Long begins = rate.getIntervalBegins();
             Long ends = rate.getIntervalEnds();
+
+            if (begins == null || begins != 0) {
+                throw new ServiceException(String.format(
+                        "%s、%s的第一条规则起始金额必须为0", companyName, productName));
+            }
             // 检查区间有效性
             if (begins == null || ends == null) {
                 throw new ServiceException("区间开始和结束值不能为空");
@@ -160,73 +178,101 @@ public class SysDeptRateServiceImpl implements ISysDeptRateService
 
             if (begins >= ends) {
                 throw new ServiceException(String.format(
-                        "%s、%s配置的区间开始值必须小于结束值", getCompanyName(rate.getCompanyType()), getProductName(rate.getProductType())));
+                        "%s、%s配置的区间开始值必须小于结束值", companyName, productName));
+            }
+
+            if (rate.getRate() == null || rate.getRate().compareTo(new BigDecimal("0")) == 0) {
+                throw new ServiceException(String.format(
+                        "%s、%s的费率不能为0", companyName, productName));
+            }
+            return;
+        }
+
+        // 多条规则的校验
+        // 1. 第一条起始金额必须为0
+        SysDeptRate firstRate = rates.get(0);
+        if (firstRate.getIntervalBegins() == null || firstRate.getIntervalBegins() != 0) {
+            throw new ServiceException(String.format(
+                    "%s、%s的第一条规则起始金额必须为0", companyName, productName));
+        }
+
+        // 2. 校验每条规则的有效性
+        for (int i = 0; i < rates.size(); i++) {
+            SysDeptRate rate = rates.get(i);
+            Long begins = rate.getIntervalBegins();
+            Long ends = rate.getIntervalEnds();
+
+            if (begins == null || ends == null) {
+                throw new ServiceException(String.format(
+                        "%s、%s第%d条规则的区间开始和结束值不能为空", companyName, productName, i + 1));
+            }
+
+            if (begins >= ends) {
+                throw new ServiceException(String.format(
+                        "%s、%s第%d条规则的区间开始值必须小于结束值", companyName, productName, i + 1));
             }
 
-            if (rate.getRate().compareTo(new BigDecimal("0")) == 0) {
+            if (rate.getRate() == null || rate.getRate().compareTo(new BigDecimal("0")) == 0) {
                 throw new ServiceException(String.format(
-                        "%s、%s的费率不能为0", getCompanyName(rate.getCompanyType()), getProductName(rate.getProductType())));
+                        "%s、%s第%d条规则的费率不能为0", companyName, productName, i + 1));
             }
-            return; // 单个或空列表无需校验
         }
-        
-        // 两两比较所有区间
+
+        // 3. 校验连续性:当前行的起始金额必须等于上一行的结束金额
+        for (int i = 1; i < rates.size(); i++) {
+            SysDeptRate prevRate = rates.get(i - 1);
+            SysDeptRate currRate = rates.get(i);
+
+            if (!currRate.getIntervalBegins().equals(prevRate.getIntervalEnds())) {
+                throw new ServiceException(String.format(
+                        "%s、%s的规则不连续:第%d条规则结束金额为%d,第%d条规则起始金额为%d,必须相等",
+                        companyName, productName,
+                        i, prevRate.getIntervalEnds(),
+                        i + 1, currRate.getIntervalBegins()));
+            }
+        }
+
+        // 4. 校验区间重叠(虽然连续性校验已经保证了不重叠,但保留此校验作为双重保障)
         for (int i = 0; i < rates.size(); i++) {
             for (int j = i + 1; j < rates.size(); j++) {
                 SysDeptRate rate1 = rates.get(i);
                 SysDeptRate rate2 = rates.get(j);
-                
+
                 Long begins1 = rate1.getIntervalBegins();
                 Long ends1 = rate1.getIntervalEnds();
                 Long begins2 = rate2.getIntervalBegins();
                 Long ends2 = rate2.getIntervalEnds();
-                
-                // 检查区间有效性
-                if (begins1 == null || ends1 == null || begins2 == null || ends2 == null) {
-                    throw new ServiceException("区间开始和结束值不能为空");
-                }
-                
-                if (begins1 >= ends1) {
-                    throw new ServiceException(String.format(
-                        "第%d条配置的区间开始值必须小于结束值", i + 1));
-                }
-                
-                if (begins2 >= ends2) {
-                    throw new ServiceException(String.format(
-                        "第%d条配置的区间开始值必须小于结束值", j + 1));
-                }
-                
+
                 // 判断两个区间是否重叠
                 // 区间 1: (begins1, ends1]
                 // 区间 2: (begins2, ends2]
                 // 不重叠的条件:ends1 <= begins2 或 ends2 <= begins1
                 // 重叠的条件:!(ends1 <= begins2 || ends2 <= begins1)
                 boolean isOverlap = !(ends1 <= begins2 || ends2 <= begins1);
-                
-                if (isOverlap) {
-                    throw new ServiceException(String.format(
-                        "%s、%s的区间配置存在重叠:区间%d(%d,%d] 与区间%d(%d,%d]",
-                            getCompanyName(rates.get(i).getCompanyType()),
-                            getProductName(rates.get(i).getProductType()),
-                        i + 1, begins1, ends1,
-                        j + 1, begins2, ends2));
-                }
-
-                if (rate1.getRate().compareTo(new BigDecimal("0")) == 0) {
-                    throw new ServiceException(String.format(
-                            "%s、%s的费率不能为0", getCompanyName(rate1.getCompanyType()), getProductName(rate1.getProductType())));
-                }
 
-                if (rate1.getRate().compareTo(rate2.getRate()) < 0) {
+                if (isOverlap) {
                     throw new ServiceException(String.format(
-                            "%s、%s的费率配置有误:区间%d(%d,%d] 与区间%d(%d,%d]",
-                            getCompanyName(rates.get(i).getCompanyType()),
-                            getProductName(rates.get(i).getProductType()),
+                            "%s、%s的区间配置存在重叠:第%d条规则(%d,%d] 与第%d条规则(%d,%d]",
+                            companyName, productName,
                             i + 1, begins1, ends1,
                             j + 1, begins2, ends2));
                 }
             }
         }
+
+        // 5. 校验费率递减:起始金额小的区间,费率应该更大或相等
+        for (int i = 0; i < rates.size() - 1; i++) {
+            SysDeptRate currentRate = rates.get(i);
+            SysDeptRate nextRate = rates.get(i + 1);
+
+            if (currentRate.getRate().compareTo(nextRate.getRate()) < 0) {
+                throw new ServiceException(String.format(
+                        "%s、%s的费率配置有误:第%d条规则费率%.2f应大于等于第%d条规则费率%.2f",
+                        companyName, productName,
+                        i + 1, currentRate.getRate(),
+                        i + 2, nextRate.getRate()));
+            }
+        }
     }
 
     private static String getCompanyName(String companyType) {