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