CRM:code review 客户导入

This commit is contained in:
YunaiV 2024-02-23 18:50:22 +08:00
parent a64da48aa3
commit e53a0ca884
33 changed files with 277 additions and 217 deletions

View File

@ -257,11 +257,11 @@ public class CollectionUtils {
return !CollectionUtil.isEmpty(from) ? from.get(0) : null; 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()); 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)) { if (CollUtil.isEmpty(from)) {
return null; return null;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -33,15 +33,7 @@ public class ExcelUtils {
*/ */
public static <T> void write(HttpServletResponse response, String filename, String sheetName, public static <T> void write(HttpServletResponse response, String filename, String sheetName,
Class<T> head, List<T> data) throws IOException { Class<T> head, List<T> data) throws IOException {
// 输出 Excel write(response, filename, sheetName, head, data, null);
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");
} }
/** /**

View File

@ -14,6 +14,7 @@ public interface ErrorCodeConstants {
ErrorCode CONTRACT_UPDATE_FAIL_NOT_DRAFT = new ErrorCode(1_020_000_001, "合同更新失败,原因:合同不是草稿状态"); 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_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_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 ========== // ========== 线索管理 1-020-001-000 ==========
ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索不存在"); ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索不存在");

View File

@ -13,26 +13,18 @@ import java.util.Arrays;
*/ */
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public enum CrmReturnTypeEnum implements IntArrayValuable { public enum CrmReceivableReturnTypeEnum implements IntArrayValuable {
// 支票
CHECK(1, "支票"), CHECK(1, "支票"),
// 现金
CASH(2, "现金"), CASH(2, "现金"),
// 邮政汇款
POSTAL_REMITTANCE(3, "邮政汇款"), POSTAL_REMITTANCE(3, "邮政汇款"),
// 电汇
TELEGRAPHIC_TRANSFER(4, "电汇"), TELEGRAPHIC_TRANSFER(4, "电汇"),
// 网上转账
ONLINE_TRANSFER(5, "网上转账"), ONLINE_TRANSFER(5, "网上转账"),
// 支付宝
ALIPAY(6, "支付宝"), ALIPAY(6, "支付宝"),
// 微信支付
WECHAT_PAY(7, "微信支付"), WECHAT_PAY(7, "微信支付"),
// 其他
OTHER(8, "其它"); 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();
/** /**
* 类型 * 类型

View File

@ -268,6 +268,24 @@ public class CrmCustomerController {
ExcelUtils.write(response, "客户导入模板.xls", "客户列表", CrmCustomerImportExcelVO.class, list, builderSelectMap()); 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") @PostMapping("/import")
@Operation(summary = "导入客户") @Operation(summary = "导入客户")
@PreAuthorize("@ss.hasPermission('system:customer:import')") @PreAuthorize("@ss.hasPermission('system:customer:import')")
@ -321,21 +339,4 @@ public class CrmCustomerController {
return success(true); 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;
}
} }

View File

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

View File

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

View File

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

View File

@ -14,26 +14,6 @@ public class CrmReceivablePlanSaveReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
private Long id; 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") @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@NotNull(message = "客户编号不能为空") @NotNull(message = "客户编号不能为空")
private Long customerId; private Long customerId;
@ -46,8 +26,19 @@ public class CrmReceivablePlanSaveReqVO {
@NotNull(message = "负责人编号不能为空") @NotNull(message = "负责人编号不能为空")
private Long ownerUserId; private Long ownerUserId;
@Schema(description = "显示顺序") @Schema(description = "计划回款日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
private Integer sort; @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 = "备注") @Schema(description = "备注", example = "备注")
private String remark; private String remark;

View File

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

View File

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

View File

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

View File

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

View File

@ -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:";
}

View File

@ -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);
}
}

View File

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

View File

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

View File

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

View File

@ -1,7 +1,6 @@
package cn.iocoder.yudao.module.crm.util; package cn.iocoder.yudao.module.crm.util;
import cn.hutool.core.lang.Assert; 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.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum; import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
@ -18,23 +17,11 @@ public class CrmAuditStatusUtils {
* @param bpmResult BPM 审批结果 * @param bpmResult BPM 审批结果
*/ */
public static Integer convertBpmResultToAuditStatus(Integer bpmResult) { public static Integer convertBpmResultToAuditStatus(Integer bpmResult) {
Assert.isTrue(isEndResult(bpmResult), "BPM 审批结果({}) 转换失败, 流程状态不是最终结果", bpmResult);
Integer auditStatus = BpmProcessInstanceResultEnum.APPROVE.getResult().equals(bpmResult) ? CrmAuditStatusEnum.APPROVE.getStatus() Integer auditStatus = BpmProcessInstanceResultEnum.APPROVE.getResult().equals(bpmResult) ? CrmAuditStatusEnum.APPROVE.getStatus()
: BpmProcessInstanceResultEnum.REJECT.getResult().equals(bpmResult) ? CrmAuditStatusEnum.REJECT.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); Assert.notNull(auditStatus, "BPM 审批结果({}) 转换失败", bpmResult);
return auditStatus; 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());
}
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -171,7 +171,9 @@ public class DictDataServiceImpl implements DictDataService {
@Override @Override
public List<DictDataDO> getDictDataListByDictType(String dictType) { 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;
} }
} }