Commit e53a0ca8845229fb298c4d77da0f09315a6a5a4f

Authored by YunaiV
1 parent a64da48a

✨ CRM:code review 客户导入

Showing 33 changed files with 273 additions and 213 deletions
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
... ... @@ -257,11 +257,11 @@ public class CollectionUtils {
257 257 return !CollectionUtil.isEmpty(from) ? from.get(0) : null;
258 258 }
259 259  
260   - public static <T> T findFirst(List<T> from, Predicate<T> predicate) {
  260 + public static <T> T findFirst(Collection<T> from, Predicate<T> predicate) {
261 261 return findFirst(from, predicate, Function.identity());
262 262 }
263 263  
264   - public static <T, U> U findFirst(List<T> from, Predicate<T> predicate, Function<T, U> func) {
  264 + public static <T, U> U findFirst(Collection<T> from, Predicate<T> predicate, Function<T, U> func) {
265 265 if (CollUtil.isEmpty(from)) {
266 266 return null;
267 267 }
... ...
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;
4 4 import cn.hutool.core.lang.Assert;
5 5 import cn.hutool.core.text.csv.CsvRow;
6 6 import cn.hutool.core.text.csv.CsvUtil;
7   -import cn.hutool.core.util.ObjUtil;
8 7 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
9 8 import cn.iocoder.yudao.framework.ip.core.Area;
10 9 import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum;
... ... @@ -72,26 +71,25 @@ public class AreaUtils {
72 71 * @param id 区域编号
73 72 * @return 区域
74 73 */
75   - public static Area getArea(Integer id) {
  74 + public static Area parseArea(Integer id) {
76 75 return areas.get(id);
77 76 }
78 77  
79 78 /**
80 79 * 获得指定区域对应的编号
81 80 *
82   - * @param path 区域编号
83   - * @return 编号
  81 + * @param pathStr 区域路径,例如说:河南省/石家庄市/新华区
  82 + * @return 区域
84 83 */
85   - public static Area getArea(String path) {
86   - String[] paths = path.split("/");
  84 + public static Area parseArea(String pathStr) {
  85 + String[] paths = pathStr.split("/");
87 86 Area area = null;
88   - for (int i = 0; i < paths.length; i++) {
89   - final int finalI = i;
  87 + for (String path : paths) {
90 88 if (area == null) {
91   - area = findFirst(convertList(areas.values(), a -> a), item -> ObjUtil.equal(paths[finalI], item.getName()));
92   - continue;
  89 + area = findFirst(areas.values(), item -> item.getName().equals(path));
  90 + } else {
  91 + area = findFirst(area.getChildren(), item -> item.getName().equals(path));
93 92 }
94   - area = findFirst(area.getChildren(), item -> ObjUtil.equal(paths[finalI], item.getName()));
95 93 }
96 94 return area;
97 95 }
... ... @@ -102,9 +100,9 @@ public class AreaUtils {
102 100 * @param areas 地区树
103 101 * @return 所有节点的全路径名称
104 102 */
105   - public static List<String> getAllAreaNodePaths(List<Area> areas) {
  103 + public static List<String> getAreaNodePathList(List<Area> areas) {
106 104 List<String> paths = new ArrayList<>();
107   - areas.forEach(area -> traverse(area, "", paths));
  105 + areas.forEach(area -> getAreaNodePathList(area, "", paths));
108 106 return paths;
109 107 }
110 108  
... ... @@ -113,9 +111,9 @@ public class AreaUtils {
113 111 *
114 112 * @param node 父节点
115 113 * @param path 全路径名称
116   - * @param paths 全路径名称列表
  114 + * @param paths 全路径名称列表,省份/城市/地区
117 115 */
118   - private static void traverse(Area node, String path, List<String> paths) {
  116 + private static void getAreaNodePathList(Area node, String path, List<String> paths) {
119 117 if (node == null) {
120 118 return;
121 119 }
... ... @@ -124,7 +122,7 @@ public class AreaUtils {
124 122 paths.add(currentPath);
125 123 // 递归遍历子节点
126 124 for (Area child : node.getChildren()) {
127   - traverse(child, currentPath, paths);
  125 + getAreaNodePathList(child, currentPath, paths);
128 126 }
129 127 }
130 128  
... ... @@ -195,7 +193,7 @@ public class AreaUtils {
195 193 */
196 194 public static Integer getParentIdByType(Integer id, @NonNull AreaTypeEnum type) {
197 195 for (int i = 0; i < Byte.MAX_VALUE; i++) {
198   - Area area = AreaUtils.getArea(id);
  196 + Area area = AreaUtils.parseArea(id);
199 197 if (area == null) {
200 198 return null;
201 199 }
... ...
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 {
72 72 * @return 地区
73 73 */
74 74 public static Area getArea(String ip) {
75   - return AreaUtils.getArea(getAreaId(ip));
  75 + return AreaUtils.parseArea(getAreaId(ip));
76 76 }
77 77  
78 78 /**
... ... @@ -82,6 +82,6 @@ public class IPUtils {
82 82 * @return 地区
83 83 */
84 84 public static Area getArea(long ip) {
85   - return AreaUtils.getArea(getAreaId(ip));
  85 + return AreaUtils.parseArea(getAreaId(ip));
86 86 }
87 87 }
... ...
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 {
17 17 @Test
18 18 public void testGetArea() {
19 19 // 调用:北京
20   - Area area = AreaUtils.getArea(110100);
  20 + Area area = AreaUtils.parseArea(110100);
21 21 // 断言
22 22 assertEquals(area.getId(), 110100);
23 23 assertEquals(area.getName(), "北京市");
... ...
yudao-framework/yudao-spring-boot-starter-excel/pom.xml
... ... @@ -49,7 +49,7 @@
49 49 <dependency>
50 50 <groupId>cn.iocoder.boot</groupId>
51 51 <artifactId>yudao-spring-boot-starter-biz-ip</artifactId>
52   - <scope>provided</scope> <!-- 设置为 provided,只有 ExcelUtils 使用 -->
  52 + <optional>true</optional> <!-- 设置为 optional,只有在 AreaConvert 的时候使用 -->
53 53 </dependency>
54 54 </dependencies>
55 55  
... ...
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&lt;Object&gt; {
33 33 GlobalConfiguration globalConfiguration) {
34 34 // 解析地区编号
35 35 String label = readCellData.getStringValue();
36   - Area area = AreaUtils.getArea(label);
  36 + Area area = AreaUtils.parseArea(label);
37 37 if (area == null) {
38 38 log.error("[convertToJavaData][label({}) 解析不掉]", label);
39 39 return null;
... ...
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;
19 19 */
20 20 public class SelectSheetWriteHandler implements SheetWriteHandler {
21 21  
  22 + private static final String DICT_SHEET_NAME = "字典sheet";
  23 +
  24 + // TODO @puhui999:key 不使用 int 值么?感觉不是很优雅哈。
22 25 private final List<KeyValue<Integer, List<String>>> selectMap;
23 26  
24 27 private static final char[] ALPHABET = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
25 28 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
26 29  
27 30 public SelectSheetWriteHandler(List<KeyValue<Integer, List<String>>> selectMap) {
  31 + if (CollUtil.isEmpty(selectMap)) {
  32 + this.selectMap = null;
  33 + return;
  34 + }
28 35 selectMap.sort(Comparator.comparing(item -> item.getValue().size())); // 升序不然创建下拉会报错
29 36 this.selectMap = selectMap;
30 37 }
31 38  
32 39 @Override
33 40 public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
34   - if (selectMap == null || CollUtil.isEmpty(selectMap)) {
  41 + if (CollUtil.isEmpty(selectMap)) {
35 42 return;
36 43 }
37   - // 需要设置下拉框的sheet页
38   - Sheet curSheet = writeSheetHolder.getSheet();
39   - DataValidationHelper helper = curSheet.getDataValidationHelper();
40   - String dictSheetName = "字典sheet";
  44 + // 需要设置下拉框的 sheet 页
  45 + Sheet currentSheet = writeSheetHolder.getSheet();
  46 + DataValidationHelper helper = currentSheet.getDataValidationHelper();
41 47 Workbook workbook = writeWorkbookHolder.getWorkbook();
42   - // 数据字典的sheet页
43   - Sheet dictSheet = workbook.createSheet(dictSheetName);
  48 +
  49 + // 数据字典的 sheet 页
  50 + Sheet dictSheet = workbook.createSheet(DICT_SHEET_NAME);
44 51 for (KeyValue<Integer, List<String>> keyValue : selectMap) {
45 52 // 设置下拉单元格的首行、末行、首列、末列
46 53 CellRangeAddressList rangeAddressList = new CellRangeAddressList(1, 65533, keyValue.getKey(), keyValue.getKey());
47 54 int rowLen = keyValue.getValue().size();
48   - // 设置字典sheet页的值 每一列一个字典项
  55 + // 设置字典 sheet 页的值 每一列一个字典项
49 56 for (int i = 0; i < rowLen; i++) {
50 57 Row row = dictSheet.getRow(i);
51 58 if (row == null) {
... ... @@ -53,18 +60,18 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
53 60 }
54 61 row.createCell(keyValue.getKey()).setCellValue(keyValue.getValue().get(i));
55 62 }
56   - String excelColumn = getExcelColumn(keyValue.getKey());
57   - // 下拉框数据来源 eg:字典sheet!$B1:$B2
58   - String refers = dictSheetName + "!$" + excelColumn + "$1:$" + excelColumn + "$" + rowLen;
59   - // 创建可被其他单元格引用的名称
  63 +
  64 + // TODO @puhui999:下面 1. 2.1 2.2 2.3 我是按照已经理解的,调整了下格式;这样可读性更好;在 52 到 62 行,你可以看看,是不是也弄下序号;
  65 + // 1. 创建可被其他单元格引用的名称
60 66 Name name = workbook.createName();
61   - // 设置名称的名字
62   - name.setNameName("dict" + keyValue.getKey());
63   - // 设置公式
64   - name.setRefersToFormula(refers);
65   - // 设置引用约束
66   - DataValidationConstraint constraint = helper.createFormulaListConstraint("dict" + keyValue.getKey());
67   - // 设置约束
  67 + // TODO @puhui999:下面的 excelColumn 和 refers 两行,是不是可以封装成一个方法,替代 getExcelColumn;
  68 + String excelColumn = getExcelColumn(keyValue.getKey());
  69 + String refers = DICT_SHEET_NAME + "!$" + excelColumn + "$1:$" + excelColumn + "$" + rowLen; // 下拉框数据来源 eg:字典sheet!$B1:$B2
  70 + name.setNameName("dict" + keyValue.getKey()); // 设置名称的名字
  71 + name.setRefersToFormula(refers); // 设置公式
  72 +
  73 + // 2.1 设置约束
  74 + DataValidationConstraint constraint = helper.createFormulaListConstraint("dict" + keyValue.getKey()); // 设置引用约束
68 75 DataValidation validation = helper.createValidation(constraint, rangeAddressList);
69 76 if (validation instanceof HSSFDataValidation) {
70 77 validation.setSuppressDropDownArrow(false);
... ... @@ -72,10 +79,10 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
72 79 validation.setSuppressDropDownArrow(true);
73 80 validation.setShowErrorBox(true);
74 81 }
75   - // 阻止输入非下拉框的值
  82 + // 2.2 阻止输入非下拉框的值
76 83 validation.setErrorStyle(DataValidation.ErrorStyle.STOP);
77 84 validation.createErrorBox("提示", "此值不存在于下拉选择中!");
78   - // 添加下拉框约束
  85 + // 2.3 添加下拉框约束
79 86 writeSheetHolder.getSheet().addValidationData(validation);
80 87 }
81 88 }
... ... @@ -86,8 +93,9 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
86 93 * @param num 数字
87 94 * @return 字母
88 95 */
  96 + // TODO @puhui999:这个是必须字母列哇?还是数字其实也可以哈?主要想看看,怎么能把这个逻辑,进一步简化
89 97 private String getExcelColumn(int num) {
90   - String column = "";
  98 + String column;
91 99 int len = ALPHABET.length - 1;
92 100 int first = num / len;
93 101 int second = num % len;
... ... @@ -96,9 +104,9 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
96 104 } else {
97 105 column = ALPHABET[first - 1] + "";
98 106 if (second == 0) {
99   - column = column + ALPHABET[len] + "";
  107 + column = column + ALPHABET[len];
100 108 } else {
101   - column = column + ALPHABET[second - 1] + "";
  109 + column = column + ALPHABET[second - 1];
102 110 }
103 111 }
104 112 return column;
... ...
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 {
33 33 */
34 34 public static <T> void write(HttpServletResponse response, String filename, String sheetName,
35 35 Class<T> head, List<T> data) throws IOException {
36   - // 输出 Excel
37   - EasyExcel.write(response.getOutputStream(), head)
38   - .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理
39   - .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度
40   - .registerConverter(new LongStringConverter()) // 避免 Long 类型丢失精度
41   - .sheet(sheetName).doWrite(data);
42   - // 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了
43   - response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, StandardCharsets.UTF_8.name()));
44   - response.setContentType("application/vnd.ms-excel;charset=UTF-8");
  36 + write(response, filename, sheetName, head, data, null);
45 37 }
46 38  
47 39 /**
... ...
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 {
14 14 ErrorCode CONTRACT_UPDATE_FAIL_NOT_DRAFT = new ErrorCode(1_020_000_001, "合同更新失败,原因:合同不是草稿状态");
15 15 ErrorCode CONTRACT_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_020_000_002, "合同提交审核失败,原因:合同没处在未提交状态");
16 16 ErrorCode CONTRACT_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS = new ErrorCode(1_020_000_003, "更新合同审核状态失败,原因:合同不是审核中状态");
  17 + ErrorCode CONTRACT_NO_EXISTS = new ErrorCode(1_020_000_004, "生成合同序列号重复,请重试");
17 18  
18 19 // ========== 线索管理 1-020-001-000 ==========
19 20 ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索不存在");
... ...
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/receivable/CrmReturnTypeEnum.java renamed to 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;
13 13 */
14 14 @Getter
15 15 @AllArgsConstructor
16   -public enum CrmReturnTypeEnum implements IntArrayValuable {
  16 +public enum CrmReceivableReturnTypeEnum implements IntArrayValuable {
17 17  
18   - // 支票
19 18 CHECK(1, "支票"),
20   - // 现金
21 19 CASH(2, "现金"),
22   - // 邮政汇款
23 20 POSTAL_REMITTANCE(3, "邮政汇款"),
24   - // 电汇
25 21 TELEGRAPHIC_TRANSFER(4, "电汇"),
26   - // 网上转账
27 22 ONLINE_TRANSFER(5, "网上转账"),
28   - // 支付宝
29 23 ALIPAY(6, "支付宝"),
30   - // 微信支付
31 24 WECHAT_PAY(7, "微信支付"),
32   - // 其他
33 25 OTHER(8, "其它");
34 26  
35   - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmReturnTypeEnum::getType).toArray();
  27 + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmReceivableReturnTypeEnum::getType).toArray();
36 28  
37 29 /**
38 30 * 类型
... ...
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 {
268 268 ExcelUtils.write(response, "客户导入模板.xls", "客户列表", CrmCustomerImportExcelVO.class, list, builderSelectMap());
269 269 }
270 270  
  271 + private List<KeyValue<Integer, List<String>>> builderSelectMap() {
  272 + List<KeyValue<Integer, List<String>>> selectMap = new ArrayList<>();
  273 + // 获取地区下拉数据
  274 + // TODO @puhui999:嘿嘿,这里改成省份、城市、区域,三个选项,难度大么?
  275 + Area area = AreaUtils.parseArea(Area.ID_CHINA);
  276 + selectMap.add(new KeyValue<>(6, AreaUtils.getAreaNodePathList(area.getChildren())));
  277 + // 获取客户所属行业
  278 + List<String> customerIndustries = dictDataApi.getDictDataLabelList(CRM_CUSTOMER_INDUSTRY);
  279 + selectMap.add(new KeyValue<>(8, customerIndustries));
  280 + // 获取客户等级
  281 + List<String> customerLevels = dictDataApi.getDictDataLabelList(CRM_CUSTOMER_LEVEL);
  282 + selectMap.add(new KeyValue<>(9, customerLevels));
  283 + // 获取客户来源
  284 + List<String> customerSources = dictDataApi.getDictDataLabelList(CRM_CUSTOMER_SOURCE);
  285 + selectMap.add(new KeyValue<>(10, customerSources));
  286 + return selectMap;
  287 + }
  288 +
271 289 @PostMapping("/import")
272 290 @Operation(summary = "导入客户")
273 291 @PreAuthorize("@ss.hasPermission('system:customer:import')")
... ... @@ -321,21 +339,4 @@ public class CrmCustomerController {
321 339 return success(true);
322 340 }
323 341  
324   - private List<KeyValue<Integer, List<String>>> builderSelectMap() {
325   - List<KeyValue<Integer, List<String>>> selectMap = new ArrayList<>();
326   - // 获取地区下拉数据
327   - Area area = AreaUtils.getArea(Area.ID_CHINA);
328   - selectMap.add(new KeyValue<>(6, AreaUtils.getAllAreaNodePaths(area.getChildren())));
329   - // 获取客户所属行业
330   - List<String> customerIndustries = dictDataApi.getDictDataLabelList(CRM_CUSTOMER_INDUSTRY);
331   - selectMap.add(new KeyValue<>(8, customerIndustries));
332   - // 获取客户等级
333   - List<String> customerLevels = dictDataApi.getDictDataLabelList(CRM_CUSTOMER_LEVEL);
334   - selectMap.add(new KeyValue<>(9, customerLevels));
335   - // 获取客户来源
336   - List<String> customerSources = dictDataApi.getDictDataLabelList(CRM_CUSTOMER_SOURCE);
337   - selectMap.add(new KeyValue<>(10, customerSources));
338   - return selectMap;
339   - }
340   -
341 342 }
... ...
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 {
107 107 return success(buildReceivableDetailPage(pageResult));
108 108 }
109 109  
110   - // TODO 芋艿:后面在优化导出
111 110 @GetMapping("/export-excel")
112 111 @Operation(summary = "导出回款 Excel")
113 112 @PreAuthorize("@ss.hasPermission('crm:receivable:export')")
... ...
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;
28 28 import jakarta.annotation.Resource;
29 29 import jakarta.servlet.http.HttpServletResponse;
30 30 import jakarta.validation.Valid;
31   -import org.springframework.context.annotation.Lazy;
32 31 import org.springframework.security.access.prepost.PreAuthorize;
33 32 import org.springframework.validation.annotation.Validated;
34 33 import org.springframework.web.bind.annotation.*;
... ... @@ -56,7 +55,6 @@ public class CrmReceivablePlanController {
56 55 @Resource
57 56 private CrmReceivableService receivableService;
58 57 @Resource
59   - @Lazy
60 58 private CrmContractService contractService;
61 59 @Resource
62 60 private CrmCustomerService customerService;
... ...
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;
6 6 import java.math.BigDecimal;
7 7 import java.time.LocalDateTime;
8 8  
  9 +// TODO @puhui999:缺导出
9 10 @Schema(description = "管理后台 - CRM 回款计划 Response VO")
10 11 @Data
11 12 public class CrmReceivablePlanRespVO {
... ... @@ -13,35 +14,44 @@ public class CrmReceivablePlanRespVO {
13 14 @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
14 15 private Long id;
15 16  
16   - @Schema(description = "回款编号", example = "19852")
17   - private Long receivableId;
18   -
19 17 @Schema(description = "期数", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
20 18 private Integer period;
21 19  
22   - @Schema(description = "计划回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000")
23   - private BigDecimal price;
24   -
25   - @Schema(description = "计划回款日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
26   - private LocalDateTime returnTime;
27   -
28   - @Schema(description = "提前几天提醒", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
29   - private Integer remindDays;
30   -
31   - @Schema(description = "提醒日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
32   - private LocalDateTime remindTime;
33   -
34 20 @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
35 21 private Long customerId;
  22 + @Schema(description = "客户名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "test")
  23 + private String customerName;
36 24  
37 25 @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
38 26 private Long contractId;
  27 + @Schema(description = "合同编号", example = "Q110")
  28 + private String contractNo;
39 29  
40 30 @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
41 31 private Long ownerUserId;
  32 + @Schema(description = "负责人", example = "test")
  33 + private String ownerUserName;
  34 +
  35 + @Schema(description = "计划回款日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
  36 + private LocalDateTime returnTime;
  37 +
  38 + @Schema(description = "回款方式", example = "1")
  39 + private Integer returnType; // 来自 Receivable 的 returnType 字段
42 40  
43   - @Schema(description = "显示顺序")
44   - private Integer sort;
  41 + @Schema(description = "计划回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000")
  42 + private BigDecimal price;
  43 +
  44 + @Schema(description = "回款编号", example = "19852")
  45 + private Long receivableId;
  46 +
  47 + @Schema(description = "完成状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
  48 + private Boolean finishStatus;
  49 +
  50 + @Schema(description = "提前几天提醒", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  51 + private Integer remindDays;
  52 +
  53 + @Schema(description = "提醒日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
  54 + private LocalDateTime remindTime;
45 55  
46 56 @Schema(description = "备注", example = "备注")
47 57 private String remark;
... ... @@ -49,25 +59,10 @@ public class CrmReceivablePlanRespVO {
49 59 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
50 60 private LocalDateTime createTime;
51 61  
52   - @Schema(description = "客户名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "test")
53   - private String customerName;
54   -
55   - @Schema(description = "合同编号", example = "Q110")
56   - private String contractNo;
57   -
58   - @Schema(description = "负责人", example = "test")
59   - private String ownerUserName;
60   -
61 62 @Schema(description = "创建人", example = "25682")
62 63 private String creator;
63 64  
64 65 @Schema(description = "创建人名字", example = "test")
65 66 private String creatorName;
66 67  
67   - @Schema(description = "完成状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
68   - private Boolean finishStatus;
69   -
70   - @Schema(description = "回款方式", example = "1")
71   - private Integer returnType; // 来自 Receivable 的 returnType 字段
72   -
73 68 }
... ...
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 {
14 14 @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
15 15 private Long id;
16 16  
17   - @Schema(description = "期数", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
18   - @NotNull(message = "期数不能为空")
19   - private Integer period;
20   -
21   - @Schema(description = "计划回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000")
22   - @NotNull(message = "计划回款金额不能为空")
23   - private BigDecimal price;
24   -
25   - @Schema(description = "计划回款日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
26   - @NotNull(message = "计划回款日期不能为空")
27   - private LocalDateTime returnTime;
28   -
29   - @Schema(description = "提前几天提醒", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
30   - @NotNull(message = "提前几天提醒不能为空")
31   - private Integer remindDays;
32   -
33   - @Schema(description = "提醒日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
34   - @NotNull(message = "提醒日期不能为空")
35   - private LocalDateTime remindTime;
36   -
37 17 @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
38 18 @NotNull(message = "客户编号不能为空")
39 19 private Long customerId;
... ... @@ -46,8 +26,19 @@ public class CrmReceivablePlanSaveReqVO {
46 26 @NotNull(message = "负责人编号不能为空")
47 27 private Long ownerUserId;
48 28  
49   - @Schema(description = "显示顺序")
50   - private Integer sort;
  29 + @Schema(description = "计划回款日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
  30 + @NotNull(message = "计划回款日期不能为空")
  31 + private LocalDateTime returnTime;
  32 +
  33 + @Schema(description = "回款方式", example = "1")
  34 + private Integer returnType;
  35 +
  36 + @Schema(description = "计划回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000")
  37 + @NotNull(message = "计划回款金额不能为空")
  38 + private BigDecimal price;
  39 +
  40 + @Schema(description = "提前几天提醒", example = "1")
  41 + private Integer remindDays;
51 42  
52 43 @Schema(description = "备注", example = "备注")
53 44 private String remark;
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableSaveReqVO.java
1 1 package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable;
2 2  
3 3 import cn.iocoder.yudao.framework.common.validation.InEnum;
4   -import cn.iocoder.yudao.module.crm.enums.receivable.CrmReturnTypeEnum;
  4 +import cn.iocoder.yudao.module.crm.enums.receivable.CrmReceivableReturnTypeEnum;
5 5 import io.swagger.v3.oas.annotations.media.Schema;
6 6 import jakarta.validation.constraints.NotNull;
7 7 import lombok.Data;
... ... @@ -23,7 +23,7 @@ public class CrmReceivableSaveReqVO {
23 23 private Long planId; // 不是通过回款计划创建的回款没有回款计划编号
24 24  
25 25 @Schema(description = "回款方式", example = "2")
26   - @InEnum(CrmReturnTypeEnum.class)
  26 + @InEnum(CrmReceivableReturnTypeEnum.class)
27 27 private Integer returnType;
28 28  
29 29 @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000")
... ...
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;
4 4 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
5 5 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
6 6 import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
7   -import cn.iocoder.yudao.module.crm.enums.receivable.CrmReturnTypeEnum;
  7 +import cn.iocoder.yudao.module.crm.enums.receivable.CrmReceivableReturnTypeEnum;
8 8 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
9 9 import com.baomidou.mybatisplus.annotation.KeySequence;
10 10 import com.baomidou.mybatisplus.annotation.TableId;
... ... @@ -60,7 +60,7 @@ public class CrmReceivableDO extends BaseDO {
60 60 */
61 61 private LocalDateTime returnTime;
62 62 /**
63   - * 回款方式,关联枚举{@link CrmReturnTypeEnum}
  63 + * 回款方式,关联枚举{@link CrmReceivableReturnTypeEnum}
64 64 */
65 65 private Integer returnType;
66 66 /**
... ...
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;
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 4 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
5 5 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
6   -import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  6 +import cn.iocoder.yudao.module.crm.enums.receivable.CrmReceivableReturnTypeEnum;
7 7 import com.baomidou.mybatisplus.annotation.KeySequence;
8 8 import com.baomidou.mybatisplus.annotation.TableId;
9 9 import com.baomidou.mybatisplus.annotation.TableName;
... ... @@ -13,7 +13,7 @@ import java.math.BigDecimal;
13 13 import java.time.LocalDateTime;
14 14  
15 15 /**
16   - * 回款计划 DO
  16 + * CRM 回款计划 DO
17 17 *
18 18 * @author 芋道源码
19 19 */
... ... @@ -37,49 +37,60 @@ public class CrmReceivablePlanDO extends BaseDO {
37 37 */
38 38 private Integer period;
39 39 /**
40   - * 回款编号,关联 {@link CrmReceivableDO#getId()}
  40 + * 客户编号
  41 + *
  42 + * 关联 {@link CrmCustomerDO#getId()}
41 43 */
42   - private Long receivableId;
  44 + private Long customerId;
43 45 /**
44   - * 计划回款金额,单位:元
  46 + * 合同编号
  47 + *
  48 + * 关联 {@link CrmContractDO#getId()}
45 49 */
46   - private BigDecimal price;
  50 + private Long contractId;
  51 +
  52 + /**
  53 + * 负责人编号
  54 + *
  55 + * 关联 AdminUserDO 的 id 字段
  56 + */
  57 + private Long ownerUserId;
  58 +
47 59 /**
48 60 * 计划回款日期
49 61 */
50 62 private LocalDateTime returnTime;
51 63 /**
52   - * 提前几天提醒
  64 + * 回款类型
  65 + *
  66 + * 枚举 {@link CrmReceivableReturnTypeEnum}
53 67 */
54   - private Integer remindDays;
  68 + private Integer returnType;
55 69 /**
56   - * 提醒日期
  70 + * 计划回款金额,单位:元
57 71 */
58   - private LocalDateTime remindTime;
  72 + private BigDecimal price;
  73 +
59 74 /**
60   - * 客户编号,关联 {@link CrmCustomerDO#getId()}
  75 + * 回款编号,关联 {@link CrmReceivableDO#getId()}
61 76 */
62   - private Long customerId;
  77 + private Long receivableId;
63 78 /**
64   - * 合同编号,关联 {@link CrmContractDO#getId()}
  79 + * 完成状态
65 80 */
66   - private Long contractId;
  81 + private Boolean finishStatus;
  82 +
67 83 /**
68   - * 负责人编号,关联 {@link AdminUserRespDTO#getId()}
  84 + * 提前几天提醒
69 85 */
70   - private Long ownerUserId;
  86 + private Integer remindDays;
71 87 /**
72   - * 显示顺序
  88 + * 提醒日期
73 89 */
74   - private Integer sort;
  90 + private LocalDateTime remindTime;
75 91 /**
76 92 * 备注
77 93 */
78 94 private String remark;
79 95  
80   - /**
81   - * 完成状态
82   - */
83   - private Boolean finishStatus;
84   -
85 96 }
... ...
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;
11 11 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
12 12 import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;
13 13 import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils;
14   -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
15 14 import org.apache.ibatis.annotations.Mapper;
16 15  
17 16 import java.time.LocalDateTime;
... ... @@ -26,10 +25,8 @@ import java.util.List;
26 25 @Mapper
27 26 public interface CrmContractMapper extends BaseMapperX<CrmContractDO> {
28 27  
29   - default int updateOwnerUserIdById(Long id, Long ownerUserId) {
30   - return update(new LambdaUpdateWrapper<CrmContractDO>()
31   - .eq(CrmContractDO::getId, id)
32   - .set(CrmContractDO::getOwnerUserId, ownerUserId));
  28 + default CrmContractDO selectByNo(String no) {
  29 + return selectOne(CrmContractDO::getNo, no);
33 30 }
34 31  
35 32 default PageResult<CrmContractDO> selectPageByCustomerId(CrmContractPageReqVO pageReqVO) {
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/redis/RedisKeyConstants.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.dal.redis;
  2 +
  3 +/**
  4 + * CRM Redis Key 枚举类
  5 + *
  6 + * @author 芋道源码
  7 + */
  8 +public interface RedisKeyConstants {
  9 +
  10 + /**
  11 + * 序号的缓存
  12 + *
  13 + * KEY 格式:trade_no:{prefix}
  14 + * VALUE 数据格式:编号自增
  15 + */
  16 + String NO = "crm:seq_no:";
  17 +
  18 +}
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/redis/no/CrmNoRedisDAO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.dal.redis.no;
  2 +
  3 +import cn.hutool.core.date.DatePattern;
  4 +import cn.hutool.core.date.DateUtil;
  5 +import cn.iocoder.yudao.module.crm.dal.redis.RedisKeyConstants;
  6 +import jakarta.annotation.Resource;
  7 +import org.springframework.data.redis.core.StringRedisTemplate;
  8 +import org.springframework.stereotype.Repository;
  9 +
  10 +import java.time.Duration;
  11 +import java.time.LocalDateTime;
  12 +
  13 +
  14 +/**
  15 + * Crm 订单序号的 Redis DAO
  16 + *
  17 + * @author HUIHUI
  18 + */
  19 +@Repository
  20 +public class CrmNoRedisDAO {
  21 +
  22 + /**
  23 + * 合同 {@link cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO}
  24 + */
  25 + public static final String CONTRACT_NO_PREFIX = "HT";
  26 +
  27 + /**
  28 + * 还款 {@link cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO}
  29 + */
  30 + public static final String RECEIVABLE_PREFIX = "HK";
  31 +
  32 + @Resource
  33 + private StringRedisTemplate stringRedisTemplate;
  34 +
  35 + /**
  36 + * 生成序号,使用当前日期,格式为 {PREFIX} + yyyyMMdd + 6 位自增
  37 + * 例如说:QTRK 202109 000001 (没有中间空格)
  38 + *
  39 + * @param prefix 前缀
  40 + * @return 序号
  41 + */
  42 + public String generate(String prefix) {
  43 + // 递增序号
  44 + String noPrefix = prefix + DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATE_PATTERN);
  45 + String key = RedisKeyConstants.NO + noPrefix;
  46 + Long no = stringRedisTemplate.opsForValue().increment(key);
  47 + // 设置过期时间
  48 + stringRedisTemplate.expire(key, Duration.ofDays(1L));
  49 + return noPrefix + String.format("%06d", no);
  50 + }
  51 +
  52 +}
... ...
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;
17 17 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractProductDO;
18 18 import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractMapper;
19 19 import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractProductMapper;
  20 +import cn.iocoder.yudao.module.crm.dal.redis.no.CrmNoRedisDAO;
20 21 import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
21 22 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
22 23 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
... ... @@ -70,6 +71,9 @@ public class CrmContractServiceImpl implements CrmContractService {
70 71 private CrmContractProductMapper contractProductMapper;
71 72  
72 73 @Resource
  74 + private CrmNoRedisDAO noRedisDAO;
  75 +
  76 + @Resource
73 77 private CrmPermissionService crmPermissionService;
74 78 @Resource
75 79 private CrmProductService productService;
... ... @@ -94,11 +98,14 @@ public class CrmContractServiceImpl implements CrmContractService {
94 98 List<CrmContractProductDO> contractProducts = validateContractProducts(createReqVO.getProducts());
95 99 // 1.2 校验关联字段
96 100 validateRelationDataExists(createReqVO);
97   - // TODO 芋艿:生成 no
  101 + // 1.3 生成序号
  102 + String no = noRedisDAO.generate(CrmNoRedisDAO.CONTRACT_NO_PREFIX);
  103 + if (contractMapper.selectByNo(no) != null) {
  104 + throw exception(CONTRACT_NO_EXISTS);
  105 + }
98 106  
99 107 // 2.1 插入合同
100   - CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class);
101   - contract.setNo(System.currentTimeMillis() + ""); // TODO
  108 + CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class).setNo(no);
102 109 calculateTotalPrice(contract, contractProducts);
103 110 contractMapper.insert(contract);
104 111 // 2.2 插入合同关联商品
... ... @@ -247,7 +254,7 @@ public class CrmContractServiceImpl implements CrmContractService {
247 254 crmPermissionService.transferPermission(new CrmPermissionTransferReqBO(userId, CrmBizTypeEnum.CRM_CONTRACT.getType(),
248 255 reqVO.getId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel()));
249 256 // 2.2 设置负责人
250   - contractMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
  257 + contractMapper.updateById(new CrmContractDO().setId(reqVO.getId()).setOwnerUserId(reqVO.getNewOwnerUserId()));
251 258  
252 259 // 3. 记录转移日志
253 260 LogRecordContext.putVariable("contract", contract);
... ...
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;
23 23 import com.mzt.logapi.service.impl.DiffParseFunction;
24 24 import com.mzt.logapi.starter.annotation.LogRecord;
25 25 import jakarta.annotation.Resource;
26   -import org.hibernate.validator.internal.util.stereotypes.Lazy;
  26 +import org.springframework.context.annotation.Lazy;
27 27 import org.springframework.stereotype.Service;
28 28 import org.springframework.transaction.annotation.Transactional;
29 29 import org.springframework.validation.annotation.Validated;
... ... @@ -57,6 +57,7 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
57 57 private CrmCustomerService customerService;
58 58 @Resource
59 59 private CrmPermissionService permissionService;
  60 +
60 61 @Resource
61 62 private AdminUserApi adminUserApi;
62 63  
... ... @@ -72,15 +73,16 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
72 73 int period = (int) (count + 1);
73 74 createReqVO.setPeriod(createReqVO.getPeriod() != period ? period : createReqVO.getPeriod()); // 如果期数不对则纠正
74 75  
75   - // 2.1 插入
  76 + // 2. 插入还款计划
76 77 CrmReceivablePlanDO receivablePlan = BeanUtils.toBean(createReqVO, CrmReceivablePlanDO.class).setId(null).setFinishStatus(false);
77 78 receivablePlanMapper.insert(receivablePlan);
78   - // 2.2 创建数据权限
  79 +
  80 + // 3. 创建数据权限
79 81 permissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(createReqVO.getOwnerUserId())
80 82 .setBizType(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType()).setBizId(receivablePlan.getId())
81 83 .setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
82 84  
83   - // 3. 记录操作日志上下文
  85 + // 4. 记录操作日志上下文
84 86 LogRecordContext.putVariable("receivablePlan", receivablePlan);
85 87 return receivablePlan.getId();
86 88 }
... ...
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;
31 31 import com.mzt.logapi.starter.annotation.LogRecord;
32 32 import jakarta.annotation.Resource;
33 33 import lombok.extern.slf4j.Slf4j;
  34 +import org.springframework.context.annotation.Lazy;
34 35 import org.springframework.stereotype.Service;
35 36 import org.springframework.transaction.annotation.Transactional;
36 37 import org.springframework.validation.annotation.Validated;
... ... @@ -66,10 +67,12 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
66 67 private CrmContractService contractService;
67 68 @Resource
68 69 private CrmCustomerService customerService;
69   - @Resource
  70 +// @Resource
  71 + @Lazy // 延迟加载,避免循环依赖
70 72 private CrmReceivablePlanService receivablePlanService;
71 73 @Resource
72 74 private CrmPermissionService permissionService;
  75 +
73 76 @Resource
74 77 private AdminUserApi adminUserApi;
75 78 @Resource
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmAuditStatusUtils.java
1 1 package cn.iocoder.yudao.module.crm.util;
2 2  
3 3 import cn.hutool.core.lang.Assert;
4   -import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
5 4 import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
6 5 import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
7 6  
... ... @@ -18,23 +17,11 @@ public class CrmAuditStatusUtils {
18 17 * @param bpmResult BPM 审批结果
19 18 */
20 19 public static Integer convertBpmResultToAuditStatus(Integer bpmResult) {
21   - Assert.isTrue(isEndResult(bpmResult), "BPM 审批结果({}) 转换失败, 流程状态不是最终结果", bpmResult);
22 20 Integer auditStatus = BpmProcessInstanceResultEnum.APPROVE.getResult().equals(bpmResult) ? CrmAuditStatusEnum.APPROVE.getStatus()
23 21 : BpmProcessInstanceResultEnum.REJECT.getResult().equals(bpmResult) ? CrmAuditStatusEnum.REJECT.getStatus()
24   - : BpmProcessInstanceResultEnum.CANCEL.getResult();
  22 + : BpmProcessInstanceResultEnum.CANCEL.getResult().equals(bpmResult) ? BpmProcessInstanceResultEnum.CANCEL.getResult() : null;
25 23 Assert.notNull(auditStatus, "BPM 审批结果({}) 转换失败", bpmResult);
26 24 return auditStatus;
27 25 }
28 26  
29   - /**
30   - * 判断该结果是否处于 End 最终结果
31   - *
32   - * @param bpmResult BPM 审批结果
33   - * @return 是否
34   - */
35   - public static boolean isEndResult(Integer bpmResult) {
36   - return ObjectUtils.equalsAny(bpmResult, BpmProcessInstanceResultEnum.APPROVE.getResult(),
37   - BpmProcessInstanceResultEnum.REJECT.getResult(), BpmProcessInstanceResultEnum.CANCEL.getResult());
38   - }
39   -
40 27 }
... ...
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 {
13 13 * KEY 格式:trade_no:{prefix}
14 14 * VALUE 数据格式:编号自增
15 15 */
16   - String NO = "seq_no:";
  16 + String NO = "erp:seq_no:";
17 17  
18 18 }
... ...
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;
12 12  
13 13  
14 14 /**
15   - * 订单序号的 Redis DAO
  15 + * Erp 订单序号的 Redis DAO
16 16 *
17 17 * @author HUIHUI
18 18 */
... ...
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;
5 5 import java.util.Collection;
6 6 import java.util.List;
7 7  
  8 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  9 +
8 10 /**
9 11 * 字典数据 API 接口
10 12 *
... ... @@ -41,11 +43,22 @@ public interface DictDataApi {
41 43 DictDataRespDTO parseDictData(String type, String label);
42 44  
43 45 /**
  46 + * 获得指定字典类型的字典数据列表
  47 + *
  48 + * @param dictType 字典类型
  49 + * @return 字典数据列表
  50 + */
  51 + List<DictDataRespDTO> getDictDataList(String dictType);
  52 +
  53 + /**
44 54 * 获得字典数据标签列表
45 55 *
46 56 * @param dictType 字典类型
47 57 * @return 字典数据标签列表
48 58 */
49   - List<String> getDictDataLabelList(String dictType);
  59 + default List<String> getDictDataLabelList(String dictType) {
  60 + List<DictDataRespDTO> list = getDictDataList(dictType);
  61 + return convertList(list, DictDataRespDTO::getLabel);
  62 + }
50 63  
51 64 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApiImpl.java
1 1 package cn.iocoder.yudao.module.system.api.dict;
2 2  
3   -import cn.hutool.core.collection.CollUtil;
4 3 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 4 import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO;
6 5 import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO;
... ... @@ -9,11 +8,8 @@ import jakarta.annotation.Resource;
9 8 import org.springframework.stereotype.Service;
10 9  
11 10 import java.util.Collection;
12   -import java.util.Collections;
13 11 import java.util.List;
14 12  
15   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
16   -
17 13 /**
18 14 * 字典数据 API 实现类
19 15 *
... ... @@ -43,12 +39,9 @@ public class DictDataApiImpl implements DictDataApi {
43 39 }
44 40  
45 41 @Override
46   - public List<String> getDictDataLabelList(String dictType) {
47   - List<DictDataDO> dictDataList = dictDataService.getDictDataListByDictType(dictType);
48   - if (CollUtil.isEmpty(dictDataList)) {
49   - return Collections.emptyList();
50   - }
51   - return convertList(dictDataList, DictDataDO::getLabel);
  42 + public List<DictDataRespDTO> getDictDataList(String dictType) {
  43 + List<DictDataDO> list = dictDataService.getDictDataListByDictType(dictType);
  44 + return BeanUtils.toBean(list, DictDataRespDTO.class);
52 45 }
53 46  
54 47 }
... ...
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 {
29 29 @GetMapping("/tree")
30 30 @Operation(summary = "获得地区树")
31 31 public CommonResult<List<AreaNodeRespVO>> getAreaTree() {
32   - Area area = AreaUtils.getArea(Area.ID_CHINA);
  32 + Area area = AreaUtils.parseArea(Area.ID_CHINA);
33 33 Assert.notNull(area, "获取不到中国");
34 34 return success(BeanUtils.toBean(area.getChildren(), AreaNodeRespVO.class));
35 35 }
... ...
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 {
26 26 @GetMapping("/tree")
27 27 @Operation(summary = "获得地区树")
28 28 public CommonResult<List<AppAreaNodeRespVO>> getAreaTree() {
29   - Area area = AreaUtils.getArea(Area.ID_CHINA);
  29 + Area area = AreaUtils.parseArea(Area.ID_CHINA);
30 30 Assert.notNull(area, "获取不到中国");
31 31 return success(BeanUtils.toBean(area.getChildren(), AppAreaNodeRespVO.class));
32 32 }
... ...
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 {
100 100 DictDataDO parseDictData(String dictType, String label);
101 101  
102 102 /**
103   - * 获得字典数据列表
  103 + * 获得指定数据类型的字典数据列表
104 104 *
105 105 * @param dictType 字典类型
106 106 * @return 字典数据列表
... ...
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 {
171 171  
172 172 @Override
173 173 public List<DictDataDO> getDictDataListByDictType(String dictType) {
174   - return dictDataMapper.selectList(DictDataDO::getDictType, dictType);
  174 + List<DictDataDO> list = dictDataMapper.selectList(DictDataDO::getDictType, dictType);
  175 + list.sort(Comparator.comparing(DictDataDO::getSort));
  176 + return list;
175 177 }
176 178  
177 179 }
... ...