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`;
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` (

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 ==========
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, "商机不存在");

View File

@ -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 = "最后跟进时间")

View File

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

View File

@ -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目前只要支持 namemobiletelephone 的搜索即可其它字段应该暂时不需要哈
@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;
}

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

View File

@ -19,17 +19,9 @@ public interface CrmClueMapper extends BaseMapperX<CrmClueDO> {
default PageResult<CrmClueDO> selectPage(CrmCluePageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<CrmClueDO>()
.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));
}