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 41  
42 42 DROP TABLE IF EXISTS `crm_clue`;
43 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 65 DROP TABLE IF EXISTS `crm_business`;
66 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 12 // ========== 合同管理 1-020-000-000 ==========
13 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 18 // ========== 商机管理 1-020-001-000 ==========
19 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 1 package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
2 2  
3 3 import cn.iocoder.yudao.framework.common.validation.Mobile;
  4 +import cn.iocoder.yudao.framework.common.validation.Telephone;
4 5 import io.swagger.v3.oas.annotations.media.Schema;
5 6 import lombok.Data;
6 7 import org.springframework.format.annotation.DateTimeFormat;
7 8  
  9 +import javax.validation.constraints.NotEmpty;
8 10 import javax.validation.constraints.NotNull;
9 11 import java.time.LocalDateTime;
10 12  
... ... @@ -17,44 +19,31 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
17 19 @Data
18 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 22 @Schema(description = "线索名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "线索xxx")
31   - @NotNull(message = "线索名称不能为空") // TODO @wanwan:应该是 NotEmpty 噢。空串都无法接受
  23 + @NotEmpty(message = "线索名称不能为空")
32 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 28 private Long customerId;
38 29  
39 30 @Schema(description = "下次联系时间", example = "2023-10-18 01:00:00")
40 31 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
41 32 private LocalDateTime contactNextTime;
42 33  
43   - // TODO @wanwan:@Schema 在 @Mobile 之前,要保持统一的顺序;2)可以加个 @Telephone 的校验格式;应该不是手机的格式哈
44   - @Mobile(message = "电话格式不正确")
45 34 @Schema(description = "电话", example = "18000000000")
  35 + @Telephone
46 36 private String telephone;
47 37  
48   - // TODO @wanwan:@Schema 在 @Mobile 之前,要保持统一的顺序;2)类似 @Mobile 这个提示如果是默认的,就可以不写 message
49   - @Mobile(message = "手机号格式不正确")
50 38 @Schema(description = "手机号", example = "18000000000")
  39 + @Mobile
51 40 private String mobile;
52 41  
53 42 @Schema(description = "地址", example = "北京市海淀区")
54 43 private String address;
55 44  
56 45 @Schema(description = "负责人的用户编号", example = "27199")
57   - // TODO @wanwan:这个是必填字段哈;
  46 + @NotNull(message = "负责人不能为空")
58 47 private Long ownerUserId;
59 48  
60 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 1 package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
2 2  
  3 +import cn.iocoder.yudao.module.infra.enums.DictTypeConstants;
3 4 import io.swagger.v3.oas.annotations.media.Schema;
4 5 import lombok.*;
5 6 import java.util.*;
... ... @@ -12,7 +13,6 @@ import com.alibaba.excel.annotation.ExcelProperty;
12 13 import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
13 14 import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
14 15  
15   -
16 16 /**
17 17 * 线索 Excel VO
18 18 *
... ... @@ -21,20 +21,21 @@ import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
21 21 @Data
22 22 public class CrmClueExcelVO {
23 23  
24   - @ExcelProperty("编号,主键自增")
  24 + @ExcelProperty("编号")
25 25 private Long id;
26 26  
27 27 @ExcelProperty(value = "转化状态", converter = DictConvert.class)
28   - @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 XXXDictTypeConstants 枚举类中
  28 + @DictFormat(DictTypeConstants.BOOLEAN_STRING)
29 29 private Boolean transformStatus;
30 30  
31 31 @ExcelProperty(value = "跟进状态", converter = DictConvert.class)
32   - @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 XXXDictTypeConstants 枚举类中
  32 + @DictFormat(DictTypeConstants.BOOLEAN_STRING)
33 33 private Boolean followUpStatus;
34 34  
35 35 @ExcelProperty("线索名称")
36 36 private String name;
37 37  
  38 + // TODO 这里需要导出成客户名称
38 39 @ExcelProperty("客户id")
39 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 5 import lombok.Data;
6 6 import lombok.EqualsAndHashCode;
7 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 9 @Schema(description = "管理后台 - 线索分页 Request VO")
15 10 @Data
16 11 @EqualsAndHashCode(callSuper = true)
17 12 @ToString(callSuper = true)
18 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 14 @Schema(description = "线索名称", example = "线索xxx")
29 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 17 @Schema(description = "电话", example = "18000000000")
39 18 private String telephone;
40 19  
41 20 @Schema(description = "手机号", example = "18000000000")
42 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 2  
3 3 import io.swagger.v3.oas.annotations.media.Schema;
4 4 import lombok.*;
  5 +
  6 +import javax.validation.constraints.NotNull;
5 7 import java.time.LocalDateTime;
6 8  
7 9 @Schema(description = "管理后台 - 线索 Response VO")
... ... @@ -16,4 +18,10 @@ public class CrmClueRespVO extends CrmClueBaseVO {
16 18 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
17 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 19  
20 20 default PageResult<CrmClueDO> selectPage(CrmCluePageReqVO reqVO) {
21 21 return selectPage(reqVO, new LambdaQueryWrapperX<CrmClueDO>()
22   - .eqIfPresent(CrmClueDO::getTransformStatus, reqVO.getTransformStatus())
23   - .eqIfPresent(CrmClueDO::getFollowUpStatus, reqVO.getFollowUpStatus())
24 22 .likeIfPresent(CrmClueDO::getName, reqVO.getName())
25   - .eqIfPresent(CrmClueDO::getCustomerId, reqVO.getCustomerId())
26   - .betweenIfPresent(CrmClueDO::getContactNextTime, reqVO.getContactNextTime())
27 23 .likeIfPresent(CrmClueDO::getTelephone, reqVO.getTelephone())
28 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 25 .orderByDesc(CrmClueDO::getId));
34 26 }
35 27  
... ...