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

# Conflicts:
#	sql/mysql/crm.sql
This commit is contained in:
YunaiV 2023-10-21 22:00:15 +08:00
commit 9cf4cfca86
9 changed files with 96 additions and 89 deletions

View File

@ -41,26 +41,26 @@ CREATE TABLE `crm_contract`
DROP TABLE IF EXISTS `crm_clue`; DROP TABLE IF EXISTS `crm_clue`;
CREATE TABLE `crm_clue` ( CREATE TABLE `crm_clue` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号主键自增', `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号主键自增',
`transform_status` tinyint NOT NULL COMMENT '转化状态', `transform_status` tinyint DEFAULT NULL COMMENT '转化状态',
`follow_up_status` tinyint NOT NULL COMMENT '跟进状态', `follow_up_status` tinyint DEFAULT NULL COMMENT '跟进状态',
`name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '线索名称', `name` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '线索名称',
`customer_id` bigint NOT NULL COMMENT '客户id', `customer_id` bigint NOT NULL COMMENT '客户id',
`contact_next_time` datetime NULL DEFAULT NULL COMMENT '下次联系时间', `contact_next_time` datetime DEFAULT NULL COMMENT '下次联系时间',
`telephone` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '电话', `telephone` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '电话',
`mobile` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '手机号', `mobile` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '手机号',
`address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '地址', `address` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '地址',
`owner_user_id` bigint NULL DEFAULT NULL COMMENT '负责人的用户编号', `owner_user_id` bigint NOT NULL COMMENT '负责人的用户编号',
`contact_last_time` datetime NULL DEFAULT NULL COMMENT '最后跟进时间', `contact_last_time` datetime DEFAULT NULL COMMENT '最后跟进时间',
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注', `remark` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者', `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' 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 '更新时间', `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
PRIMARY KEY (`id`) USING BTREE PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '线索表' ROW_FORMAT = Dynamic; ) ENGINE = InnoDB COMMENT = '线索表' ;
DROP TABLE IF EXISTS `crm_business`; DROP TABLE IF EXISTS `crm_business`;
CREATE TABLE `crm_business` ( CREATE TABLE `crm_business` (

View File

@ -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<? extends Payload>[] payload() default {};
}

View File

@ -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<Telephone, String> {
@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);
}
}

View File

@ -12,8 +12,8 @@ public interface ErrorCodeConstants {
// ========== 合同管理 1-020-000-000 ========== // ========== 合同管理 1-020-000-000 ==========
ErrorCode CONTRACT_NOT_EXISTS = new ErrorCode(1_020_000_000, "合同不存在"); ErrorCode CONTRACT_NOT_EXISTS = new ErrorCode(1_020_000_000, "合同不存在");
// TODO @wanwan要单独一个分段噢 // ========== 线索管理 1-020-001-000 ==========
ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_000_001, "线索不存在"); ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索不存在");
// ========== 商机管理 1-020-001-000 ========== // ========== 商机管理 1-020-001-000 ==========
ErrorCode BUSINESS_NOT_EXISTS = new ErrorCode(1_020_001_000, "商机不存在"); ErrorCode BUSINESS_NOT_EXISTS = new ErrorCode(1_020_001_000, "商机不存在");

View File

@ -1,10 +1,12 @@
package cn.iocoder.yudao.module.crm.controller.admin.clue.vo; 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.Mobile;
import cn.iocoder.yudao.framework.common.validation.Telephone;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@ -17,44 +19,31 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
@Data @Data
public class CrmClueBaseVO { 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") @Schema(description = "线索名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "线索xxx")
@NotNull(message = "线索名称不能为空") // TODO @wanwan应该是 NotEmpty 空串都无法接受 @NotEmpty(message = "线索名称不能为空")
private String name; private String name;
// TODO @wanwan中英文之间要有个空格例如说客户 id 不能为空 @Schema(description = "客户 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "520")
@Schema(description = "客户id", requiredMode = Schema.RequiredMode.REQUIRED, example = "520") @NotNull(message = "客户不能为空")
@NotNull(message = "客户id不能为空")
private Long customerId; private Long customerId;
@Schema(description = "下次联系时间", example = "2023-10-18 01:00:00") @Schema(description = "下次联系时间", example = "2023-10-18 01:00:00")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime contactNextTime; private LocalDateTime contactNextTime;
// TODO @wanwan@Schema @Mobile 之前要保持统一的顺序2可以加个 @Telephone 的校验格式应该不是手机的格式哈
@Mobile(message = "电话格式不正确")
@Schema(description = "电话", example = "18000000000") @Schema(description = "电话", example = "18000000000")
@Telephone
private String telephone; private String telephone;
// TODO @wanwan@Schema @Mobile 之前要保持统一的顺序2类似 @Mobile 这个提示如果是默认的就可以不写 message
@Mobile(message = "手机号格式不正确")
@Schema(description = "手机号", example = "18000000000") @Schema(description = "手机号", example = "18000000000")
@Mobile
private String mobile; private String mobile;
@Schema(description = "地址", example = "北京市海淀区") @Schema(description = "地址", example = "北京市海淀区")
private String address; private String address;
@Schema(description = "负责人的用户编号", example = "27199") @Schema(description = "负责人的用户编号", example = "27199")
// TODO @wanwan这个是必填字段哈 @NotNull(message = "负责人不能为空")
private Long ownerUserId; private Long ownerUserId;
@Schema(description = "最后跟进时间") @Schema(description = "最后跟进时间")

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.crm.controller.admin.clue.vo; 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 io.swagger.v3.oas.annotations.media.Schema;
import lombok.*; import lombok.*;
import java.util.*; 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.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
/** /**
* 线索 Excel VO * 线索 Excel VO
* *
@ -21,20 +21,21 @@ import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
@Data @Data
public class CrmClueExcelVO { public class CrmClueExcelVO {
@ExcelProperty("编号,主键自增") @ExcelProperty("编号")
private Long id; private Long id;
@ExcelProperty(value = "转化状态", converter = DictConvert.class) @ExcelProperty(value = "转化状态", converter = DictConvert.class)
@DictFormat("infra_boolean_string") // TODO 代码优化建议设置到对应的 XXXDictTypeConstants 枚举类中 @DictFormat(DictTypeConstants.BOOLEAN_STRING)
private Boolean transformStatus; private Boolean transformStatus;
@ExcelProperty(value = "跟进状态", converter = DictConvert.class) @ExcelProperty(value = "跟进状态", converter = DictConvert.class)
@DictFormat("infra_boolean_string") // TODO 代码优化建议设置到对应的 XXXDictTypeConstants 枚举类中 @DictFormat(DictTypeConstants.BOOLEAN_STRING)
private Boolean followUpStatus; private Boolean followUpStatus;
@ExcelProperty("线索名称") @ExcelProperty("线索名称")
private String name; private String name;
// TODO 这里需要导出成客户名称
@ExcelProperty("客户id") @ExcelProperty("客户id")
private Long customerId; private Long customerId;

View File

@ -5,54 +5,18 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.ToString; 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") @Schema(description = "管理后台 - 线索分页 Request VO")
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true) @ToString(callSuper = true)
public class CrmCluePageReqVO extends PageParam { public class CrmCluePageReqVO extends PageParam {
// TODO @wanwan目前只要支持 namemobiletelephone 的搜索即可其它字段应该暂时不需要哈
@Schema(description = "转化状态", example = "true")
private Boolean transformStatus;
@Schema(description = "跟进状态", example = "true")
private Boolean followUpStatus;
@Schema(description = "线索名称", example = "线索xxx") @Schema(description = "线索名称", example = "线索xxx")
private String name; 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") @Schema(description = "电话", example = "18000000000")
private String telephone; private String telephone;
@Schema(description = "手机号", example = "18000000000") @Schema(description = "手机号", example = "18000000000")
private String mobile; 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;
} }

View File

@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*; import lombok.*;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@Schema(description = "管理后台 - 线索 Response VO") @Schema(description = "管理后台 - 线索 Response VO")
@ -16,4 +18,10 @@ public class CrmClueRespVO extends CrmClueBaseVO {
@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 = "true")
private Boolean transformStatus;
@Schema(description = "跟进状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean followUpStatus;
} }

View File

@ -19,17 +19,9 @@ public interface CrmClueMapper extends BaseMapperX<CrmClueDO> {
default PageResult<CrmClueDO> selectPage(CrmCluePageReqVO reqVO) { default PageResult<CrmClueDO> selectPage(CrmCluePageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<CrmClueDO>() return selectPage(reqVO, new LambdaQueryWrapperX<CrmClueDO>()
.eqIfPresent(CrmClueDO::getTransformStatus, reqVO.getTransformStatus())
.eqIfPresent(CrmClueDO::getFollowUpStatus, reqVO.getFollowUpStatus())
.likeIfPresent(CrmClueDO::getName, reqVO.getName()) .likeIfPresent(CrmClueDO::getName, reqVO.getName())
.eqIfPresent(CrmClueDO::getCustomerId, reqVO.getCustomerId())
.betweenIfPresent(CrmClueDO::getContactNextTime, reqVO.getContactNextTime())
.likeIfPresent(CrmClueDO::getTelephone, reqVO.getTelephone()) .likeIfPresent(CrmClueDO::getTelephone, reqVO.getTelephone())
.likeIfPresent(CrmClueDO::getMobile, reqVO.getMobile()) .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)); .orderByDesc(CrmClueDO::getId));
} }