Browse Source

✨ CRM:code review 客户导入

YunaiV 1 year ago
parent
commit
e53a0ca884
33 changed files with 273 additions and 213 deletions
  1. 2 2
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
  2. 15 17
      yudao-framework/yudao-spring-boot-starter-biz-ip/src/main/java/cn/iocoder/yudao/framework/ip/core/utils/AreaUtils.java
  3. 2 2
      yudao-framework/yudao-spring-boot-starter-biz-ip/src/main/java/cn/iocoder/yudao/framework/ip/core/utils/IPUtils.java
  4. 1 1
      yudao-framework/yudao-spring-boot-starter-biz-ip/src/test/java/cn/iocoder/yudao/framework/ip/core/utils/AreaUtilsTest.java
  5. 1 1
      yudao-framework/yudao-spring-boot-starter-excel/pom.xml
  6. 1 1
      yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/AreaConvert.java
  7. 32 24
      yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java
  8. 1 9
      yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java
  9. 1 0
      yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
  10. 2 10
      yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/receivable/CrmReceivableReturnTypeEnum.java
  11. 18 17
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java
  12. 0 1
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java
  13. 0 2
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java
  14. 27 32
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java
  15. 13 22
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanSaveReqVO.java
  16. 2 2
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableSaveReqVO.java
  17. 2 2
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivableDO.java
  18. 34 23
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivablePlanDO.java
  19. 2 5
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java
  20. 18 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/redis/RedisKeyConstants.java
  21. 52 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/redis/no/CrmNoRedisDAO.java
  22. 11 4
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
  23. 6 4
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java
  24. 4 1
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java
  25. 1 14
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmAuditStatusUtils.java
  26. 1 1
      yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/RedisKeyConstants.java
  27. 1 1
      yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/no/ErpNoRedisDAO.java
  28. 14 1
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApi.java
  29. 3 10
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApiImpl.java
  30. 1 1
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/AreaController.java
  31. 1 1
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/ip/AppAreaController.java
  32. 1 1
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataService.java
  33. 3 1
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceImpl.java

+ 2 - 2
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java

@@ -257,11 +257,11 @@ public class CollectionUtils {
         return !CollectionUtil.isEmpty(from) ? from.get(0) : null;
     }
 
-    public static <T> T findFirst(List<T> from, Predicate<T> predicate) {
+    public static <T> T findFirst(Collection<T> from, Predicate<T> predicate) {
         return findFirst(from, predicate, Function.identity());
     }
 
-    public static <T, U> U findFirst(List<T> from, Predicate<T> predicate, Function<T, U> func) {
+    public static <T, U> U findFirst(Collection<T> from, Predicate<T> predicate, Function<T, U> func) {
         if (CollUtil.isEmpty(from)) {
             return null;
         }

+ 15 - 17
yudao-framework/yudao-spring-boot-starter-biz-ip/src/main/java/cn/iocoder/yudao/framework/ip/core/utils/AreaUtils.java

@@ -4,7 +4,6 @@ import cn.hutool.core.io.resource.ResourceUtil;
 import cn.hutool.core.lang.Assert;
 import cn.hutool.core.text.csv.CsvRow;
 import cn.hutool.core.text.csv.CsvUtil;
-import cn.hutool.core.util.ObjUtil;
 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
 import cn.iocoder.yudao.framework.ip.core.Area;
 import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum;
@@ -72,26 +71,25 @@ public class AreaUtils {
      * @param id 区域编号
      * @return 区域
      */
-    public static Area getArea(Integer id) {
+    public static Area parseArea(Integer id) {
         return areas.get(id);
     }
 
     /**
      * 获得指定区域对应的编号
      *
-     * @param path 区域编号
-     * @return 编号
+     * @param pathStr 区域路径,例如说:河南省/石家庄市/新华区
+     * @return 区域
      */
-    public static Area getArea(String path) {
-        String[] paths = path.split("/");
+    public static Area parseArea(String pathStr) {
+        String[] paths = pathStr.split("/");
         Area area = null;
-        for (int i = 0; i < paths.length; i++) {
-            final int finalI = i;
+        for (String path : paths) {
             if (area == null) {
-                area = findFirst(convertList(areas.values(), a -> a), item -> ObjUtil.equal(paths[finalI], item.getName()));
-                continue;
+                area = findFirst(areas.values(), item -> item.getName().equals(path));
+            } else {
+                area = findFirst(area.getChildren(), item -> item.getName().equals(path));
             }
-            area = findFirst(area.getChildren(), item -> ObjUtil.equal(paths[finalI], item.getName()));
         }
         return area;
     }
@@ -102,9 +100,9 @@ public class AreaUtils {
      * @param areas 地区树
      * @return 所有节点的全路径名称
      */
-    public static List<String> getAllAreaNodePaths(List<Area> areas) {
+    public static List<String> getAreaNodePathList(List<Area> areas) {
         List<String> paths = new ArrayList<>();
-        areas.forEach(area -> traverse(area, "", paths));
+        areas.forEach(area -> getAreaNodePathList(area, "", paths));
         return paths;
     }
 
@@ -113,9 +111,9 @@ public class AreaUtils {
      *
      * @param node  父节点
      * @param path  全路径名称
-     * @param paths 全路径名称列表
+     * @param paths 全路径名称列表,省份/城市/地区
      */
-    private static void traverse(Area node, String path, List<String> paths) {
+    private static void getAreaNodePathList(Area node, String path, List<String> paths) {
         if (node == null) {
             return;
         }
@@ -124,7 +122,7 @@ public class AreaUtils {
         paths.add(currentPath);
         // 递归遍历子节点
         for (Area child : node.getChildren()) {
-            traverse(child, currentPath, paths);
+            getAreaNodePathList(child, currentPath, paths);
         }
     }
 
@@ -195,7 +193,7 @@ public class AreaUtils {
      */
     public static Integer getParentIdByType(Integer id, @NonNull AreaTypeEnum type) {
         for (int i = 0; i < Byte.MAX_VALUE; i++) {
-            Area area = AreaUtils.getArea(id);
+            Area area = AreaUtils.parseArea(id);
             if (area == null) {
                 return null;
             }

+ 2 - 2
yudao-framework/yudao-spring-boot-starter-biz-ip/src/main/java/cn/iocoder/yudao/framework/ip/core/utils/IPUtils.java

@@ -72,7 +72,7 @@ public class IPUtils {
      * @return 地区
      */
     public static Area getArea(String ip) {
-        return AreaUtils.getArea(getAreaId(ip));
+        return AreaUtils.parseArea(getAreaId(ip));
     }
 
     /**
@@ -82,6 +82,6 @@ public class IPUtils {
      * @return 地区
      */
     public static Area getArea(long ip) {
-        return AreaUtils.getArea(getAreaId(ip));
+        return AreaUtils.parseArea(getAreaId(ip));
     }
 }

+ 1 - 1
yudao-framework/yudao-spring-boot-starter-biz-ip/src/test/java/cn/iocoder/yudao/framework/ip/core/utils/AreaUtilsTest.java

@@ -17,7 +17,7 @@ public class AreaUtilsTest {
     @Test
     public void testGetArea() {
         // 调用:北京
-        Area area = AreaUtils.getArea(110100);
+        Area area = AreaUtils.parseArea(110100);
         // 断言
         assertEquals(area.getId(), 110100);
         assertEquals(area.getName(), "北京市");

+ 1 - 1
yudao-framework/yudao-spring-boot-starter-excel/pom.xml

@@ -49,7 +49,7 @@
         <dependency>
             <groupId>cn.iocoder.boot</groupId>
             <artifactId>yudao-spring-boot-starter-biz-ip</artifactId>
-            <scope>provided</scope> <!-- 设置为 provided,只有 ExcelUtils 使用 -->
+            <optional>true</optional> <!-- 设置为 optional,只有在 AreaConvert 的时候使用 -->
         </dependency>
     </dependencies>
 

+ 1 - 1
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/AreaConvert.java

@@ -33,7 +33,7 @@ public class AreaConvert implements Converter<Object> {
                                     GlobalConfiguration globalConfiguration) {
         // 解析地区编号
         String label = readCellData.getStringValue();
-        Area area = AreaUtils.getArea(label);
+        Area area = AreaUtils.parseArea(label);
         if (area == null) {
             log.error("[convertToJavaData][label({}) 解析不掉]", label);
             return null;

+ 32 - 24
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java

@@ -19,33 +19,40 @@ import java.util.List;
  */
 public class SelectSheetWriteHandler implements SheetWriteHandler {
 
+    private static final String DICT_SHEET_NAME = "字典sheet";
+
+    // TODO @puhui999:key 不使用 int 值么?感觉不是很优雅哈。
     private final List<KeyValue<Integer, List<String>>> selectMap;
 
     private static final char[] ALPHABET = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
             'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
 
     public SelectSheetWriteHandler(List<KeyValue<Integer, List<String>>> selectMap) {
+        if (CollUtil.isEmpty(selectMap)) {
+            this.selectMap = null;
+            return;
+        }
         selectMap.sort(Comparator.comparing(item -> item.getValue().size())); // 升序不然创建下拉会报错
         this.selectMap = selectMap;
     }
 
     @Override
     public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
-        if (selectMap == null || CollUtil.isEmpty(selectMap)) {
+        if (CollUtil.isEmpty(selectMap)) {
             return;
         }
-        // 需要设置下拉框的sheet页
-        Sheet curSheet = writeSheetHolder.getSheet();
-        DataValidationHelper helper = curSheet.getDataValidationHelper();
-        String dictSheetName = "字典sheet";
+        // 需要设置下拉框的 sheet 页
+        Sheet currentSheet = writeSheetHolder.getSheet();
+        DataValidationHelper helper = currentSheet.getDataValidationHelper();
         Workbook workbook = writeWorkbookHolder.getWorkbook();
-        // 数据字典的sheet页
-        Sheet dictSheet = workbook.createSheet(dictSheetName);
+
+        // 数据字典的 sheet 页
+        Sheet dictSheet = workbook.createSheet(DICT_SHEET_NAME);
         for (KeyValue<Integer, List<String>> keyValue : selectMap) {
             // 设置下拉单元格的首行、末行、首列、末列
             CellRangeAddressList rangeAddressList = new CellRangeAddressList(1, 65533, keyValue.getKey(), keyValue.getKey());
             int rowLen = keyValue.getValue().size();
-            // 设置字典sheet页的值 每一列一个字典项
+            // 设置字典 sheet 页的值 每一列一个字典项
             for (int i = 0; i < rowLen; i++) {
                 Row row = dictSheet.getRow(i);
                 if (row == null) {
@@ -53,18 +60,18 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
                 }
                 row.createCell(keyValue.getKey()).setCellValue(keyValue.getValue().get(i));
             }
-            String excelColumn = getExcelColumn(keyValue.getKey());
-            // 下拉框数据来源 eg:字典sheet!$B1:$B2
-            String refers = dictSheetName + "!$" + excelColumn + "$1:$" + excelColumn + "$" + rowLen;
-            // 创建可被其他单元格引用的名称
+
+            // TODO @puhui999:下面 1. 2.1 2.2 2.3 我是按照已经理解的,调整了下格式;这样可读性更好;在 52 到 62 行,你可以看看,是不是也弄下序号;
+            // 1. 创建可被其他单元格引用的名称
             Name name = workbook.createName();
-            // 设置名称的名字
-            name.setNameName("dict" + keyValue.getKey());
-            // 设置公式
-            name.setRefersToFormula(refers);
-            // 设置引用约束
-            DataValidationConstraint constraint = helper.createFormulaListConstraint("dict" + keyValue.getKey());
-            // 设置约束
+            // TODO @puhui999:下面的 excelColumn 和 refers 两行,是不是可以封装成一个方法,替代 getExcelColumn;
+            String excelColumn = getExcelColumn(keyValue.getKey());
+            String refers = DICT_SHEET_NAME + "!$" + excelColumn + "$1:$" + excelColumn + "$" + rowLen; // 下拉框数据来源 eg:字典sheet!$B1:$B2
+            name.setNameName("dict" + keyValue.getKey()); // 设置名称的名字
+            name.setRefersToFormula(refers); // 设置公式
+
+            // 2.1 设置约束
+            DataValidationConstraint constraint = helper.createFormulaListConstraint("dict" + keyValue.getKey()); // 设置引用约束
             DataValidation validation = helper.createValidation(constraint, rangeAddressList);
             if (validation instanceof HSSFDataValidation) {
                 validation.setSuppressDropDownArrow(false);
@@ -72,10 +79,10 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
                 validation.setSuppressDropDownArrow(true);
                 validation.setShowErrorBox(true);
             }
-            // 阻止输入非下拉框的值
+            // 2.2 阻止输入非下拉框的值
             validation.setErrorStyle(DataValidation.ErrorStyle.STOP);
             validation.createErrorBox("提示", "此值不存在于下拉选择中!");
-            // 添加下拉框约束
+            // 2.3 添加下拉框约束
             writeSheetHolder.getSheet().addValidationData(validation);
         }
     }
@@ -86,8 +93,9 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
      * @param num 数字
      * @return 字母
      */
+    // TODO @puhui999:这个是必须字母列哇?还是数字其实也可以哈?主要想看看,怎么能把这个逻辑,进一步简化
     private String getExcelColumn(int num) {
-        String column = "";
+        String column;
         int len = ALPHABET.length - 1;
         int first = num / len;
         int second = num % len;
@@ -96,9 +104,9 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
         } else {
             column = ALPHABET[first - 1] + "";
             if (second == 0) {
-                column = column + ALPHABET[len] + "";
+                column = column + ALPHABET[len];
             } else {
-                column = column + ALPHABET[second - 1] + "";
+                column = column + ALPHABET[second - 1];
             }
         }
         return column;

+ 1 - 9
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java

@@ -33,15 +33,7 @@ public class ExcelUtils {
      */
     public static <T> void write(HttpServletResponse response, String filename, String sheetName,
                                  Class<T> head, List<T> data) throws IOException {
-        // 输出 Excel
-        EasyExcel.write(response.getOutputStream(), head)
-                .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理
-                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度
-                .registerConverter(new LongStringConverter()) // 避免 Long 类型丢失精度
-                .sheet(sheetName).doWrite(data);
-        // 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了
-        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, StandardCharsets.UTF_8.name()));
-        response.setContentType("application/vnd.ms-excel;charset=UTF-8");
+        write(response, filename, sheetName, head, data, null);
     }
 
     /**

+ 1 - 0
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java

@@ -14,6 +14,7 @@ public interface ErrorCodeConstants {
     ErrorCode CONTRACT_UPDATE_FAIL_NOT_DRAFT = new ErrorCode(1_020_000_001, "合同更新失败,原因:合同不是草稿状态");
     ErrorCode CONTRACT_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_020_000_002, "合同提交审核失败,原因:合同没处在未提交状态");
     ErrorCode CONTRACT_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS = new ErrorCode(1_020_000_003, "更新合同审核状态失败,原因:合同不是审核中状态");
+    ErrorCode CONTRACT_NO_EXISTS = new ErrorCode(1_020_000_004, "生成合同序列号重复,请重试");
 
     // ========== 线索管理 1-020-001-000 ==========
     ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索不存在");

+ 2 - 10
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/receivable/CrmReturnTypeEnum.java → yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/receivable/CrmReceivableReturnTypeEnum.java

@@ -13,26 +13,18 @@ import java.util.Arrays;
  */
 @Getter
 @AllArgsConstructor
-public enum CrmReturnTypeEnum implements IntArrayValuable {
+public enum CrmReceivableReturnTypeEnum implements IntArrayValuable {
 
-    // 支票
     CHECK(1, "支票"),
-    // 现金
     CASH(2, "现金"),
-    // 邮政汇款
     POSTAL_REMITTANCE(3, "邮政汇款"),
-    // 电汇
     TELEGRAPHIC_TRANSFER(4, "电汇"),
-    // 网上转账
     ONLINE_TRANSFER(5, "网上转账"),
-    // 支付宝
     ALIPAY(6, "支付宝"),
-    // 微信支付
     WECHAT_PAY(7, "微信支付"),
-    // 其他
     OTHER(8, "其它");
 
-    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmReturnTypeEnum::getType).toArray();
+    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmReceivableReturnTypeEnum::getType).toArray();
 
     /**
      * 类型

+ 18 - 17
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java

@@ -268,6 +268,24 @@ public class CrmCustomerController {
         ExcelUtils.write(response, "客户导入模板.xls", "客户列表", CrmCustomerImportExcelVO.class, list, builderSelectMap());
     }
 
+    private List<KeyValue<Integer, List<String>>> builderSelectMap() {
+        List<KeyValue<Integer, List<String>>> selectMap = new ArrayList<>();
+        // 获取地区下拉数据
+        // TODO @puhui999:嘿嘿,这里改成省份、城市、区域,三个选项,难度大么?
+        Area area = AreaUtils.parseArea(Area.ID_CHINA);
+        selectMap.add(new KeyValue<>(6, AreaUtils.getAreaNodePathList(area.getChildren())));
+        // 获取客户所属行业
+        List<String> customerIndustries = dictDataApi.getDictDataLabelList(CRM_CUSTOMER_INDUSTRY);
+        selectMap.add(new KeyValue<>(8, customerIndustries));
+        // 获取客户等级
+        List<String> customerLevels = dictDataApi.getDictDataLabelList(CRM_CUSTOMER_LEVEL);
+        selectMap.add(new KeyValue<>(9, customerLevels));
+        // 获取客户来源
+        List<String> customerSources = dictDataApi.getDictDataLabelList(CRM_CUSTOMER_SOURCE);
+        selectMap.add(new KeyValue<>(10, customerSources));
+        return selectMap;
+    }
+
     @PostMapping("/import")
     @Operation(summary = "导入客户")
     @PreAuthorize("@ss.hasPermission('system:customer:import')")
@@ -321,21 +339,4 @@ public class CrmCustomerController {
         return success(true);
     }
 
-    private List<KeyValue<Integer, List<String>>> builderSelectMap() {
-        List<KeyValue<Integer, List<String>>> selectMap = new ArrayList<>();
-        // 获取地区下拉数据
-        Area area = AreaUtils.getArea(Area.ID_CHINA);
-        selectMap.add(new KeyValue<>(6, AreaUtils.getAllAreaNodePaths(area.getChildren())));
-        // 获取客户所属行业
-        List<String> customerIndustries = dictDataApi.getDictDataLabelList(CRM_CUSTOMER_INDUSTRY);
-        selectMap.add(new KeyValue<>(8, customerIndustries));
-        // 获取客户等级
-        List<String> customerLevels = dictDataApi.getDictDataLabelList(CRM_CUSTOMER_LEVEL);
-        selectMap.add(new KeyValue<>(9, customerLevels));
-        // 获取客户来源
-        List<String> customerSources = dictDataApi.getDictDataLabelList(CRM_CUSTOMER_SOURCE);
-        selectMap.add(new KeyValue<>(10, customerSources));
-        return selectMap;
-    }
-
 }

+ 0 - 1
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java

@@ -107,7 +107,6 @@ public class CrmReceivableController {
         return success(buildReceivableDetailPage(pageResult));
     }
 
-    // TODO 芋艿:后面在优化导出
     @GetMapping("/export-excel")
     @Operation(summary = "导出回款 Excel")
     @PreAuthorize("@ss.hasPermission('crm:receivable:export')")

+ 0 - 2
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java

@@ -28,7 +28,6 @@ import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.annotation.Resource;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.validation.Valid;
-import org.springframework.context.annotation.Lazy;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
@@ -56,7 +55,6 @@ public class CrmReceivablePlanController {
     @Resource
     private CrmReceivableService receivableService;
     @Resource
-    @Lazy
     private CrmContractService contractService;
     @Resource
     private CrmCustomerService customerService;

+ 27 - 32
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java

@@ -6,6 +6,7 @@ import lombok.Data;
 import java.math.BigDecimal;
 import java.time.LocalDateTime;
 
+// TODO @puhui999:缺导出
 @Schema(description = "管理后台 - CRM 回款计划 Response VO")
 @Data
 public class CrmReceivablePlanRespVO {
@@ -13,35 +14,44 @@ public class CrmReceivablePlanRespVO {
     @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
     private Long id;
 
-    @Schema(description = "回款编号", example = "19852")
-    private Long receivableId;
-
     @Schema(description = "期数", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
     private Integer period;
 
-    @Schema(description = "计划回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000")
-    private BigDecimal price;
-
-    @Schema(description = "计划回款日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
-    private LocalDateTime returnTime;
-
-    @Schema(description = "提前几天提醒", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
-    private Integer remindDays;
-
-    @Schema(description = "提醒日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
-    private LocalDateTime remindTime;
-
     @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
     private Long customerId;
+    @Schema(description = "客户名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "test")
+    private String customerName;
 
     @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
     private Long contractId;
+    @Schema(description = "合同编号", example = "Q110")
+    private String contractNo;
 
     @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
     private Long ownerUserId;
+    @Schema(description = "负责人", example = "test")
+    private String ownerUserName;
+
+    @Schema(description = "计划回款日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
+    private LocalDateTime returnTime;
+
+    @Schema(description = "回款方式", example = "1")
+    private Integer returnType; // 来自 Receivable 的 returnType 字段
 
-    @Schema(description = "显示顺序")
-    private Integer sort;
+    @Schema(description = "计划回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000")
+    private BigDecimal price;
+
+    @Schema(description = "回款编号", example = "19852")
+    private Long receivableId;
+
+    @Schema(description = "完成状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+    private Boolean finishStatus;
+
+    @Schema(description = "提前几天提醒", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private Integer remindDays;
+
+    @Schema(description = "提醒日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
+    private LocalDateTime remindTime;
 
     @Schema(description = "备注", example = "备注")
     private String remark;
@@ -49,25 +59,10 @@ public class CrmReceivablePlanRespVO {
     @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
     private LocalDateTime createTime;
 
-    @Schema(description = "客户名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "test")
-    private String customerName;
-
-    @Schema(description = "合同编号", example = "Q110")
-    private String contractNo;
-
-    @Schema(description = "负责人", example = "test")
-    private String ownerUserName;
-
     @Schema(description = "创建人", example = "25682")
     private String creator;
 
     @Schema(description = "创建人名字", example = "test")
     private String creatorName;
 
-    @Schema(description = "完成状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
-    private Boolean finishStatus;
-
-    @Schema(description = "回款方式", example = "1")
-    private Integer returnType; // 来自 Receivable 的 returnType 字段
-
 }

+ 13 - 22
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanSaveReqVO.java

@@ -14,26 +14,6 @@ public class CrmReceivablePlanSaveReqVO {
     @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
     private Long id;
 
-    @Schema(description = "期数", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
-    @NotNull(message = "期数不能为空")
-    private Integer period;
-
-    @Schema(description = "计划回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000")
-    @NotNull(message = "计划回款金额不能为空")
-    private BigDecimal price;
-
-    @Schema(description = "计划回款日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
-    @NotNull(message = "计划回款日期不能为空")
-    private LocalDateTime returnTime;
-
-    @Schema(description = "提前几天提醒", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
-    @NotNull(message = "提前几天提醒不能为空")
-    private Integer remindDays;
-
-    @Schema(description = "提醒日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
-    @NotNull(message = "提醒日期不能为空")
-    private LocalDateTime remindTime;
-
     @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
     @NotNull(message = "客户编号不能为空")
     private Long customerId;
@@ -46,8 +26,19 @@ public class CrmReceivablePlanSaveReqVO {
     @NotNull(message = "负责人编号不能为空")
     private Long ownerUserId;
 
-    @Schema(description = "显示顺序")
-    private Integer sort;
+    @Schema(description = "计划回款日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
+    @NotNull(message = "计划回款日期不能为空")
+    private LocalDateTime returnTime;
+
+    @Schema(description = "回款方式", example = "1")
+    private Integer returnType;
+
+    @Schema(description = "计划回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000")
+    @NotNull(message = "计划回款金额不能为空")
+    private BigDecimal price;
+
+    @Schema(description = "提前几天提醒", example = "1")
+    private Integer remindDays;
 
     @Schema(description = "备注", example = "备注")
     private String remark;

+ 2 - 2
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableSaveReqVO.java

@@ -1,7 +1,7 @@
 package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable;
 
 import cn.iocoder.yudao.framework.common.validation.InEnum;
-import cn.iocoder.yudao.module.crm.enums.receivable.CrmReturnTypeEnum;
+import cn.iocoder.yudao.module.crm.enums.receivable.CrmReceivableReturnTypeEnum;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotNull;
 import lombok.Data;
@@ -23,7 +23,7 @@ public class CrmReceivableSaveReqVO {
     private Long planId; // 不是通过回款计划创建的回款没有回款计划编号
 
     @Schema(description = "回款方式", example = "2")
-    @InEnum(CrmReturnTypeEnum.class)
+    @InEnum(CrmReceivableReturnTypeEnum.class)
     private Integer returnType;
 
     @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000")

+ 2 - 2
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivableDO.java

@@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
 import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
-import cn.iocoder.yudao.module.crm.enums.receivable.CrmReturnTypeEnum;
+import cn.iocoder.yudao.module.crm.enums.receivable.CrmReceivableReturnTypeEnum;
 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
 import com.baomidou.mybatisplus.annotation.KeySequence;
 import com.baomidou.mybatisplus.annotation.TableId;
@@ -60,7 +60,7 @@ public class CrmReceivableDO extends BaseDO {
      */
     private LocalDateTime returnTime;
     /**
-     * 回款方式,关联枚举{@link CrmReturnTypeEnum}
+     * 回款方式,关联枚举{@link CrmReceivableReturnTypeEnum}
      */
     private Integer returnType;
     /**

+ 34 - 23
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivablePlanDO.java

@@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.crm.dal.dataobject.receivable;
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
-import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import cn.iocoder.yudao.module.crm.enums.receivable.CrmReceivableReturnTypeEnum;
 import com.baomidou.mybatisplus.annotation.KeySequence;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
@@ -13,7 +13,7 @@ import java.math.BigDecimal;
 import java.time.LocalDateTime;
 
 /**
- * 回款计划 DO
+ * CRM 回款计划 DO
  *
  * @author 芋道源码
  */
@@ -37,49 +37,60 @@ public class CrmReceivablePlanDO extends BaseDO {
      */
     private Integer period;
     /**
-     * 回款编号,关联 {@link CrmReceivableDO#getId()}
+     * 客户编号
+     *
+     * 关联 {@link CrmCustomerDO#getId()}
      */
-    private Long receivableId;
+    private Long customerId;
     /**
-     * 计划回款金额,单位:元
+     * 合同编号
+     *
+     * 关联 {@link CrmContractDO#getId()}
      */
-    private BigDecimal price;
+    private Long contractId;
+
+    /**
+     * 负责人编号
+     *
+     * 关联 AdminUserDO 的 id 字段
+     */
+    private Long ownerUserId;
+
     /**
      * 计划回款日期
      */
     private LocalDateTime returnTime;
     /**
-     * 提前几天提醒
+     * 回款类型
+     *
+     * 枚举 {@link CrmReceivableReturnTypeEnum}
      */
-    private Integer remindDays;
+    private Integer returnType;
     /**
-     * 提醒日期
+     * 计划回款金额,单位:元
      */
-    private LocalDateTime remindTime;
+    private BigDecimal price;
+
     /**
-     * 客户编号,关联 {@link CrmCustomerDO#getId()}
+     * 回款编号,关联 {@link CrmReceivableDO#getId()}
      */
-    private Long customerId;
+    private Long receivableId;
     /**
-     * 合同编号,关联 {@link CrmContractDO#getId()}
+     * 完成状态
      */
-    private Long contractId;
+    private Boolean finishStatus;
+
     /**
-     * 负责人编号,关联 {@link AdminUserRespDTO#getId()}
+     * 提前几天提醒
      */
-    private Long ownerUserId;
+    private Integer remindDays;
     /**
-     * 显示顺序
+     * 提醒日期
      */
-    private Integer sort;
+    private LocalDateTime remindTime;
     /**
      * 备注
      */
     private String remark;
 
-    /**
-     * 完成状态
-     */
-    private Boolean finishStatus;
-
 }

+ 2 - 5
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java

@@ -11,7 +11,6 @@ import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
 import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;
 import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils;
-import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import org.apache.ibatis.annotations.Mapper;
 
 import java.time.LocalDateTime;
@@ -26,10 +25,8 @@ import java.util.List;
 @Mapper
 public interface CrmContractMapper extends BaseMapperX<CrmContractDO> {
 
-    default int updateOwnerUserIdById(Long id, Long ownerUserId) {
-        return update(new LambdaUpdateWrapper<CrmContractDO>()
-                .eq(CrmContractDO::getId, id)
-                .set(CrmContractDO::getOwnerUserId, ownerUserId));
+    default CrmContractDO selectByNo(String no) {
+        return selectOne(CrmContractDO::getNo, no);
     }
 
     default PageResult<CrmContractDO> selectPageByCustomerId(CrmContractPageReqVO pageReqVO) {

+ 18 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/redis/RedisKeyConstants.java

@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.crm.dal.redis;
+
+/**
+ * CRM Redis Key 枚举类
+ *
+ * @author 芋道源码
+ */
+public interface RedisKeyConstants {
+
+    /**
+     * 序号的缓存
+     *
+     * KEY 格式:trade_no:{prefix}
+     * VALUE 数据格式:编号自增
+     */
+    String NO = "crm:seq_no:";
+
+}

+ 52 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/redis/no/CrmNoRedisDAO.java

@@ -0,0 +1,52 @@
+package cn.iocoder.yudao.module.crm.dal.redis.no;
+
+import cn.hutool.core.date.DatePattern;
+import cn.hutool.core.date.DateUtil;
+import cn.iocoder.yudao.module.crm.dal.redis.RedisKeyConstants;
+import jakarta.annotation.Resource;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Repository;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+
+
+/**
+ * Crm 订单序号的 Redis DAO
+ *
+ * @author HUIHUI
+ */
+@Repository
+public class CrmNoRedisDAO {
+
+    /**
+     * 合同 {@link cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO}
+     */
+    public static final String CONTRACT_NO_PREFIX = "HT";
+
+    /**
+     * 还款 {@link cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO}
+     */
+    public static final String RECEIVABLE_PREFIX = "HK";
+
+    @Resource
+    private StringRedisTemplate stringRedisTemplate;
+
+    /**
+     * 生成序号,使用当前日期,格式为 {PREFIX} + yyyyMMdd + 6 位自增
+     * 例如说:QTRK 202109 000001 (没有中间空格)
+     *
+     * @param prefix 前缀
+     * @return 序号
+     */
+    public String generate(String prefix) {
+        // 递增序号
+        String noPrefix = prefix + DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATE_PATTERN);
+        String key = RedisKeyConstants.NO + noPrefix;
+        Long no = stringRedisTemplate.opsForValue().increment(key);
+        // 设置过期时间
+        stringRedisTemplate.expire(key, Duration.ofDays(1L));
+        return noPrefix + String.format("%06d", no);
+    }
+
+}

+ 11 - 4
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java

@@ -17,6 +17,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractProductDO;
 import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractMapper;
 import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractProductMapper;
+import cn.iocoder.yudao.module.crm.dal.redis.no.CrmNoRedisDAO;
 import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
@@ -69,6 +70,9 @@ public class CrmContractServiceImpl implements CrmContractService {
     @Resource
     private CrmContractProductMapper contractProductMapper;
 
+    @Resource
+    private CrmNoRedisDAO noRedisDAO;
+
     @Resource
     private CrmPermissionService crmPermissionService;
     @Resource
@@ -94,11 +98,14 @@ public class CrmContractServiceImpl implements CrmContractService {
         List<CrmContractProductDO> contractProducts = validateContractProducts(createReqVO.getProducts());
         // 1.2 校验关联字段
         validateRelationDataExists(createReqVO);
-        // TODO 芋艿:生成 no
+        // 1.3 生成序号
+        String no = noRedisDAO.generate(CrmNoRedisDAO.CONTRACT_NO_PREFIX);
+        if (contractMapper.selectByNo(no) != null) {
+            throw exception(CONTRACT_NO_EXISTS);
+        }
 
         // 2.1 插入合同
-        CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class);
-        contract.setNo(System.currentTimeMillis() + ""); // TODO
+        CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class).setNo(no);
         calculateTotalPrice(contract, contractProducts);
         contractMapper.insert(contract);
         // 2.2 插入合同关联商品
@@ -247,7 +254,7 @@ public class CrmContractServiceImpl implements CrmContractService {
         crmPermissionService.transferPermission(new CrmPermissionTransferReqBO(userId, CrmBizTypeEnum.CRM_CONTRACT.getType(),
                 reqVO.getId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel()));
         // 2.2 设置负责人
-        contractMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
+        contractMapper.updateById(new CrmContractDO().setId(reqVO.getId()).setOwnerUserId(reqVO.getNewOwnerUserId()));
 
         // 3. 记录转移日志
         LogRecordContext.putVariable("contract", contract);

+ 6 - 4
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java

@@ -23,7 +23,7 @@ import com.mzt.logapi.context.LogRecordContext;
 import com.mzt.logapi.service.impl.DiffParseFunction;
 import com.mzt.logapi.starter.annotation.LogRecord;
 import jakarta.annotation.Resource;
-import org.hibernate.validator.internal.util.stereotypes.Lazy;
+import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
@@ -57,6 +57,7 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
     private CrmCustomerService customerService;
     @Resource
     private CrmPermissionService permissionService;
+
     @Resource
     private AdminUserApi adminUserApi;
 
@@ -72,15 +73,16 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
         int period = (int) (count + 1);
         createReqVO.setPeriod(createReqVO.getPeriod() != period ? period : createReqVO.getPeriod()); // 如果期数不对则纠正
 
-        // 2.1 插入
+        // 2. 插入还款计划
         CrmReceivablePlanDO receivablePlan = BeanUtils.toBean(createReqVO, CrmReceivablePlanDO.class).setId(null).setFinishStatus(false);
         receivablePlanMapper.insert(receivablePlan);
-        // 2.2 创建数据权限
+
+        // 3. 创建数据权限
         permissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(createReqVO.getOwnerUserId())
                 .setBizType(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType()).setBizId(receivablePlan.getId())
                 .setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
 
-        // 3. 记录操作日志上下文
+        // 4. 记录操作日志上下文
         LogRecordContext.putVariable("receivablePlan", receivablePlan);
         return receivablePlan.getId();
     }

+ 4 - 1
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java

@@ -31,6 +31,7 @@ import com.mzt.logapi.service.impl.DiffParseFunction;
 import com.mzt.logapi.starter.annotation.LogRecord;
 import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
@@ -66,10 +67,12 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
     private CrmContractService contractService;
     @Resource
     private CrmCustomerService customerService;
-    @Resource
+//    @Resource
+    @Lazy // 延迟加载,避免循环依赖
     private CrmReceivablePlanService receivablePlanService;
     @Resource
     private CrmPermissionService permissionService;
+
     @Resource
     private AdminUserApi adminUserApi;
     @Resource

+ 1 - 14
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmAuditStatusUtils.java

@@ -1,7 +1,6 @@
 package cn.iocoder.yudao.module.crm.util;
 
 import cn.hutool.core.lang.Assert;
-import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
 import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
 import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
 
@@ -18,23 +17,11 @@ public class CrmAuditStatusUtils {
      * @param bpmResult BPM 审批结果
      */
     public static Integer convertBpmResultToAuditStatus(Integer bpmResult) {
-        Assert.isTrue(isEndResult(bpmResult), "BPM 审批结果({}) 转换失败, 流程状态不是最终结果", bpmResult);
         Integer auditStatus = BpmProcessInstanceResultEnum.APPROVE.getResult().equals(bpmResult) ? CrmAuditStatusEnum.APPROVE.getStatus()
                 : BpmProcessInstanceResultEnum.REJECT.getResult().equals(bpmResult) ? CrmAuditStatusEnum.REJECT.getStatus()
-                : BpmProcessInstanceResultEnum.CANCEL.getResult();
+                : BpmProcessInstanceResultEnum.CANCEL.getResult().equals(bpmResult) ? BpmProcessInstanceResultEnum.CANCEL.getResult() : null;
         Assert.notNull(auditStatus, "BPM 审批结果({}) 转换失败", bpmResult);
         return auditStatus;
     }
 
-    /**
-     * 判断该结果是否处于 End 最终结果
-     *
-     * @param bpmResult BPM 审批结果
-     * @return 是否
-     */
-    public static boolean isEndResult(Integer bpmResult) {
-        return ObjectUtils.equalsAny(bpmResult, BpmProcessInstanceResultEnum.APPROVE.getResult(),
-                BpmProcessInstanceResultEnum.REJECT.getResult(), BpmProcessInstanceResultEnum.CANCEL.getResult());
-    }
-
 }

+ 1 - 1
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/RedisKeyConstants.java

@@ -13,6 +13,6 @@ public interface RedisKeyConstants {
      * KEY 格式:trade_no:{prefix}
      * VALUE 数据格式:编号自增
      */
-    String NO = "seq_no:";
+    String NO = "erp:seq_no:";
 
 }

+ 1 - 1
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/no/ErpNoRedisDAO.java

@@ -12,7 +12,7 @@ import java.time.LocalDateTime;
 
 
 /**
- * 订单序号的 Redis DAO
+ * Erp 订单序号的 Redis DAO
  *
  * @author HUIHUI
  */

+ 14 - 1
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApi.java

@@ -5,6 +5,8 @@ import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO;
 import java.util.Collection;
 import java.util.List;
 
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
+
 /**
  * 字典数据 API 接口
  *
@@ -40,12 +42,23 @@ public interface DictDataApi {
      */
     DictDataRespDTO parseDictData(String type, String label);
 
+    /**
+     * 获得指定字典类型的字典数据列表
+     *
+     * @param dictType 字典类型
+     * @return 字典数据列表
+     */
+    List<DictDataRespDTO> getDictDataList(String dictType);
+
     /**
      * 获得字典数据标签列表
      *
      * @param dictType 字典类型
      * @return 字典数据标签列表
      */
-    List<String> getDictDataLabelList(String dictType);
+    default List<String> getDictDataLabelList(String dictType) {
+        List<DictDataRespDTO> list = getDictDataList(dictType);
+        return convertList(list, DictDataRespDTO::getLabel);
+    }
 
 }

+ 3 - 10
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApiImpl.java

@@ -1,6 +1,5 @@
 package cn.iocoder.yudao.module.system.api.dict;
 
-import cn.hutool.core.collection.CollUtil;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO;
 import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO;
@@ -9,11 +8,8 @@ import jakarta.annotation.Resource;
 import org.springframework.stereotype.Service;
 
 import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
 
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
-
 /**
  * 字典数据 API 实现类
  *
@@ -43,12 +39,9 @@ public class DictDataApiImpl implements DictDataApi {
     }
 
     @Override
-    public List<String> getDictDataLabelList(String dictType) {
-        List<DictDataDO> dictDataList = dictDataService.getDictDataListByDictType(dictType);
-        if (CollUtil.isEmpty(dictDataList)) {
-            return Collections.emptyList();
-        }
-        return convertList(dictDataList, DictDataDO::getLabel);
+    public List<DictDataRespDTO> getDictDataList(String dictType) {
+        List<DictDataDO> list = dictDataService.getDictDataListByDictType(dictType);
+        return BeanUtils.toBean(list, DictDataRespDTO.class);
     }
 
 }

+ 1 - 1
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/AreaController.java

@@ -29,7 +29,7 @@ public class AreaController {
     @GetMapping("/tree")
     @Operation(summary = "获得地区树")
     public CommonResult<List<AreaNodeRespVO>> getAreaTree() {
-        Area area = AreaUtils.getArea(Area.ID_CHINA);
+        Area area = AreaUtils.parseArea(Area.ID_CHINA);
         Assert.notNull(area, "获取不到中国");
         return success(BeanUtils.toBean(area.getChildren(), AreaNodeRespVO.class));
     }

+ 1 - 1
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/ip/AppAreaController.java

@@ -26,7 +26,7 @@ public class AppAreaController {
     @GetMapping("/tree")
     @Operation(summary = "获得地区树")
     public CommonResult<List<AppAreaNodeRespVO>> getAreaTree() {
-        Area area = AreaUtils.getArea(Area.ID_CHINA);
+        Area area = AreaUtils.parseArea(Area.ID_CHINA);
         Assert.notNull(area, "获取不到中国");
         return success(BeanUtils.toBean(area.getChildren(), AppAreaNodeRespVO.class));
     }

+ 1 - 1
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataService.java

@@ -100,7 +100,7 @@ public interface DictDataService {
     DictDataDO parseDictData(String dictType, String label);
 
     /**
-     * 获得字典数据列表
+     * 获得指定数据类型的字典数据列表
      *
      * @param dictType 字典类型
      * @return 字典数据列表

+ 3 - 1
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceImpl.java

@@ -171,7 +171,9 @@ public class DictDataServiceImpl implements DictDataService {
 
     @Override
     public List<DictDataDO> getDictDataListByDictType(String dictType) {
-        return dictDataMapper.selectList(DictDataDO::getDictType, dictType);
+        List<DictDataDO> list = dictDataMapper.selectList(DictDataDO::getDictType, dictType);
+        list.sort(Comparator.comparing(DictDataDO::getSort));
+        return list;
     }
 
 }