Commit 9cf4cfca86a9506eb17ade02f2a55ce6a0e06c0a

Authored by YunaiV
2 parents f942e2c7 e4eeaa1d

Merge branch 'feature/crm' of https://gitee.com/jiangwanwan/ruoyi-vue-pro into feature/crm

# Conflicts:
#	sql/mysql/crm.sql
sql/mysql/crm.sql
@@ -41,26 +41,26 @@ CREATE TABLE `crm_contract` @@ -41,26 +41,26 @@ CREATE TABLE `crm_contract`
41 41
42 DROP TABLE IF EXISTS `crm_clue`; 42 DROP TABLE IF EXISTS `crm_clue`;
43 CREATE TABLE `crm_clue` ( 43 CREATE TABLE `crm_clue` (
44 - `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号,主键自增',  
45 - `transform_status` tinyint NOT NULL COMMENT '转化状态',  
46 - `follow_up_status` tinyint NOT NULL COMMENT '跟进状态',  
47 - `name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '线索名称',  
48 - `customer_id` bigint NOT NULL COMMENT '客户id',  
49 - `contact_next_time` datetime NULL DEFAULT NULL COMMENT '下次联系时间',  
50 - `telephone` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '电话',  
51 - `mobile` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '手机号',  
52 - `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '地址',  
53 - `owner_user_id` bigint NULL DEFAULT NULL COMMENT '负责人的用户编号',  
54 - `contact_last_time` datetime NULL DEFAULT NULL COMMENT '最后跟进时间',  
55 - `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',  
56 - `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',  
57 - `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',  
58 - `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',  
59 - `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',  
60 - `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',  
61 - `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',  
62 - PRIMARY KEY (`id`) USING BTREE  
63 -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '线索表' ROW_FORMAT = Dynamic; 44 + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号,主键自增',
  45 + `transform_status` tinyint DEFAULT NULL COMMENT '转化状态',
  46 + `follow_up_status` tinyint DEFAULT NULL COMMENT '跟进状态',
  47 + `name` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '线索名称',
  48 + `customer_id` bigint NOT NULL COMMENT '客户id',
  49 + `contact_next_time` datetime DEFAULT NULL COMMENT '下次联系时间',
  50 + `telephone` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '电话',
  51 + `mobile` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '手机号',
  52 + `address` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '地址',
  53 + `owner_user_id` bigint NOT NULL COMMENT '负责人的用户编号',
  54 + `contact_last_time` datetime DEFAULT NULL COMMENT '最后跟进时间',
  55 + `remark` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
  56 + `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者',
  57 + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  58 + `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新者',
  59 + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  60 + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
  61 + `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
  62 + PRIMARY KEY (`id`) USING BTREE
  63 +) ENGINE = InnoDB COMMENT = '线索表' ;
64 64
65 DROP TABLE IF EXISTS `crm_business`; 65 DROP TABLE IF EXISTS `crm_business`;
66 CREATE TABLE `crm_business` ( 66 CREATE TABLE `crm_business` (
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/Telephone.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.common.validation;
  2 +
  3 +import javax.validation.Constraint;
  4 +import javax.validation.Payload;
  5 +import java.lang.annotation.*;
  6 +
  7 +@Target({
  8 + ElementType.METHOD,
  9 + ElementType.FIELD,
  10 + ElementType.ANNOTATION_TYPE,
  11 + ElementType.CONSTRUCTOR,
  12 + ElementType.PARAMETER,
  13 + ElementType.TYPE_USE
  14 +})
  15 +@Retention(RetentionPolicy.RUNTIME)
  16 +@Documented
  17 +@Constraint(
  18 + validatedBy = TelephoneValidator.class
  19 +)
  20 +public @interface Telephone {
  21 +
  22 + String message() default "电话格式不正确";
  23 +
  24 + Class<?>[] groups() default {};
  25 +
  26 + Class<? extends Payload>[] payload() default {};
  27 +
  28 +}
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/TelephoneValidator.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.common.validation;
  2 +
  3 +import cn.hutool.core.text.CharSequenceUtil;
  4 +import cn.hutool.core.util.PhoneUtil;
  5 +
  6 +import javax.validation.ConstraintValidator;
  7 +import javax.validation.ConstraintValidatorContext;
  8 +
  9 +public class TelephoneValidator implements ConstraintValidator<Telephone, String> {
  10 +
  11 + @Override
  12 + public void initialize(Telephone annotation) {
  13 + }
  14 +
  15 + @Override
  16 + public boolean isValid(String value, ConstraintValidatorContext context) {
  17 + // 如果手机号为空,默认不校验,即校验通过
  18 + if (CharSequenceUtil.isEmpty(value)) {
  19 + return true;
  20 + }
  21 + // 校验手机
  22 + return PhoneUtil.isTel(value) || PhoneUtil.isPhone(value);
  23 + }
  24 +
  25 +}
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
@@ -12,8 +12,8 @@ public interface ErrorCodeConstants { @@ -12,8 +12,8 @@ public interface ErrorCodeConstants {
12 // ========== 合同管理 1-020-000-000 ========== 12 // ========== 合同管理 1-020-000-000 ==========
13 ErrorCode CONTRACT_NOT_EXISTS = new ErrorCode(1_020_000_000, "合同不存在"); 13 ErrorCode CONTRACT_NOT_EXISTS = new ErrorCode(1_020_000_000, "合同不存在");
14 14
15 - // TODO @wanwan:要单独一个分段噢  
16 - ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_000_001, "线索不存在"); 15 + // ========== 线索管理 1-020-001-000 ==========
  16 + ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索不存在");
17 17
18 // ========== 商机管理 1-020-001-000 ========== 18 // ========== 商机管理 1-020-001-000 ==========
19 ErrorCode BUSINESS_NOT_EXISTS = new ErrorCode(1_020_001_000, "商机不存在"); 19 ErrorCode BUSINESS_NOT_EXISTS = new ErrorCode(1_020_001_000, "商机不存在");
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueBaseVO.java
1 package cn.iocoder.yudao.module.crm.controller.admin.clue.vo; 1 package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
2 2
3 import cn.iocoder.yudao.framework.common.validation.Mobile; 3 import cn.iocoder.yudao.framework.common.validation.Mobile;
  4 +import cn.iocoder.yudao.framework.common.validation.Telephone;
4 import io.swagger.v3.oas.annotations.media.Schema; 5 import io.swagger.v3.oas.annotations.media.Schema;
5 import lombok.Data; 6 import lombok.Data;
6 import org.springframework.format.annotation.DateTimeFormat; 7 import org.springframework.format.annotation.DateTimeFormat;
7 8
  9 +import javax.validation.constraints.NotEmpty;
8 import javax.validation.constraints.NotNull; 10 import javax.validation.constraints.NotNull;
9 import java.time.LocalDateTime; 11 import java.time.LocalDateTime;
10 12
@@ -17,44 +19,31 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_ @@ -17,44 +19,31 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
17 @Data 19 @Data
18 public class CrmClueBaseVO { 20 public class CrmClueBaseVO {
19 21
20 - // TODO @wanwan:转化状态,新增和修改的时候,应该不传递的哈;而是在未来的时候,才会更新到  
21 - @Schema(description = "转化状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")  
22 - @NotNull(message = "转化状态不能为空")  
23 - private Boolean transformStatus;  
24 -  
25 - // TODO @wanwan:同 transformStatus  
26 - @Schema(description = "跟进状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")  
27 - @NotNull(message = "跟进状态不能为空")  
28 - private Boolean followUpStatus;  
29 -  
30 @Schema(description = "线索名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "线索xxx") 22 @Schema(description = "线索名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "线索xxx")
31 - @NotNull(message = "线索名称不能为空") // TODO @wanwan:应该是 NotEmpty 噢。空串都无法接受 23 + @NotEmpty(message = "线索名称不能为空")
32 private String name; 24 private String name;
33 25
34 - // TODO @wanwan:中英文之间,要有个空格;例如说,客户 id 不能为空  
35 - @Schema(description = "客户id", requiredMode = Schema.RequiredMode.REQUIRED, example = "520")  
36 - @NotNull(message = "客户id不能为空") 26 + @Schema(description = "客户 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "520")
  27 + @NotNull(message = "客户不能为空")
37 private Long customerId; 28 private Long customerId;
38 29
39 @Schema(description = "下次联系时间", example = "2023-10-18 01:00:00") 30 @Schema(description = "下次联系时间", example = "2023-10-18 01:00:00")
40 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) 31 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
41 private LocalDateTime contactNextTime; 32 private LocalDateTime contactNextTime;
42 33
43 - // TODO @wanwan:@Schema 在 @Mobile 之前,要保持统一的顺序;2)可以加个 @Telephone 的校验格式;应该不是手机的格式哈  
44 - @Mobile(message = "电话格式不正确")  
45 @Schema(description = "电话", example = "18000000000") 34 @Schema(description = "电话", example = "18000000000")
  35 + @Telephone
46 private String telephone; 36 private String telephone;
47 37
48 - // TODO @wanwan:@Schema 在 @Mobile 之前,要保持统一的顺序;2)类似 @Mobile 这个提示如果是默认的,就可以不写 message  
49 - @Mobile(message = "手机号格式不正确")  
50 @Schema(description = "手机号", example = "18000000000") 38 @Schema(description = "手机号", example = "18000000000")
  39 + @Mobile
51 private String mobile; 40 private String mobile;
52 41
53 @Schema(description = "地址", example = "北京市海淀区") 42 @Schema(description = "地址", example = "北京市海淀区")
54 private String address; 43 private String address;
55 44
56 @Schema(description = "负责人的用户编号", example = "27199") 45 @Schema(description = "负责人的用户编号", example = "27199")
57 - // TODO @wanwan:这个是必填字段哈; 46 + @NotNull(message = "负责人不能为空")
58 private Long ownerUserId; 47 private Long ownerUserId;
59 48
60 @Schema(description = "最后跟进时间") 49 @Schema(description = "最后跟进时间")
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueExcelVO.java
1 package cn.iocoder.yudao.module.crm.controller.admin.clue.vo; 1 package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
2 2
  3 +import cn.iocoder.yudao.module.infra.enums.DictTypeConstants;
3 import io.swagger.v3.oas.annotations.media.Schema; 4 import io.swagger.v3.oas.annotations.media.Schema;
4 import lombok.*; 5 import lombok.*;
5 import java.util.*; 6 import java.util.*;
@@ -12,7 +13,6 @@ import com.alibaba.excel.annotation.ExcelProperty; @@ -12,7 +13,6 @@ import com.alibaba.excel.annotation.ExcelProperty;
12 import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; 13 import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
13 import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; 14 import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
14 15
15 -  
16 /** 16 /**
17 * 线索 Excel VO 17 * 线索 Excel VO
18 * 18 *
@@ -21,20 +21,21 @@ import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; @@ -21,20 +21,21 @@ import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
21 @Data 21 @Data
22 public class CrmClueExcelVO { 22 public class CrmClueExcelVO {
23 23
24 - @ExcelProperty("编号,主键自增") 24 + @ExcelProperty("编号")
25 private Long id; 25 private Long id;
26 26
27 @ExcelProperty(value = "转化状态", converter = DictConvert.class) 27 @ExcelProperty(value = "转化状态", converter = DictConvert.class)
28 - @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 XXXDictTypeConstants 枚举类中 28 + @DictFormat(DictTypeConstants.BOOLEAN_STRING)
29 private Boolean transformStatus; 29 private Boolean transformStatus;
30 30
31 @ExcelProperty(value = "跟进状态", converter = DictConvert.class) 31 @ExcelProperty(value = "跟进状态", converter = DictConvert.class)
32 - @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 XXXDictTypeConstants 枚举类中 32 + @DictFormat(DictTypeConstants.BOOLEAN_STRING)
33 private Boolean followUpStatus; 33 private Boolean followUpStatus;
34 34
35 @ExcelProperty("线索名称") 35 @ExcelProperty("线索名称")
36 private String name; 36 private String name;
37 37
  38 + // TODO 这里需要导出成客户名称
38 @ExcelProperty("客户id") 39 @ExcelProperty("客户id")
39 private Long customerId; 40 private Long customerId;
40 41
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmCluePageReqVO.java
@@ -5,54 +5,18 @@ import io.swagger.v3.oas.annotations.media.Schema; @@ -5,54 +5,18 @@ import io.swagger.v3.oas.annotations.media.Schema;
5 import lombok.Data; 5 import lombok.Data;
6 import lombok.EqualsAndHashCode; 6 import lombok.EqualsAndHashCode;
7 import lombok.ToString; 7 import lombok.ToString;
8 -import org.springframework.format.annotation.DateTimeFormat;  
9 -  
10 -import java.time.LocalDateTime;  
11 -  
12 -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;  
13 8
14 @Schema(description = "管理后台 - 线索分页 Request VO") 9 @Schema(description = "管理后台 - 线索分页 Request VO")
15 @Data 10 @Data
16 @EqualsAndHashCode(callSuper = true) 11 @EqualsAndHashCode(callSuper = true)
17 @ToString(callSuper = true) 12 @ToString(callSuper = true)
18 public class CrmCluePageReqVO extends PageParam { 13 public class CrmCluePageReqVO extends PageParam {
19 -  
20 - // TODO @wanwan:目前只要支持 name、mobile、telephone 的搜索即可;其它字段应该暂时不需要哈;  
21 -  
22 - @Schema(description = "转化状态", example = "true")  
23 - private Boolean transformStatus;  
24 -  
25 - @Schema(description = "跟进状态", example = "true")  
26 - private Boolean followUpStatus;  
27 -  
28 @Schema(description = "线索名称", example = "线索xxx") 14 @Schema(description = "线索名称", example = "线索xxx")
29 private String name; 15 private String name;
30 16
31 - @Schema(description = "客户id", example = "520")  
32 - private Long customerId;  
33 -  
34 - @Schema(description = "下次联系时间", example = "2023-10-18 01:00:00")  
35 - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)  
36 - private LocalDateTime[] contactNextTime;  
37 -  
38 @Schema(description = "电话", example = "18000000000") 17 @Schema(description = "电话", example = "18000000000")
39 private String telephone; 18 private String telephone;
40 19
41 @Schema(description = "手机号", example = "18000000000") 20 @Schema(description = "手机号", example = "18000000000")
42 private String mobile; 21 private String mobile;
43 -  
44 - @Schema(description = "地址", example = "北京市海淀区")  
45 - private String address;  
46 -  
47 - @Schema(description = "负责人的用户编号", example = "27199")  
48 - private Long ownerUserId;  
49 -  
50 - @Schema(description = "最后跟进时间")  
51 - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)  
52 - private LocalDateTime[] contactLastTime;  
53 -  
54 - @Schema(description = "创建时间")  
55 - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)  
56 - private LocalDateTime[] createTime;  
57 -  
58 } 22 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueRespVO.java
@@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.crm.controller.admin.clue.vo; @@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
2 2
3 import io.swagger.v3.oas.annotations.media.Schema; 3 import io.swagger.v3.oas.annotations.media.Schema;
4 import lombok.*; 4 import lombok.*;
  5 +
  6 +import javax.validation.constraints.NotNull;
5 import java.time.LocalDateTime; 7 import java.time.LocalDateTime;
6 8
7 @Schema(description = "管理后台 - 线索 Response VO") 9 @Schema(description = "管理后台 - 线索 Response VO")
@@ -16,4 +18,10 @@ public class CrmClueRespVO extends CrmClueBaseVO { @@ -16,4 +18,10 @@ public class CrmClueRespVO extends CrmClueBaseVO {
16 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) 18 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
17 private LocalDateTime createTime; 19 private LocalDateTime createTime;
18 20
  21 + @Schema(description = "转化状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
  22 + private Boolean transformStatus;
  23 +
  24 + @Schema(description = "跟进状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
  25 + private Boolean followUpStatus;
  26 +
19 } 27 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/clue/CrmClueMapper.java
@@ -19,17 +19,9 @@ public interface CrmClueMapper extends BaseMapperX&lt;CrmClueDO&gt; { @@ -19,17 +19,9 @@ public interface CrmClueMapper extends BaseMapperX&lt;CrmClueDO&gt; {
19 19
20 default PageResult<CrmClueDO> selectPage(CrmCluePageReqVO reqVO) { 20 default PageResult<CrmClueDO> selectPage(CrmCluePageReqVO reqVO) {
21 return selectPage(reqVO, new LambdaQueryWrapperX<CrmClueDO>() 21 return selectPage(reqVO, new LambdaQueryWrapperX<CrmClueDO>()
22 - .eqIfPresent(CrmClueDO::getTransformStatus, reqVO.getTransformStatus())  
23 - .eqIfPresent(CrmClueDO::getFollowUpStatus, reqVO.getFollowUpStatus())  
24 .likeIfPresent(CrmClueDO::getName, reqVO.getName()) 22 .likeIfPresent(CrmClueDO::getName, reqVO.getName())
25 - .eqIfPresent(CrmClueDO::getCustomerId, reqVO.getCustomerId())  
26 - .betweenIfPresent(CrmClueDO::getContactNextTime, reqVO.getContactNextTime())  
27 .likeIfPresent(CrmClueDO::getTelephone, reqVO.getTelephone()) 23 .likeIfPresent(CrmClueDO::getTelephone, reqVO.getTelephone())
28 .likeIfPresent(CrmClueDO::getMobile, reqVO.getMobile()) 24 .likeIfPresent(CrmClueDO::getMobile, reqVO.getMobile())
29 - .likeIfPresent(CrmClueDO::getAddress, reqVO.getAddress())  
30 - .eqIfPresent(CrmClueDO::getOwnerUserId, reqVO.getOwnerUserId())  
31 - .betweenIfPresent(CrmClueDO::getContactLastTime, reqVO.getContactLastTime())  
32 - .betweenIfPresent(CrmClueDO::getCreateTime, reqVO.getCreateTime())  
33 .orderByDesc(CrmClueDO::getId)); 25 .orderByDesc(CrmClueDO::getId));
34 } 26 }
35 27