Explorar o código

修改:<公海,我的客户,客户跟进业务逻辑>

张旭锋 hai 1 mes
pai
achega
d07b36fb47
Modificáronse 65 ficheiros con 5625 adicións e 94 borrados
  1. 1 1
      src/main/java/com/jtzx/crm/module/api/constant/RedisConstant.java
  2. 0 2
      src/main/java/com/jtzx/crm/module/api/web/auth/AdminAuthController.java
  3. 105 0
      src/main/java/com/jtzx/crm/module/sys/entity/SysCustomerDataEntity.java
  4. 98 0
      src/main/java/com/jtzx/crm/module/sys/entity/SysCustomerFollowAuditEntity.java
  5. 133 0
      src/main/java/com/jtzx/crm/module/sys/entity/SysCustomerFollowEntity.java
  6. 85 0
      src/main/java/com/jtzx/crm/module/sys/entity/SysCustomerOperLogEntity.java
  7. 54 0
      src/main/java/com/jtzx/crm/module/sys/entity/SysCustomerPersonEntity.java
  8. 46 0
      src/main/java/com/jtzx/crm/module/sys/entity/SysCustomerTodoEntity.java
  9. 15 0
      src/main/java/com/jtzx/crm/module/sys/mapper/SysCustomerDataMapper.java
  10. 15 0
      src/main/java/com/jtzx/crm/module/sys/mapper/SysCustomerFollowAuditMapper.java
  11. 15 0
      src/main/java/com/jtzx/crm/module/sys/mapper/SysCustomerFollowMapper.java
  12. 15 0
      src/main/java/com/jtzx/crm/module/sys/mapper/SysCustomerOperLogMapper.java
  13. 15 0
      src/main/java/com/jtzx/crm/module/sys/mapper/SysCustomerPersonMapper.java
  14. 15 0
      src/main/java/com/jtzx/crm/module/sys/mapper/SysCustomerTodoMapper.java
  15. 5 0
      src/main/java/com/jtzx/crm/module/sys/mapper/xml/SysCustomerDataMapper.xml
  16. 5 0
      src/main/java/com/jtzx/crm/module/sys/mapper/xml/SysCustomerFollowAuditMapper.xml
  17. 5 0
      src/main/java/com/jtzx/crm/module/sys/mapper/xml/SysCustomerFollowMapper.xml
  18. 5 0
      src/main/java/com/jtzx/crm/module/sys/mapper/xml/SysCustomerOperLogMapper.xml
  19. 5 0
      src/main/java/com/jtzx/crm/module/sys/mapper/xml/SysCustomerPersonMapper.xml
  20. 5 0
      src/main/java/com/jtzx/crm/module/sys/mapper/xml/SysCustomerTodoMapper.xml
  21. 66 0
      src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerDataFollowListDTO.java
  22. 93 0
      src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerDataInsertOrUpdateDTO.java
  23. 94 0
      src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerDataListDTO.java
  24. 49 0
      src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerDataPrivateListDTO.java
  25. 40 0
      src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerDataPublicListDTO.java
  26. 91 0
      src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerFollowAuditInsertOrUpdateDTO.java
  27. 91 0
      src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerFollowAuditListDTO.java
  28. 97 0
      src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerFollowInsertOrUpdateDTO.java
  29. 97 0
      src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerFollowListDTO.java
  30. 75 0
      src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerOperLogInsertOrUpdateDTO.java
  31. 79 0
      src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerOperLogListDTO.java
  32. 56 0
      src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerPersonInsertOrUpdateDTO.java
  33. 58 0
      src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerPersonListDTO.java
  34. 47 0
      src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerTodoInsertOrUpdateDTO.java
  35. 52 0
      src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerTodoListDTO.java
  36. 43 0
      src/main/java/com/jtzx/crm/module/sys/model/response/BindUserBO.java
  37. 47 0
      src/main/java/com/jtzx/crm/module/sys/model/response/DateSelectBO.java
  38. 39 0
      src/main/java/com/jtzx/crm/module/sys/model/response/SelectBO.java
  39. 97 0
      src/main/java/com/jtzx/crm/module/sys/model/response/SysCustomerDataBO.java
  40. 60 0
      src/main/java/com/jtzx/crm/module/sys/model/response/SysCustomerDataPrivateBO.java
  41. 52 0
      src/main/java/com/jtzx/crm/module/sys/model/response/SysCustomerDataPublicBO.java
  42. 94 0
      src/main/java/com/jtzx/crm/module/sys/model/response/SysCustomerFollowAuditBO.java
  43. 105 0
      src/main/java/com/jtzx/crm/module/sys/model/response/SysCustomerFollowBO.java
  44. 70 0
      src/main/java/com/jtzx/crm/module/sys/model/response/SysCustomerFollowListBO.java
  45. 47 0
      src/main/java/com/jtzx/crm/module/sys/model/response/SysCustomerFollowSelectBO.java
  46. 82 0
      src/main/java/com/jtzx/crm/module/sys/model/response/SysCustomerOperLogBO.java
  47. 61 0
      src/main/java/com/jtzx/crm/module/sys/model/response/SysCustomerPersonBO.java
  48. 40 0
      src/main/java/com/jtzx/crm/module/sys/model/response/SysCustomerPrivateSelectBO.java
  49. 29 0
      src/main/java/com/jtzx/crm/module/sys/model/response/SysCustomerPublicSelectBO.java
  50. 40 0
      src/main/java/com/jtzx/crm/module/sys/model/response/SysCustomerSelectBO.java
  51. 55 0
      src/main/java/com/jtzx/crm/module/sys/model/response/SysCustomerTodoBO.java
  52. 244 0
      src/main/java/com/jtzx/crm/module/sys/service/SysCustomerDataFollowService.java
  53. 247 0
      src/main/java/com/jtzx/crm/module/sys/service/SysCustomerDataPrivateService.java
  54. 167 0
      src/main/java/com/jtzx/crm/module/sys/service/SysCustomerDataPublicService.java
  55. 239 0
      src/main/java/com/jtzx/crm/module/sys/service/SysCustomerDataService.java
  56. 238 0
      src/main/java/com/jtzx/crm/module/sys/service/SysCustomerFollowAuditService.java
  57. 242 0
      src/main/java/com/jtzx/crm/module/sys/service/SysCustomerFollowService.java
  58. 229 0
      src/main/java/com/jtzx/crm/module/sys/service/SysCustomerOperLogService.java
  59. 216 0
      src/main/java/com/jtzx/crm/module/sys/service/SysCustomerPersonService.java
  60. 212 0
      src/main/java/com/jtzx/crm/module/sys/service/SysCustomerTodoService.java
  61. 711 0
      src/main/java/com/jtzx/crm/module/sys/service/SysCustomerUserCommonService.java
  62. 6 85
      src/main/java/com/jtzx/crm/module/sys/service/SysRoleService.java
  63. 27 1
      src/main/java/com/jtzx/crm/module/sys/service/SysUserGroupRelationService.java
  64. 87 5
      src/main/java/com/jtzx/crm/module/sys/service/SysUserRoleRelationService.java
  65. 72 0
      src/main/java/com/jtzx/crm/module/sys/util/DateSelectUtil.java

+ 1 - 1
src/main/java/com/jtzx/crm/module/api/constant/RedisConstant.java

@@ -34,7 +34,7 @@ public final class RedisConstant {
     /**
      * 短信的有效期,120秒-2分钟
      */
-    public static final Integer SMS_CODE_DURATION = 120;//秒
+    public static final Integer SMS_CODE_DURATION = 60;//秒
 
 
 

+ 0 - 2
src/main/java/com/jtzx/crm/module/api/web/auth/AdminAuthController.java

@@ -158,8 +158,6 @@ public class AdminAuthController {
         SysUserLoginBO userInfo = sysUserService.adminLogin(dto.getUsername(), dto.getPassword());
         if (userInfo == null || StrUtil.isEmpty(userInfo.getPhoneNo()) || !userInfo.getPhoneNo().equals(dto.getMobile())) {
             resultData.setStatus(false);
-
-
             if (userInfo == null) {
                 //跳到账号密码登陆页面
                 return ApiResult.data("账号不存在", resultData).setCode(401);

+ 105 - 0
src/main/java/com/jtzx/crm/module/sys/entity/SysCustomerDataEntity.java

@@ -0,0 +1,105 @@
+package com.jtzx.crm.module.sys.entity;
+
+import cc.uncarbon.framework.crud.entity.HelioBaseEntity;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+
+import java.time.LocalDateTime;
+
+
+/**
+ * 客户(学校)数据
+ */
+@EqualsAndHashCode(callSuper = true)
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+@TableName(value = "sys_customer_data")
+public class SysCustomerDataEntity extends HelioBaseEntity<Long> {
+
+    @Schema(description = "名称")
+    @TableField(value = "name")
+    private String name;
+
+    @Schema(description = "学段id(param_base_data表主id)")
+    @TableField(value = "stage_id")
+    private Long stageId;
+
+    @Schema(description = "学段名称")
+    @TableField(value = "stage_name")
+    private String stageName;
+
+    @Schema(description = "省份id(省份表主id)")
+    @TableField(value = "province_id")
+    private Long provinceId;
+
+    @Schema(description = "省份名称")
+    @TableField(value = "province_name")
+    private String provinceName;
+
+    @Schema(description = "城市id(城市表主id)")
+    @TableField(value = "city_id")
+    private Long cityId;
+
+    @Schema(description = "城市名称")
+    @TableField(value = "city_name")
+    private String cityName;
+
+    @Schema(description = "地址")
+    @TableField(value = "address")
+    private String address;
+
+    @Schema(description = "放入公海时间")
+    @TableField(value = "reset_datetime")
+    private LocalDateTime resetDatetime;
+
+    @Schema(description = "认领人id")
+    @TableField(value = "claim_user_id")
+    private Long claimUserId;
+
+    @Schema(description = "认领人名称")
+    @TableField(value = "claim_user_name")
+    private String claimUserName;
+
+    @Schema(description = "认领状态,0-否,1-是")
+    @TableField(value = "claim_status")
+    private Integer claimStatus;
+
+    @Schema(description = "认领时间")
+    @TableField(value = "claim_datetime")
+    private LocalDateTime claimDatetime;
+
+    @Schema(description = "认领人员所在的项目组id")
+    @TableField(value = "claim_user_group_id")
+    private Long claimUserGroupId;
+
+    @Schema(description = "跟进状态id,赢单状态id固定-37,方案演示id固定-33")
+    @TableField(value = "follow_id")
+    private Long followId;
+
+    @Schema(description = "跟进状态的名称")
+    @TableField(value = "follow_name")
+    private String followName;
+
+    @Schema(description = "赢单人id(不为null时为赢单人id)")
+    @TableField(value = "success_user_id")
+    private Long successUserId;
+
+    @Schema(description = "赢单人名称(不为null时为赢单人名称)")
+    @TableField(value = "success_user_name")
+    private String successUserName;
+
+    @Schema(description = "是否赢单,0-否,1-是")
+    @TableField(value = "success_status")
+    private Integer successStatus;
+
+}

+ 98 - 0
src/main/java/com/jtzx/crm/module/sys/entity/SysCustomerFollowAuditEntity.java

@@ -0,0 +1,98 @@
+package com.jtzx.crm.module.sys.entity;
+
+import cc.uncarbon.framework.crud.entity.HelioBaseEntity;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+import io.swagger.v3.oas.annotations.media.Schema;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+
+/**
+ * 赢单跟进审核信息
+ */
+@EqualsAndHashCode(callSuper = true)
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+@TableName(value = "sys_customer_follow_audit")
+public class SysCustomerFollowAuditEntity extends HelioBaseEntity<Long> {
+
+    @Schema(description = "认领人(跟进)id")
+    @TableField(value = "claim_user_id")
+    private Long claimUserId;
+
+    @Schema(description = "审核人id")
+    @TableField(value = "claim_user_name")
+    private String claimUserName;
+
+    @Schema(description = "认领人员所在的项目组id")
+    @TableField(value = "claim_user_group_id")
+    private Long claimUserGroupId;
+
+    @Schema(description = "跟进时间")
+    @TableField(value = "claim_datetime")
+    private LocalDateTime claimDatetime;
+
+    @Schema(description = "客户表主id")
+    @TableField(value = "customer_id")
+    private Long customerId;
+
+    @Schema(description = "客户名称")
+    @TableField(value = "customer_name")
+    private String customerName;
+
+    @Schema(description = "跟进状态id,赢单状态id固定-37,方案演示id固定-33")
+    @TableField(value = "follow_id")
+    private Long followId;
+
+    @Schema(description = "跟进状态的名称")
+    @TableField(value = "follow_name")
+    private String followName;
+
+    @Schema(description = "联系人id(customer_person表主id)")
+    @TableField(value = "customer_person_id")
+    private Long customerPersonId;
+
+    @Schema(description = "联系人名称")
+    @TableField(value = "customer_person_name")
+    private String customerPersonName;
+
+    @Schema(description = "职务id(param_base_data表主id)")
+    @TableField(value = "position_id")
+    private Long positionId;
+
+    @Schema(description = "职务名称")
+    @TableField(value = "position_name")
+    private String positionName;
+
+    @Schema(description = "跟进内容")
+    @TableField(value = "follow_content")
+    private String followContent;
+
+    @Schema(description = "产品id(param_base_data表主id)")
+    @TableField(value = "product_id")
+    private Long productId;
+
+    @Schema(description = "产品名称")
+    @TableField(value = "product_name")
+    private String productName;
+
+    @Schema(description = "审核状态:0-待审核,1-已通过,2-已驳回")
+    @TableField(value = "audit_status")
+    private Integer auditStatus;
+
+    @Schema(description = "审核人id")
+    @TableField(value = "audit_user_id")
+    private Long auditUserId;
+
+    @Schema(description = "审核人名称")
+    @TableField(value = "audit_user_name")
+    private String auditUserName;
+
+}

+ 133 - 0
src/main/java/com/jtzx/crm/module/sys/entity/SysCustomerFollowEntity.java

@@ -0,0 +1,133 @@
+package com.jtzx.crm.module.sys.entity;
+
+import cc.uncarbon.framework.crud.entity.HelioBaseEntity;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+
+import java.time.LocalDateTime;
+
+
+/**
+ * 客户跟进数据信息
+ */
+@EqualsAndHashCode(callSuper = true)
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+@TableName(value = "sys_customer_follow")
+public class SysCustomerFollowEntity extends HelioBaseEntity<Long> {
+
+    @Schema(description = "认领人(跟进)id")
+    @TableField(value = "claim_user_id")
+    private Long claimUserId;
+
+    @Schema(description = "认领人(跟进)名称")
+    @TableField(value = "claim_user_name")
+    private String claimUserName;
+
+    @Schema(description = "认领人员所在的项目组id")
+    @TableField(value = "claim_user_group_id")
+    private Long claimUserGroupId;
+
+    @Schema(description = "跟进时间")
+    @TableField(value = "claim_datetime")
+    private LocalDateTime claimDatetime;
+
+    @Schema(description = "客户表主id")
+    @TableField(value = "customer_id")
+    private Long customerId;
+
+    @Schema(description = "客户名称")
+    @TableField(value = "customer_name")
+    private String customerName;
+
+    @Schema(description = "产品id(param_base_data表主id)")
+    @TableField(value = "product_id")
+    private Long productId;
+
+    @Schema(description = "产品名称")
+    @TableField(value = "product_name")
+    private String productName;
+
+    @Schema(description = "跟进状态id,赢单状态id固定-37,方案演示id固定-33")
+    @TableField(value = "follow_id")
+    private Long followId;
+
+    @Schema(description = "跟进状态的名称")
+    @TableField(value = "follow_name")
+    private String followName;
+
+    @Schema(description = "是否赢单,0-否,1-是")
+    @TableField(value = "success_status")
+    private Integer successStatus;
+
+    @Schema(description = "联系人id(customer_person表主id)")
+    @TableField(value = "customer_person_id")
+    private Long customerPersonId;
+
+    @Schema(description = "联系人名称")
+    @TableField(value = "customer_person_name")
+    private String customerPersonName;
+
+    @Schema(description = "职务id(param_base_data表主id)")
+    @TableField(value = "position_id")
+    private Long positionId;
+
+    @Schema(description = "职务名称")
+    @TableField(value = "position_name")
+    private String positionName;
+
+    @Schema(description = "跟进内容")
+    @TableField(value = "follow_content")
+    private String followContent;
+
+    @Schema(description = "预约待办类型,0-预约,1-待办,(follow_id=33时为预约,其它为待办),这个数据可以为null")
+    @TableField(value = "order_type")
+    private Integer orderType;
+
+    @Schema(description = "待办时间")
+    @TableField(value = "order_datetime")
+    private LocalDateTime orderDatetime;
+
+    @Schema(description = "预约,待办取消状态,0-未取消,1-已取消(预约的取消需要填写取消原因,待办的取消是一个开关,不需要填写原因)")
+    @TableField(value = "order_status")
+    private Integer orderStatus;
+
+    @Schema(description = "预约取消原因")
+    @TableField(value = "cancel_remark")
+    private String cancelRemark;
+
+    @Schema(description = "学段id(param_base_data表主id)")
+    @TableField(value = "stage_id")
+    private Long stageId;
+
+    @Schema(description = "学段名称")
+    @TableField(value = "stage_name")
+    private String stageName;
+
+    @Schema(description = "省份id(省份表主id)")
+    @TableField(value = "province_id")
+    private Long provinceId;
+
+    @Schema(description = "省份名称")
+    @TableField(value = "province_name")
+    private String provinceName;
+
+    @Schema(description = "城市id(城市表主id)")
+    @TableField(value = "city_id")
+    private Long cityId;
+
+    @Schema(description = "城市名称")
+    @TableField(value = "city_name")
+    private String cityName;
+
+}

+ 85 - 0
src/main/java/com/jtzx/crm/module/sys/entity/SysCustomerOperLogEntity.java

@@ -0,0 +1,85 @@
+package com.jtzx.crm.module.sys.entity;
+
+import cc.uncarbon.framework.crud.entity.HelioBaseEntity;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+
+import java.time.LocalDateTime;
+
+
+/**
+ * 客户操作日志信息数据
+ */
+@EqualsAndHashCode(callSuper = true)
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+@TableName(value = "sys_customer_oper_log")
+public class SysCustomerOperLogEntity extends HelioBaseEntity<Long> {
+
+    @Schema(description = "客户表主id")
+    @TableField(value = "customer_id")
+    private Long customerId;
+
+    @Schema(description = "客户名称")
+    @TableField(value = "customer_name")
+    private String customerName;
+
+    @Schema(description = "操作人id")
+    @TableField(value = "oper_user_id")
+    private Long operUserId;
+
+    @Schema(description = "操作人名称")
+    @TableField(value = "oper_name")
+    private String operName;
+
+    @Schema(description = "操作类型,0-认领,1-跟进,2-转让,3-转到公海,11-新增,12-修改,13-删除")
+    @TableField(value = "oper_action")
+    private Integer operAction;
+
+    @Schema(description = "操作时间")
+    @TableField(value = "oper_datetime")
+    private LocalDateTime operDatetime;
+
+    @Schema(description = "操作人员所在的项目组id")
+    @TableField(value = "oper_user_group_id")
+    private Long operUserGroupId;
+
+    @Schema(description = "省份id(省份表主id)")
+    @TableField(value = "province_id")
+    private Long provinceId;
+
+    @Schema(description = "省份名称")
+    @TableField(value = "province_name")
+    private String provinceName;
+
+    @Schema(description = "城市id(城市表主id)")
+    @TableField(value = "city_id")
+    private Long cityId;
+
+    @Schema(description = "城市名称")
+    @TableField(value = "city_name")
+    private String cityName;
+
+    @Schema(description = "跟进状态id,赢单状态id固定-37,方案演示id固定-33")
+    @TableField(value = "follow_id")
+    private Long followId;
+
+    @Schema(description = "跟进状态的名称")
+    @TableField(value = "follow_name")
+    private String followName;
+
+    @Schema(description = "操作内容")
+    @TableField(value = "content")
+    private String content;
+
+}

+ 54 - 0
src/main/java/com/jtzx/crm/module/sys/entity/SysCustomerPersonEntity.java

@@ -0,0 +1,54 @@
+package com.jtzx.crm.module.sys.entity;
+
+import cc.uncarbon.framework.crud.entity.HelioBaseEntity;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+import io.swagger.v3.oas.annotations.media.Schema;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+
+/**
+ * 客户(学校)联系人数据
+ */
+@EqualsAndHashCode(callSuper = true)
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+@TableName(value = "sys_customer_person")
+public class SysCustomerPersonEntity extends HelioBaseEntity<Long> {
+
+    @Schema(description = "客户表主id")
+    @TableField(value = "customer_id")
+    private Long customerId;
+
+    @Schema(description = "姓名")
+    @TableField(value = "name")
+    private String name;
+
+    @Schema(description = "职务id(param_base_data表主id)")
+    @TableField(value = "position_id")
+    private Long positionId;
+
+    @Schema(description = "职务名称")
+    @TableField(value = "position_name")
+    private String positionName;
+
+    @Schema(description = "手机号")
+    @TableField(value = "phone_no")
+    private String phoneNo;
+
+    @Schema(description = "办公电话")
+    @TableField(value = "office_phone_no")
+    private String officePhoneNo;
+
+    @Schema(description = "备注")
+    @TableField(value = "remark")
+    private String remark;
+
+}

+ 46 - 0
src/main/java/com/jtzx/crm/module/sys/entity/SysCustomerTodoEntity.java

@@ -0,0 +1,46 @@
+package com.jtzx.crm.module.sys.entity;
+
+import cc.uncarbon.framework.crud.entity.HelioBaseEntity;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+import io.swagger.v3.oas.annotations.media.Schema;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+
+/**
+ * 自定义待办信息数据
+ */
+@EqualsAndHashCode(callSuper = true)
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+@TableName(value = "sys_customer_todo")
+public class SysCustomerTodoEntity extends HelioBaseEntity<Long> {
+
+    @Schema(description = "认领人(跟进)id")
+    @TableField(value = "claim_user_id")
+    private Long claimUserId;
+
+    @Schema(description = "认领人(跟进)名称")
+    @TableField(value = "claim_user_name")
+    private String claimUserName;
+
+    @Schema(description = "认领人员所在的项目组id")
+    @TableField(value = "claim_user_group_id")
+    private Long claimUserGroupId;
+
+    @Schema(description = "跟进(待办)内容")
+    @TableField(value = "follow_content")
+    private String followContent;
+
+    @Schema(description = "待办时间")
+    @TableField(value = "order_datetime")
+    private LocalDateTime orderDatetime;
+
+}

+ 15 - 0
src/main/java/com/jtzx/crm/module/sys/mapper/SysCustomerDataMapper.java

@@ -0,0 +1,15 @@
+package com.jtzx.crm.module.sys.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+import com.jtzx.crm.module.sys.entity.SysCustomerDataEntity;
+
+
+/**
+ * 客户(学校)数据
+ */
+@Mapper
+public interface SysCustomerDataMapper extends BaseMapper<SysCustomerDataEntity> {
+
+}

+ 15 - 0
src/main/java/com/jtzx/crm/module/sys/mapper/SysCustomerFollowAuditMapper.java

@@ -0,0 +1,15 @@
+package com.jtzx.crm.module.sys.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+import com.jtzx.crm.module.sys.entity.SysCustomerFollowAuditEntity;
+
+
+/**
+ * 赢单跟进审核信息
+ */
+@Mapper
+public interface SysCustomerFollowAuditMapper extends BaseMapper<SysCustomerFollowAuditEntity> {
+
+}

+ 15 - 0
src/main/java/com/jtzx/crm/module/sys/mapper/SysCustomerFollowMapper.java

@@ -0,0 +1,15 @@
+package com.jtzx.crm.module.sys.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+import com.jtzx.crm.module.sys.entity.SysCustomerFollowEntity;
+
+
+/**
+ * 客户跟进数据信息
+ */
+@Mapper
+public interface SysCustomerFollowMapper extends BaseMapper<SysCustomerFollowEntity> {
+
+}

+ 15 - 0
src/main/java/com/jtzx/crm/module/sys/mapper/SysCustomerOperLogMapper.java

@@ -0,0 +1,15 @@
+package com.jtzx.crm.module.sys.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+import com.jtzx.crm.module.sys.entity.SysCustomerOperLogEntity;
+
+
+/**
+ * 客户操作日志信息数据
+ */
+@Mapper
+public interface SysCustomerOperLogMapper extends BaseMapper<SysCustomerOperLogEntity> {
+
+}

+ 15 - 0
src/main/java/com/jtzx/crm/module/sys/mapper/SysCustomerPersonMapper.java

@@ -0,0 +1,15 @@
+package com.jtzx.crm.module.sys.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+import com.jtzx.crm.module.sys.entity.SysCustomerPersonEntity;
+
+
+/**
+ * 客户(学校)联系人数据
+ */
+@Mapper
+public interface SysCustomerPersonMapper extends BaseMapper<SysCustomerPersonEntity> {
+
+}

+ 15 - 0
src/main/java/com/jtzx/crm/module/sys/mapper/SysCustomerTodoMapper.java

@@ -0,0 +1,15 @@
+package com.jtzx.crm.module.sys.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+import com.jtzx.crm.module.sys.entity.SysCustomerTodoEntity;
+
+
+/**
+ * 自定义待办信息数据
+ */
+@Mapper
+public interface SysCustomerTodoMapper extends BaseMapper<SysCustomerTodoEntity> {
+
+}

+ 5 - 0
src/main/java/com/jtzx/crm/module/sys/mapper/xml/SysCustomerDataMapper.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.jtzx.crm.module.sys.mapper.SysCustomerDataMapper">
+
+</mapper>

+ 5 - 0
src/main/java/com/jtzx/crm/module/sys/mapper/xml/SysCustomerFollowAuditMapper.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.jtzx.crm.module.sys.mapper.SysCustomerFollowAuditMapper">
+
+</mapper>

+ 5 - 0
src/main/java/com/jtzx/crm/module/sys/mapper/xml/SysCustomerFollowMapper.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.jtzx.crm.module.sys.mapper.SysCustomerFollowMapper">
+
+</mapper>

+ 5 - 0
src/main/java/com/jtzx/crm/module/sys/mapper/xml/SysCustomerOperLogMapper.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.jtzx.crm.module.sys.mapper.SysCustomerOperLogMapper">
+
+</mapper>

+ 5 - 0
src/main/java/com/jtzx/crm/module/sys/mapper/xml/SysCustomerPersonMapper.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.jtzx.crm.module.sys.mapper.SysCustomerPersonMapper">
+
+</mapper>

+ 5 - 0
src/main/java/com/jtzx/crm/module/sys/mapper/xml/SysCustomerTodoMapper.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.jtzx.crm.module.sys.mapper.SysCustomerTodoMapper">
+
+</mapper>

+ 66 - 0
src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerDataFollowListDTO.java

@@ -0,0 +1,66 @@
+package com.jtzx.crm.module.sys.model.request;
+
+import cc.uncarbon.framework.core.constant.HelioConstant;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+
+/**
+ * 后台管理-分页列表客户(学校)跟进数据 DTO
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class AdminSysCustomerDataFollowListDTO implements Serializable {
+
+    @Schema(description = "时间段开始时间")
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime startDatetime;
+
+    @Schema(description = "时间段结束时间")
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime endDatetime;
+
+    @Schema(description = "类型id,1-首单客户,2-存量客户")
+    private Integer typeId;
+
+    @Schema(description = "学段id(param_base_data表主id)")
+    private Long stageId;
+
+    @Schema(description = "省份id(省份表主id)")
+    private Long provinceId;
+
+    @Schema(description = "城市id(城市表主id)")
+    private Long cityId;
+
+    @Schema(description = "小组id(分组表主id)")
+    private Long groupId;
+
+    @Schema(description = "用户id(用户表主id)")
+    private Long userId;
+
+    @Schema(description = "产品id(param_base_data表主id)")
+    private Long productId;
+
+    @Schema(description = "名称(关键词)")
+    private String queryStr;
+
+    @Schema(description = "页码")
+    private Integer pageNum;
+
+    @Schema(description = "每页行数")
+    private Integer pageSize;
+}

+ 93 - 0
src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerDataInsertOrUpdateDTO.java

@@ -0,0 +1,93 @@
+package com.jtzx.crm.module.sys.model.request;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.Size;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+
+/**
+ * 后台管理-新增/编辑客户(学校)数据 DTO
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class AdminSysCustomerDataInsertOrUpdateDTO implements Serializable {
+
+    @Schema(description = "主键ID", hidden = true, title = "仅更新时使用")
+    private Long id;
+
+    @Schema(description = "名称")
+    @Size(max = 255, message = "【名称】最长255位")
+    private String name;
+
+    @Schema(description = "学段id(param_base_data表主id)")
+    private Long stageId;
+
+    @Schema(description = "学段名称")
+    @Size(max = 255, message = "【学段名称】最长255位")
+    private String stageName;
+
+    @Schema(description = "省份id(省份表主id)")
+    private Long provinceId;
+
+    @Schema(description = "省份名称")
+    @Size(max = 255, message = "【省份名称】最长255位")
+    private String provinceName;
+
+    @Schema(description = "城市id(城市表主id)")
+    private Long cityId;
+
+    @Schema(description = "城市名称")
+    @Size(max = 255, message = "【城市名称】最长255位")
+    private String cityName;
+
+    @Schema(description = "地址")
+    @Size(max = 255, message = "【地址】最长255位")
+    private String address;
+
+    @Schema(description = "放入公海时间")
+    private LocalDateTime resetDatetime;
+
+    @Schema(description = "认领人id")
+    private Long claimUserId;
+
+    @Schema(description = "认领人名称")
+    @Size(max = 255, message = "【认领人名称】最长255位")
+    private String claimUserName;
+
+    @Schema(description = "认领状态,0-否,1-是")
+    private Integer claimStatus;
+
+    @Schema(description = "认领时间")
+    private LocalDateTime claimDatetime;
+
+    @Schema(description = "认领人员所在的项目组id")
+    private Long claimUserGroupId;
+
+    @Schema(description = "跟进状态id,赢单状态id固定-37,方案演示id固定-33")
+    private Long followId;
+
+    @Schema(description = "跟进状态的名称")
+    @Size(max = 255, message = "【跟进状态的名称】最长255位")
+    private String followName;
+
+    @Schema(description = "赢单人id(不为null时为赢单人id)")
+    private Long successUserId;
+
+    @Schema(description = "赢单人名称(不为null时为赢单人名称)")
+    @Size(max = 255, message = "【赢单人名称(不为null时为赢单人名称)】最长255位")
+    private String successUserName;
+
+    @Schema(description = "是否赢单,0-否,1-是")
+    private Integer successStatus;
+
+}

+ 94 - 0
src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerDataListDTO.java

@@ -0,0 +1,94 @@
+package com.jtzx.crm.module.sys.model.request;
+
+import cc.uncarbon.framework.core.constant.HelioConstant;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+
+/**
+ * 后台管理-分页列表客户(学校)数据 DTO
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class AdminSysCustomerDataListDTO implements Serializable {
+
+    @Schema(description = "名称(关键词)")
+    private String name;
+
+    @Schema(description = "学段id(param_base_data表主id)")
+    private Long stageId;
+
+    @Schema(description = "学段名称(关键词)")
+    private String stageName;
+
+    @Schema(description = "省份id(省份表主id)")
+    private Long provinceId;
+
+    @Schema(description = "省份名称(关键词)")
+    private String provinceName;
+
+    @Schema(description = "城市id(城市表主id)")
+    private Long cityId;
+
+    @Schema(description = "城市名称(关键词)")
+    private String cityName;
+
+    @Schema(description = "地址(关键词)")
+    private String address;
+
+    @Schema(description = "放入公海时间")
+    private LocalDateTime resetDatetime;
+
+    @Schema(description = "认领人id")
+    private Long claimUserId;
+
+    @Schema(description = "认领人名称(关键词)")
+    private String claimUserName;
+
+    @Schema(description = "认领状态,0-否,1-是")
+    private Integer claimStatus;
+
+    @Schema(description = "认领时间")
+    private LocalDateTime claimDatetime;
+
+    @Schema(description = "认领人员所在的项目组id")
+    private Long claimUserGroupId;
+
+    @Schema(description = "跟进状态id,赢单状态id固定-37,方案演示id固定-33")
+    private Long followId;
+
+    @Schema(description = "跟进状态的名称(关键词)")
+    private String followName;
+
+    @Schema(description = "赢单人id(不为null时为赢单人id)")
+    private Long successUserId;
+
+    @Schema(description = "赢单人名称(不为null时为赢单人名称)(关键词)")
+    private String successUserName;
+
+    @Schema(description = "是否赢单,0-否,1-是")
+    private Integer successStatus;
+
+    @Schema(description = "时间区间起")
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime beginAt;
+
+    @Schema(description = "时间区间止")
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime endAt;
+
+}

+ 49 - 0
src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerDataPrivateListDTO.java

@@ -0,0 +1,49 @@
+package com.jtzx.crm.module.sys.model.request;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+
+import java.io.Serializable;
+
+
+/**
+ * 后台管理-分页列表我的客户(学校)数据 DTO
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class AdminSysCustomerDataPrivateListDTO implements Serializable {
+
+    @Schema(description = "类型id,1-首单客户,2-存量客户")
+    private Integer typeId;
+
+    @Schema(description = "学段id(param_base_data表主id)")
+    private Long stageId;
+
+    @Schema(description = "省份id(省份表主id)")
+    private Long provinceId;
+
+    @Schema(description = "城市id(城市表主id)")
+    private Long cityId;
+
+    @Schema(description = "小组id(分组表主id)")
+    private Long groupId;
+
+    @Schema(description = "用户id(用户表主id)")
+    private Long userId;
+
+    @Schema(description = "名称(关键词)")
+    private String queryStr;
+
+    @Schema(description = "页码")
+    private Integer pageNum;
+
+    @Schema(description = "每页行数")
+    private Integer pageSize;
+}

+ 40 - 0
src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerDataPublicListDTO.java

@@ -0,0 +1,40 @@
+package com.jtzx.crm.module.sys.model.request;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+
+import java.io.Serializable;
+
+
+/**
+ * 后台管理-分页列表公海客户(学校)数据 DTO
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class AdminSysCustomerDataPublicListDTO implements Serializable {
+
+    @Schema(description = "名称(关键词)")
+    private String queryStr;
+
+    @Schema(description = "学段id(param_base_data表主id)")
+    private Long stageId;
+
+    @Schema(description = "省份id(省份表主id)")
+    private Long provinceId;
+
+    @Schema(description = "城市id(城市表主id)")
+    private Long cityId;
+
+    @Schema(description = "页码")
+    private Integer pageNum;
+
+    @Schema(description = "每页行数")
+    private Integer pageSize;
+}

+ 91 - 0
src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerFollowAuditInsertOrUpdateDTO.java

@@ -0,0 +1,91 @@
+package com.jtzx.crm.module.sys.model.request;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import java.io.Serializable;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+
+/**
+ * 后台管理-新增/编辑赢单跟进审核信息 DTO
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class AdminSysCustomerFollowAuditInsertOrUpdateDTO implements Serializable {
+
+    @Schema(description = "主键ID", hidden = true, title = "仅更新时使用")
+    private Long id;
+
+    @Schema(description = "认领人(跟进)id")
+    private Long claimUserId;
+
+    @Schema(description = "审核人id")
+    @Size(max = 255, message = "【审核人id】最长255位")
+    private String claimUserName;
+
+    @Schema(description = "认领人员所在的项目组id")
+    private Long claimUserGroupId;
+
+    @Schema(description = "跟进时间")
+    private LocalDateTime claimDatetime;
+
+    @Schema(description = "客户表主id")
+    private Long customerId;
+
+    @Schema(description = "客户名称")
+    private String customerName;
+
+    @Schema(description = "跟进状态id,赢单状态id固定-37,方案演示id固定-33")
+    private Long followId;
+
+    @Schema(description = "跟进状态的名称")
+    @Size(max = 255, message = "【跟进状态的名称】最长255位")
+    private String followName;
+
+    @Schema(description = "联系人id(customer_person表主id)")
+    private Long customerPersonId;
+
+    @Schema(description = "联系人名称")
+    @Size(max = 255, message = "【联系人名称】最长255位")
+    private String customerPersonName;
+
+    @Schema(description = "职务id(param_base_data表主id)")
+    private Long positionId;
+
+    @Schema(description = "职务名称")
+    @Size(max = 255, message = "【职务名称】最长255位")
+    private String positionName;
+
+    @Schema(description = "跟进内容")
+    @Size(max = 5000, message = "【跟进内容】最长5000位")
+    private String followContent;
+
+    @Schema(description = "产品id(param_base_data表主id)")
+    private Long productId;
+
+    @Schema(description = "产品名称")
+    @Size(max = 255, message = "【产品名称】最长255位")
+    private String productName;
+
+    @Schema(description = "审核状态:0-待审核,1-已通过,2-已驳回")
+    private Integer auditStatus;
+
+    @Schema(description = "审核人id")
+    private Long auditUserId;
+
+    @Schema(description = "审核人名称")
+    @Size(max = 255, message = "【审核人名称】最长255位")
+    private String auditUserName;
+
+}

+ 91 - 0
src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerFollowAuditListDTO.java

@@ -0,0 +1,91 @@
+package com.jtzx.crm.module.sys.model.request;
+
+import cc.uncarbon.framework.core.constant.HelioConstant;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.io.Serializable;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+
+/**
+ * 后台管理-分页列表赢单跟进审核信息 DTO
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class AdminSysCustomerFollowAuditListDTO implements Serializable {
+
+    @Schema(description = "认领人(跟进)id")
+    private Long claimUserId;
+
+    @Schema(description = "审核人id(关键词)")
+    private String claimUserName;
+
+    @Schema(description = "认领人员所在的项目组id")
+    private Long claimUserGroupId;
+
+    @Schema(description = "跟进时间")
+    private LocalDateTime claimDatetime;
+
+    @Schema(description = "客户表主id")
+    private Long customerId;
+
+    @Schema(description = "客户名称(关键词)")
+    private String customerName;
+
+    @Schema(description = "跟进状态id,赢单状态id固定-37,方案演示id固定-33")
+    private Long followId;
+
+    @Schema(description = "跟进状态的名称(关键词)")
+    private String followName;
+
+    @Schema(description = "联系人id(customer_person表主id)")
+    private Long customerPersonId;
+
+    @Schema(description = "联系人名称(关键词)")
+    private String customerPersonName;
+
+    @Schema(description = "职务id(param_base_data表主id)")
+    private Long positionId;
+
+    @Schema(description = "职务名称(关键词)")
+    private String positionName;
+
+    @Schema(description = "跟进内容(关键词)")
+    private String followContent;
+
+    @Schema(description = "产品id(param_base_data表主id)")
+    private Long productId;
+
+    @Schema(description = "产品名称(关键词)")
+    private String productName;
+
+    @Schema(description = "审核状态:0-待审核,1-已通过,2-已驳回")
+    private Integer auditStatus;
+
+    @Schema(description = "审核人id")
+    private Long auditUserId;
+
+    @Schema(description = "审核人名称(关键词)")
+    private String auditUserName;
+
+    @Schema(description = "时间区间起")
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime beginAt;
+
+    @Schema(description = "时间区间止")
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime endAt;
+
+}

+ 97 - 0
src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerFollowInsertOrUpdateDTO.java

@@ -0,0 +1,97 @@
+package com.jtzx.crm.module.sys.model.request;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import java.io.Serializable;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+
+/**
+ * 后台管理-新增/编辑客户跟进数据信息 DTO
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class AdminSysCustomerFollowInsertOrUpdateDTO implements Serializable {
+
+    @Schema(description = "主键ID", hidden = true, title = "仅更新时使用")
+    private Long id;
+
+    @Schema(description = "认领人(跟进)id")
+    private Long claimUserId;
+
+    @Schema(description = "认领人(跟进)名称")
+    @Size(max = 255, message = "【认领人(跟进)名称】最长255位")
+    private String claimUserName;
+
+    @Schema(description = "认领人员所在的项目组id")
+    private Long claimUserGroupId;
+
+    @Schema(description = "跟进时间")
+    private LocalDateTime claimDatetime;
+
+    @Schema(description = "客户表主id")
+    private Long customerId;
+
+    @Schema(description = "客户名称")
+    private String customerName;
+
+    @Schema(description = "产品id(param_base_data表主id)")
+    private Long productId;
+
+    @Schema(description = "产品名称")
+    @Size(max = 255, message = "【产品名称】最长255位")
+    private String productName;
+
+    @Schema(description = "跟进状态id,赢单状态id固定-37,方案演示id固定-33")
+    private Long followId;
+
+    @Schema(description = "跟进状态的名称")
+    @Size(max = 255, message = "【跟进状态的名称】最长255位")
+    private String followName;
+
+    @Schema(description = "是否赢单,0-否,1-是")
+    private Integer successStatus;
+
+    @Schema(description = "联系人id(customer_person表主id)")
+    private Long customerPersonId;
+
+    @Schema(description = "联系人名称")
+    @Size(max = 255, message = "【联系人名称】最长255位")
+    private String customerPersonName;
+
+    @Schema(description = "职务id(param_base_data表主id)")
+    private Long positionId;
+
+    @Schema(description = "职务名称")
+    @Size(max = 255, message = "【职务名称】最长255位")
+    private String positionName;
+
+    @Schema(description = "跟进内容")
+    @Size(max = 5000, message = "【跟进内容】最长5000位")
+    private String followContent;
+
+    @Schema(description = "预约待办类型,0-预约,1-待办,(follow_id=33时为预约,其它为待办),这个数据可以为null")
+    private Integer orderType;
+
+    @Schema(description = "待办时间")
+    private LocalDateTime orderDatetime;
+
+    @Schema(description = "预约,待办取消状态,0-未取消,1-已取消(预约的取消需要填写取消原因,待办的取消是一个开关,不需要填写原因)")
+    private Integer orderStatus;
+
+    @Schema(description = "预约取消原因")
+    @Size(max = 5000, message = "【预约取消原因】最长5000位")
+    private String cancelRemark;
+
+}

+ 97 - 0
src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerFollowListDTO.java

@@ -0,0 +1,97 @@
+package com.jtzx.crm.module.sys.model.request;
+
+import cc.uncarbon.framework.core.constant.HelioConstant;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.io.Serializable;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+
+/**
+ * 后台管理-分页列表客户跟进数据信息 DTO
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class AdminSysCustomerFollowListDTO implements Serializable {
+
+    @Schema(description = "认领人(跟进)id")
+    private Long claimUserId;
+
+    @Schema(description = "认领人(跟进)名称(关键词)")
+    private String claimUserName;
+
+    @Schema(description = "认领人员所在的项目组id")
+    private Long claimUserGroupId;
+
+    @Schema(description = "跟进时间")
+    private LocalDateTime claimDatetime;
+
+    @Schema(description = "客户表主id")
+    private Long customerId;
+
+    @Schema(description = "客户名称(关键词)")
+    private String customerName;
+
+    @Schema(description = "产品id(param_base_data表主id)")
+    private Long productId;
+
+    @Schema(description = "产品名称(关键词)")
+    private String productName;
+
+    @Schema(description = "跟进状态id,赢单状态id固定-37,方案演示id固定-33")
+    private Long followId;
+
+    @Schema(description = "跟进状态的名称(关键词)")
+    private String followName;
+
+    @Schema(description = "是否赢单,0-否,1-是")
+    private Integer successStatus;
+
+    @Schema(description = "联系人id(customer_person表主id)")
+    private Long customerPersonId;
+
+    @Schema(description = "联系人名称(关键词)")
+    private String customerPersonName;
+
+    @Schema(description = "职务id(param_base_data表主id)")
+    private Long positionId;
+
+    @Schema(description = "职务名称(关键词)")
+    private String positionName;
+
+    @Schema(description = "跟进内容(关键词)")
+    private String followContent;
+
+    @Schema(description = "预约待办类型,0-预约,1-待办,(follow_id=33时为预约,其它为待办),这个数据可以为null")
+    private Integer orderType;
+
+    @Schema(description = "待办时间")
+    private LocalDateTime orderDatetime;
+
+    @Schema(description = "预约,待办取消状态,0-未取消,1-已取消(预约的取消需要填写取消原因,待办的取消是一个开关,不需要填写原因)")
+    private Integer orderStatus;
+
+    @Schema(description = "预约取消原因(关键词)")
+    private String cancelRemark;
+
+    @Schema(description = "时间区间起")
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime beginAt;
+
+    @Schema(description = "时间区间止")
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime endAt;
+
+}

+ 75 - 0
src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerOperLogInsertOrUpdateDTO.java

@@ -0,0 +1,75 @@
+package com.jtzx.crm.module.sys.model.request;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.Size;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+
+/**
+ * 后台管理-新增/编辑客户操作日志信息数据 DTO
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class AdminSysCustomerOperLogInsertOrUpdateDTO implements Serializable {
+
+    @Schema(description = "主键ID", hidden = true, title = "仅更新时使用")
+    private Long id;
+
+    @Schema(description = "客户表主id")
+    private Long customerId;
+
+    @Schema(description = "客户名称")
+    private String customerName;
+
+    @Schema(description = "操作人id")
+    private Long operUserId;
+
+    @Schema(description = "操作人名称")
+    @Size(max = 255, message = "【操作人名称】最长255位")
+    private String operName;
+
+    @Schema(description = "操作类型,0-认领,1-跟进,2-转让,3-转到公海,11-新增,12-修改,13-删除")
+    private Integer operAction;
+
+    @Schema(description = "操作时间")
+    private LocalDateTime operDatetime;
+
+    @Schema(description = "操作人员所在的项目组id")
+    private Long operUserGroupId;
+
+    @Schema(description = "省份id(省份表主id)")
+    private Long provinceId;
+
+    @Schema(description = "省份名称")
+    @Size(max = 255, message = "【省份名称】最长255位")
+    private String provinceName;
+
+    @Schema(description = "城市id(城市表主id)")
+    private Long cityId;
+
+    @Schema(description = "城市名称")
+    @Size(max = 255, message = "【城市名称】最长255位")
+    private String cityName;
+
+    @Schema(description = "跟进状态id,赢单状态id固定-37,方案演示id固定-33")
+    private Long followId;
+
+    @Schema(description = "跟进状态的名称")
+    @Size(max = 255, message = "【跟进状态的名称】最长255位")
+    private String followName;
+
+    @Schema(description = "操作内容")
+    @Size(max = 5000, message = "【操作内容】最长5000位")
+    private String content;
+
+}

+ 79 - 0
src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerOperLogListDTO.java

@@ -0,0 +1,79 @@
+package com.jtzx.crm.module.sys.model.request;
+
+import cc.uncarbon.framework.core.constant.HelioConstant;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+
+/**
+ * 后台管理-分页列表客户操作日志信息数据 DTO
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class AdminSysCustomerOperLogListDTO implements Serializable {
+
+    @Schema(description = "客户表主id")
+    private Long customerId;
+
+    @Schema(description = "客户名称(关键词)")
+    private String customerName;
+
+    @Schema(description = "操作人id")
+    private Long operUserId;
+
+    @Schema(description = "操作人名称(关键词)")
+    private String operName;
+
+    @Schema(description = "操作类型,0-认领,1-跟进,2-转让,3-转到公海,11-新增,12-修改,13-删除")
+    private Integer operAction;
+
+    @Schema(description = "操作时间")
+    private LocalDateTime operDatetime;
+
+    @Schema(description = "操作人员所在的项目组id")
+    private Long operUserGroupId;
+
+    @Schema(description = "省份id(省份表主id)")
+    private Long provinceId;
+
+    @Schema(description = "省份名称(关键词)")
+    private String provinceName;
+
+    @Schema(description = "城市id(城市表主id)")
+    private Long cityId;
+
+    @Schema(description = "城市名称(关键词)")
+    private String cityName;
+
+    @Schema(description = "跟进状态id,赢单状态id固定-37,方案演示id固定-33")
+    private Long followId;
+
+    @Schema(description = "跟进状态的名称(关键词)")
+    private String followName;
+
+    @Schema(description = "操作内容")
+    private String content;
+
+    @Schema(description = "时间区间起")
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime beginAt;
+
+    @Schema(description = "时间区间止")
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime endAt;
+
+}

+ 56 - 0
src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerPersonInsertOrUpdateDTO.java

@@ -0,0 +1,56 @@
+package com.jtzx.crm.module.sys.model.request;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import java.io.Serializable;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+
+/**
+ * 后台管理-新增/编辑客户(学校)联系人数据 DTO
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class AdminSysCustomerPersonInsertOrUpdateDTO implements Serializable {
+
+    @Schema(description = "主键ID", hidden = true, title = "仅更新时使用")
+    private Long id;
+
+    @Schema(description = "客户表主id")
+    private Long customerId;
+
+    @Schema(description = "姓名")
+    @Size(max = 255, message = "【姓名】最长255位")
+    private String name;
+
+    @Schema(description = "职务id(param_base_data表主id)")
+    private Long positionId;
+
+    @Schema(description = "职务名称")
+    @Size(max = 255, message = "【职务名称】最长255位")
+    private String positionName;
+
+    @Schema(description = "手机号")
+    @Size(max = 12, message = "【手机号】最长12位")
+    private String phoneNo;
+
+    @Schema(description = "办公电话")
+    @Size(max = 12, message = "【办公电话】最长12位")
+    private String officePhoneNo;
+
+    @Schema(description = "备注")
+    @Size(max = 2000, message = "【备注】最长2000位")
+    private String remark;
+
+}

+ 58 - 0
src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerPersonListDTO.java

@@ -0,0 +1,58 @@
+package com.jtzx.crm.module.sys.model.request;
+
+import cc.uncarbon.framework.core.constant.HelioConstant;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.io.Serializable;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+
+/**
+ * 后台管理-分页列表客户(学校)联系人数据 DTO
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class AdminSysCustomerPersonListDTO implements Serializable {
+
+    @Schema(description = "客户表主id")
+    private Long customerId;
+
+    @Schema(description = "姓名(关键词)")
+    private String name;
+
+    @Schema(description = "职务id(param_base_data表主id)")
+    private Long positionId;
+
+    @Schema(description = "职务名称(关键词)")
+    private String positionName;
+
+    @Schema(description = "手机号(关键词)")
+    private String phoneNo;
+
+    @Schema(description = "办公电话(关键词)")
+    private String officePhoneNo;
+
+    @Schema(description = "备注(关键词)")
+    private String remark;
+
+    @Schema(description = "时间区间起")
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime beginAt;
+
+    @Schema(description = "时间区间止")
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime endAt;
+
+}

+ 47 - 0
src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerTodoInsertOrUpdateDTO.java

@@ -0,0 +1,47 @@
+package com.jtzx.crm.module.sys.model.request;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import java.io.Serializable;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+
+/**
+ * 后台管理-新增/编辑自定义待办信息数据 DTO
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class AdminSysCustomerTodoInsertOrUpdateDTO implements Serializable {
+
+    @Schema(description = "主键ID", hidden = true, title = "仅更新时使用")
+    private Long id;
+
+    @Schema(description = "认领人(跟进)id")
+    private Long claimUserId;
+
+    @Schema(description = "认领人(跟进)名称")
+    @Size(max = 255, message = "【认领人(跟进)名称】最长255位")
+    private String claimUserName;
+
+    @Schema(description = "认领人员所在的项目组id")
+    private Long claimUserGroupId;
+
+    @Schema(description = "跟进(待办)内容")
+    @Size(max = 5000, message = "【跟进(待办)内容】最长5000位")
+    private String followContent;
+
+    @Schema(description = "待办时间")
+    private LocalDateTime orderDatetime;
+
+}

+ 52 - 0
src/main/java/com/jtzx/crm/module/sys/model/request/AdminSysCustomerTodoListDTO.java

@@ -0,0 +1,52 @@
+package com.jtzx.crm.module.sys.model.request;
+
+import cc.uncarbon.framework.core.constant.HelioConstant;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.io.Serializable;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+
+/**
+ * 后台管理-分页列表自定义待办信息数据 DTO
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class AdminSysCustomerTodoListDTO implements Serializable {
+
+    @Schema(description = "认领人(跟进)id")
+    private Long claimUserId;
+
+    @Schema(description = "认领人(跟进)名称(关键词)")
+    private String claimUserName;
+
+    @Schema(description = "认领人员所在的项目组id")
+    private Long claimUserGroupId;
+
+    @Schema(description = "跟进(待办)内容(关键词)")
+    private String followContent;
+
+    @Schema(description = "待办时间")
+    private LocalDateTime orderDatetime;
+
+    @Schema(description = "时间区间起")
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime beginAt;
+
+    @Schema(description = "时间区间止")
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime endAt;
+
+}

+ 43 - 0
src/main/java/com/jtzx/crm/module/sys/model/response/BindUserBO.java

@@ -0,0 +1,43 @@
+package com.jtzx.crm.module.sys.model.response;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+
+import java.io.Serializable;
+
+
+/**
+ * 小组绑定的成员信息
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class BindUserBO implements Serializable {
+
+    @Schema(description = "主键ID")
+    private Long id;
+
+    @Schema(description = "省份id")
+    private Long provinceId;
+
+    @Schema(description = "城市id")
+    private Long cityId;
+
+    @Schema(description = "groupID")
+    private Long groupId;
+
+    @Schema(description = "姓名")
+    private String username;
+
+    @Schema(description = "排序")
+    private Integer sortCode;
+
+    @Schema(description = "是否选中,true-是,false-否")
+    private Boolean selectStatus;
+}

+ 47 - 0
src/main/java/com/jtzx/crm/module/sys/model/response/DateSelectBO.java

@@ -0,0 +1,47 @@
+package com.jtzx.crm.module.sys.model.response;
+
+import cc.uncarbon.framework.core.constant.HelioConstant;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+
+/**
+ * 全国省省份数据 BO
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class DateSelectBO implements Serializable {
+
+
+    @Schema(description = "名称")
+    private String name;
+
+    @Schema(description = "排序")
+    private Integer sortCode;
+
+    @Schema(description = "是否选中,true-是,false-否")
+    private Boolean selectStatus;
+
+    @Schema(description = "开始时间")
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime startDate;
+
+    @Schema(description = "结束时间")
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime endDate;
+
+}

+ 39 - 0
src/main/java/com/jtzx/crm/module/sys/model/response/SelectBO.java

@@ -0,0 +1,39 @@
+package com.jtzx.crm.module.sys.model.response;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+
+import java.io.Serializable;
+import java.util.List;
+
+
+/**
+ * 全国省省份数据 BO
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class SelectBO implements Serializable {
+
+    @Schema(description = "主键ID")
+    private Long id;
+
+    @Schema(description = "名称")
+    private String name;
+
+    @Schema(description = "排序")
+    private Integer sortCode;
+
+    @Schema(description = "是否选中,true-是,false-否")
+    private Boolean selectStatus;
+
+    @Schema(description = "子数据")
+    private List<SelectBO> subData;
+
+}

+ 97 - 0
src/main/java/com/jtzx/crm/module/sys/model/response/SysCustomerDataBO.java

@@ -0,0 +1,97 @@
+package com.jtzx.crm.module.sys.model.response;
+
+import cc.uncarbon.framework.core.constant.HelioConstant;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+
+/**
+ * 客户(学校)数据 BO
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class SysCustomerDataBO implements Serializable {
+
+    @Schema(description = "主键ID")
+    private Long id;
+
+    @Schema(description = "创建时刻")
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime createdAt;
+
+    @Schema(description = "更新时刻")
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime updatedAt;
+
+    @Schema(description = "名称")
+    private String name;
+
+    @Schema(description = "学段id(param_base_data表主id)")
+    private Long stageId;
+
+    @Schema(description = "学段名称")
+    private String stageName;
+
+    @Schema(description = "省份id(省份表主id)")
+    private Long provinceId;
+
+    @Schema(description = "省份名称")
+    private String provinceName;
+
+    @Schema(description = "城市id(城市表主id)")
+    private Long cityId;
+
+    @Schema(description = "城市名称")
+    private String cityName;
+
+    @Schema(description = "地址")
+    private String address;
+
+    @Schema(description = "放入公海时间")
+    private LocalDateTime resetDatetime;
+
+    @Schema(description = "认领人id")
+    private Long claimUserId;
+
+    @Schema(description = "认领人名称")
+    private String claimUserName;
+
+    @Schema(description = "认领状态,0-否,1-是")
+    private Integer claimStatus;
+
+    @Schema(description = "认领时间")
+    private LocalDateTime claimDatetime;
+
+    @Schema(description = "认领人员所在的项目组id")
+    private Long claimUserGroupId;
+
+    @Schema(description = "跟进状态id,赢单状态id固定-37,方案演示id固定-33")
+    private Long followId;
+
+    @Schema(description = "跟进状态的名称")
+    private String followName;
+
+    @Schema(description = "赢单人id(不为null时为赢单人id)")
+    private Long successUserId;
+
+    @Schema(description = "赢单人名称(不为null时为赢单人名称)")
+    private String successUserName;
+
+    @Schema(description = "是否赢单,0-否,1-是")
+    private Integer successStatus;
+
+}

+ 60 - 0
src/main/java/com/jtzx/crm/module/sys/model/response/SysCustomerDataPrivateBO.java

@@ -0,0 +1,60 @@
+package com.jtzx.crm.module.sys.model.response;
+
+import cc.uncarbon.framework.core.constant.HelioConstant;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+
+/**
+ * 客户(学校)数据 BO
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class SysCustomerDataPrivateBO implements Serializable {
+
+    @Schema(description = "主键ID")
+    private Long id;
+
+    @Schema(description = "客户类型名称")
+    private String typeName;
+
+    @Schema(description = "名称")
+    private String name;
+
+    @Schema(description = "学段名称")
+    private String stageName;
+
+    @Schema(description = "省份名称")
+    private String provinceName;
+
+    @Schema(description = "城市名称")
+    private String cityName;
+
+    @Schema(description = "小组名称")
+    private String groupName;
+
+    @Schema(description = "认领人名称")
+    private String claimUserName;
+
+    @Schema(description = "认领时间")
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_FORMAT)
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_FORMAT)
+    private LocalDateTime claimDatetime;
+
+    @Schema(description = "上次跟进时间")
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_FORMAT)
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_FORMAT)
+    private LocalDateTime lastFollowDatetime;
+}

+ 52 - 0
src/main/java/com/jtzx/crm/module/sys/model/response/SysCustomerDataPublicBO.java

@@ -0,0 +1,52 @@
+package com.jtzx.crm.module.sys.model.response;
+
+import cc.uncarbon.framework.core.constant.HelioConstant;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+
+/**
+ * 客户(学校)数据 BO
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class SysCustomerDataPublicBO implements Serializable {
+
+    @Schema(description = "主键ID")
+    private Long id;
+
+    @Schema(description = "名称")
+    private String name;
+
+    @Schema(description = "学段名称")
+    private String stageName;
+
+    @Schema(description = "省份名称")
+    private String provinceName;
+
+    @Schema(description = "城市名称")
+    private String cityName;
+
+    @Schema(description = "放入公海时间")
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_FORMAT)
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_FORMAT)
+    private LocalDateTime resetDatetime;
+
+    @Schema(description = "闲置天数")
+    private String unUsedDayCount;
+
+    @Schema(description = "是否可删除,true-是,false-否")
+    private Boolean delStatus;
+}

+ 94 - 0
src/main/java/com/jtzx/crm/module/sys/model/response/SysCustomerFollowAuditBO.java

@@ -0,0 +1,94 @@
+package com.jtzx.crm.module.sys.model.response;
+
+import cc.uncarbon.framework.core.constant.HelioConstant;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.io.Serializable;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+
+/**
+ * 赢单跟进审核信息 BO
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class SysCustomerFollowAuditBO implements Serializable {
+
+    @Schema(description = "主键ID")
+    private Long id;
+
+    @Schema(description = "创建时刻")
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime createdAt;
+
+    @Schema(description = "更新时刻")
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime updatedAt;
+
+    @Schema(description = "认领人(跟进)id")
+    private Long claimUserId;
+
+    @Schema(description = "审核人id")
+    private String claimUserName;
+
+    @Schema(description = "认领人员所在的项目组id")
+    private Long claimUserGroupId;
+
+    @Schema(description = "跟进时间")
+    private LocalDateTime claimDatetime;
+
+    @Schema(description = "客户表主id")
+    private Long customerId;
+
+    @Schema(description = "客户名称")
+    private String customerName;
+
+    @Schema(description = "跟进状态id,赢单状态id固定-37,方案演示id固定-33")
+    private Long followId;
+
+    @Schema(description = "跟进状态的名称")
+    private String followName;
+
+    @Schema(description = "联系人id(customer_person表主id)")
+    private Long customerPersonId;
+
+    @Schema(description = "联系人名称")
+    private String customerPersonName;
+
+    @Schema(description = "职务id(param_base_data表主id)")
+    private Long positionId;
+
+    @Schema(description = "职务名称")
+    private String positionName;
+
+    @Schema(description = "跟进内容")
+    private String followContent;
+
+    @Schema(description = "产品id(param_base_data表主id)")
+    private Long productId;
+
+    @Schema(description = "产品名称")
+    private String productName;
+
+    @Schema(description = "审核状态:0-待审核,1-已通过,2-已驳回")
+    private Integer auditStatus;
+
+    @Schema(description = "审核人id")
+    private Long auditUserId;
+
+    @Schema(description = "审核人名称")
+    private String auditUserName;
+
+}

+ 105 - 0
src/main/java/com/jtzx/crm/module/sys/model/response/SysCustomerFollowBO.java

@@ -0,0 +1,105 @@
+package com.jtzx.crm.module.sys.model.response;
+
+import cc.uncarbon.framework.core.constant.HelioConstant;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+
+/**
+ * 客户跟进数据信息 BO
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class SysCustomerFollowBO implements Serializable {
+
+    @Schema(description = "主键ID")
+    private Long id;
+
+    @Schema(description = "创建时刻")
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime createdAt;
+
+    @Schema(description = "更新时刻")
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime updatedAt;
+
+    @Schema(description = "认领人(跟进)id")
+    private Long claimUserId;
+
+    @Schema(description = "认领人(跟进)名称")
+    private String claimUserName;
+
+    @Schema(description = "认领人员所在的项目组id")
+    private Long claimUserGroupId;
+
+    @Schema(description = "跟进时间")
+    private LocalDateTime claimDatetime;
+
+    @Schema(description = "客户表主id")
+    private Long customerId;
+
+    @Schema(description = "客户名称")
+    private String customerName;
+
+    @Schema(description = "产品id(param_base_data表主id)")
+    private Long productId;
+
+    @Schema(description = "产品名称")
+    private String productName;
+
+    @Schema(description = "跟进状态id,赢单状态id固定-37,方案演示id固定-33")
+    private Long followId;
+
+    @Schema(description = "跟进状态的名称")
+    private String followName;
+
+    @Schema(description = "是否赢单,0-否,1-是")
+    private Integer successStatus;
+
+    @Schema(description = "联系人id(customer_person表主id)")
+    private Long customerPersonId;
+
+    @Schema(description = "联系人名称")
+    private String customerPersonName;
+
+    @Schema(description = "职务id(param_base_data表主id)")
+    private Long positionId;
+
+    @Schema(description = "职务名称")
+    private String positionName;
+
+    @Schema(description = "跟进内容")
+    private String followContent;
+
+    @Schema(description = "预约待办类型,0-预约,1-待办,(follow_id=33时为预约,其它为待办),这个数据可以为null")
+    private Integer orderType;
+
+    @Schema(description = "待办时间")
+    private LocalDateTime orderDatetime;
+
+    @Schema(description = "预约,待办取消状态,0-未取消,1-已取消(预约的取消需要填写取消原因,待办的取消是一个开关,不需要填写原因)")
+    private Integer orderStatus;
+
+    @Schema(description = "预约取消原因")
+    private String cancelRemark;
+
+    @Schema(description = "学段id(param_base_data表主id)")
+    private Long stageId;
+
+    @Schema(description = "学段名称")
+    private String stageName;
+}

+ 70 - 0
src/main/java/com/jtzx/crm/module/sys/model/response/SysCustomerFollowListBO.java

@@ -0,0 +1,70 @@
+package com.jtzx.crm.module.sys.model.response;
+
+import cc.uncarbon.framework.core.constant.HelioConstant;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+
+/**
+ * 客户跟进数据信息 BO
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class SysCustomerFollowListBO implements Serializable {
+
+    @Schema(description = "主键ID")
+    private Long id;
+
+    @Schema(description = "客户类型名称")
+    private String typeName;
+
+    @Schema(description = "客户名称")
+    private String customerName;
+
+    @Schema(description = "联系人名称")
+    private String customerPersonName;
+
+    @Schema(description = "职务名称")
+    private String positionName;
+
+    @Schema(description = "学段名称")
+    private String stageName;
+
+    @Schema(description = "省份名称")
+    private String provinceName;
+
+    @Schema(description = "城市名称")
+    private String cityName;
+
+    @Schema(description = "小组名称")
+    private String groupName;
+
+    @Schema(description = "认领人(跟进)名称")
+    private String claimUserName;
+
+    @Schema(description = "产品名称")
+    private String productName;
+
+    @Schema(description = "跟进状态的名称")
+    private String followName;
+
+    @Schema(description = "跟进内容")
+    private String followContent;
+
+    @Schema(description = "跟进时间")
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_FORMAT)
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_FORMAT)
+    private LocalDateTime claimDatetime;
+}

+ 47 - 0
src/main/java/com/jtzx/crm/module/sys/model/response/SysCustomerFollowSelectBO.java

@@ -0,0 +1,47 @@
+package com.jtzx.crm.module.sys.model.response;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+
+import java.io.Serializable;
+import java.util.List;
+
+
+/**
+ * 我的跟进页面顶部筛选条件数据对象
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class SysCustomerFollowSelectBO implements Serializable {
+
+
+    @Schema(description = "时间数据")
+    private List<DateSelectBO> dateData;
+
+    @Schema(description = "产品数据")
+    private List<SelectBO> productData;
+
+//    @Schema(description = "类型数据")
+//    private List<SelectBO> typeData;
+
+    @Schema(description = "学段数据")
+    private List<SelectBO> stageData;
+
+    @Schema(description = "省市数据")
+    private List<SelectBO> regionData;
+
+    @Schema(description = "分组数据")
+    private List<SelectBO> groupData;
+
+    @Schema(description = "成员数据")
+    private List<BindUserBO> memberData;
+
+
+}

+ 82 - 0
src/main/java/com/jtzx/crm/module/sys/model/response/SysCustomerOperLogBO.java

@@ -0,0 +1,82 @@
+package com.jtzx.crm.module.sys.model.response;
+
+import cc.uncarbon.framework.core.constant.HelioConstant;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+
+/**
+ * 客户操作日志信息数据 BO
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class SysCustomerOperLogBO implements Serializable {
+
+    @Schema(description = "主键ID")
+    private Long id;
+
+    @Schema(description = "创建时刻")
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime createdAt;
+
+    @Schema(description = "更新时刻")
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime updatedAt;
+
+    @Schema(description = "客户表主id")
+    private Long customerId;
+
+    @Schema(description = "客户名称")
+    private String customerName;
+
+    @Schema(description = "操作人id")
+    private Long operUserId;
+
+    @Schema(description = "操作人名称")
+    private String operName;
+
+    @Schema(description = "操作类型,0-认领,1-跟进,2-转让,3-转到公海,11-新增,12-修改,13-删除")
+    private Integer operAction;
+
+    @Schema(description = "操作时间")
+    private LocalDateTime operDatetime;
+
+    @Schema(description = "操作人员所在的项目组id")
+    private Long operUserGroupId;
+
+    @Schema(description = "省份id(省份表主id)")
+    private Long provinceId;
+
+    @Schema(description = "省份名称")
+    private String provinceName;
+
+    @Schema(description = "城市id(城市表主id)")
+    private Long cityId;
+
+    @Schema(description = "城市名称")
+    private String cityName;
+
+    @Schema(description = "跟进状态id,赢单状态id固定-37,方案演示id固定-33")
+    private Long followId;
+
+    @Schema(description = "跟进状态的名称")
+    private String followName;
+
+    @Schema(description = "操作内容")
+    private String content;
+
+}

+ 61 - 0
src/main/java/com/jtzx/crm/module/sys/model/response/SysCustomerPersonBO.java

@@ -0,0 +1,61 @@
+package com.jtzx.crm.module.sys.model.response;
+
+import cc.uncarbon.framework.core.constant.HelioConstant;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.io.Serializable;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+
+/**
+ * 客户(学校)联系人数据 BO
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class SysCustomerPersonBO implements Serializable {
+
+    @Schema(description = "主键ID")
+    private Long id;
+
+    @Schema(description = "创建时刻")
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime createdAt;
+
+    @Schema(description = "更新时刻")
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime updatedAt;
+
+    @Schema(description = "客户表主id")
+    private Long customerId;
+
+    @Schema(description = "姓名")
+    private String name;
+
+    @Schema(description = "职务id(param_base_data表主id)")
+    private Long positionId;
+
+    @Schema(description = "职务名称")
+    private String positionName;
+
+    @Schema(description = "手机号")
+    private String phoneNo;
+
+    @Schema(description = "办公电话")
+    private String officePhoneNo;
+
+    @Schema(description = "备注")
+    private String remark;
+
+}

+ 40 - 0
src/main/java/com/jtzx/crm/module/sys/model/response/SysCustomerPrivateSelectBO.java

@@ -0,0 +1,40 @@
+package com.jtzx.crm.module.sys.model.response;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+
+import java.io.Serializable;
+import java.util.List;
+
+
+/**
+ * 私域客户页面顶部筛选条件数据对象
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class SysCustomerPrivateSelectBO implements Serializable {
+
+    @Schema(description = "类型数据")
+    private List<SelectBO> typeData;
+
+    @Schema(description = "学段数据")
+    private List<SelectBO> stageData;
+
+    @Schema(description = "省市数据")
+    private List<SelectBO> regionData;
+
+    @Schema(description = "分组数据")
+    private List<SelectBO> groupData;
+
+    @Schema(description = "成员数据")
+    private List<BindUserBO> memberData;
+
+
+}

+ 29 - 0
src/main/java/com/jtzx/crm/module/sys/model/response/SysCustomerPublicSelectBO.java

@@ -0,0 +1,29 @@
+package com.jtzx.crm.module.sys.model.response;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+
+import java.io.Serializable;
+import java.util.List;
+
+
+/**
+ * 私域客户页面顶部筛选条件数据对象
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class SysCustomerPublicSelectBO implements Serializable {
+
+    @Schema(description = "学段数据")
+    private List<SelectBO> stageData;
+
+    @Schema(description = "省市数据")
+    private List<SelectBO> regionData;
+}

+ 40 - 0
src/main/java/com/jtzx/crm/module/sys/model/response/SysCustomerSelectBO.java

@@ -0,0 +1,40 @@
+package com.jtzx.crm.module.sys.model.response;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+
+import java.io.Serializable;
+import java.util.List;
+
+
+/**
+ * 私域客户页面顶部筛选条件数据对象
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class SysCustomerSelectBO implements Serializable {
+
+    @Schema(description = "类型数据")
+    private List<SelectBO> typeData;
+
+    @Schema(description = "学段数据")
+    private List<SelectBO> stageData;
+
+    @Schema(description = "省市数据")
+    private List<SelectBO> regionData;
+
+    @Schema(description = "分组数据")
+    private List<SelectBO> groupData;
+
+    @Schema(description = "成员数据")
+    private List<BindUserBO> memberData;
+
+
+}

+ 55 - 0
src/main/java/com/jtzx/crm/module/sys/model/response/SysCustomerTodoBO.java

@@ -0,0 +1,55 @@
+package com.jtzx.crm.module.sys.model.response;
+
+import cc.uncarbon.framework.core.constant.HelioConstant;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.io.Serializable;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+
+/**
+ * 自定义待办信息数据 BO
+ */
+@Accessors(chain = true)
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class SysCustomerTodoBO implements Serializable {
+
+    @Schema(description = "主键ID")
+    private Long id;
+
+    @Schema(description = "创建时刻")
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime createdAt;
+
+    @Schema(description = "更新时刻")
+    @DateTimeFormat(pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HelioConstant.Jackson.DATE_TIME_FORMAT)
+    private LocalDateTime updatedAt;
+
+    @Schema(description = "认领人(跟进)id")
+    private Long claimUserId;
+
+    @Schema(description = "认领人(跟进)名称")
+    private String claimUserName;
+
+    @Schema(description = "认领人员所在的项目组id")
+    private Long claimUserGroupId;
+
+    @Schema(description = "跟进(待办)内容")
+    private String followContent;
+
+    @Schema(description = "待办时间")
+    private LocalDateTime orderDatetime;
+
+}

+ 244 - 0
src/main/java/com/jtzx/crm/module/sys/service/SysCustomerDataFollowService.java

@@ -0,0 +1,244 @@
+package com.jtzx.crm.module.sys.service;
+
+
+import cc.uncarbon.framework.core.page.PageResult;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.text.CharSequenceUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.google.common.collect.Lists;
+import com.jtzx.crm.module.sys.entity.SysCustomerFollowEntity;
+import com.jtzx.crm.module.sys.entity.SysGroupEntity;
+import com.jtzx.crm.module.sys.entity.SysParamBaseDataEntity;
+import com.jtzx.crm.module.sys.mapper.SysCustomerFollowMapper;
+import com.jtzx.crm.module.sys.mapper.SysGroupMapper;
+import com.jtzx.crm.module.sys.mapper.SysParamBaseDataMapper;
+import com.jtzx.crm.module.sys.model.request.AdminSysCustomerDataFollowListDTO;
+import com.jtzx.crm.module.sys.model.response.BindUserBO;
+import com.jtzx.crm.module.sys.model.response.SysCustomerFollowListBO;
+import com.jtzx.crm.module.sys.model.response.SysCustomerFollowSelectBO;
+import com.jtzx.crm.module.sys.model.response.SysCustomerSelectBO;
+import com.jtzx.crm.module.sys.util.DateSelectUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 客户跟进业务
+ */
+@RequiredArgsConstructor
+@Service
+@Slf4j
+public class SysCustomerDataFollowService {
+
+    private final SysCustomerFollowMapper sysCustomerFollowMapper;
+
+    private final SysGroupMapper sysGroupMapper;
+
+    private final SysParamBaseDataMapper sysParamBaseDataMapper;
+
+
+    private final SysCustomerUserCommonService sysCustomerUserCommonService;
+
+
+    /**
+     * 查询客户跟进页面顶部筛选条件数据
+     * @return
+     */
+    public SysCustomerFollowSelectBO queryHeadSelectPrivateData() {
+
+        SysCustomerFollowSelectBO resultData = SysCustomerFollowSelectBO.builder().build();
+        // 时间
+        resultData.setDateData(DateSelectUtil.getDateData());
+        //类型
+//        resultData.setTypeData(sysCustomerUserCommonService.handleTypeData());
+        //学段信息
+        resultData.setStageData(sysCustomerUserCommonService.handleStageData());
+        //产品信息
+        resultData.setStageData(sysCustomerUserCommonService.handleProductData());
+        //省市信息
+        SysCustomerSelectBO sysCustomerSelectBO = sysCustomerUserCommonService.querySelectDataByType2(true, true);
+        if (sysCustomerSelectBO != null && CollUtil.isNotEmpty(sysCustomerSelectBO.getRegionData())) {
+            resultData.setRegionData(sysCustomerSelectBO.getRegionData());
+            //小组
+            resultData.setGroupData(sysCustomerSelectBO.getGroupData());
+            //人员
+            resultData.setMemberData(sysCustomerSelectBO.getMemberData());
+        }
+        return resultData;
+    }
+
+
+    /**
+     * 后台管理-客户(学校)跟进分页列表
+     */
+    public PageResult<SysCustomerFollowListBO> adminList(AdminSysCustomerDataFollowListDTO dto) {
+
+        List<Long> userIds = Lists.newArrayList();
+        if (dto.getUserId() != null) {
+            userIds.add(dto.getUserId());
+        }
+        List<Long> groupIds = Lists.newArrayList();
+        if (dto.getGroupId() != null) {
+            groupIds.add(dto.getGroupId());
+        }
+        List<Long> cityIds = sysCustomerUserCommonService.queryUserCityIds1(dto.getProvinceId());
+        List<BindUserBO> bindUserBOS = sysCustomerUserCommonService.queryBindUserListByCityId(cityIds);
+        if (CollUtil.isNotEmpty(bindUserBOS)) {
+            if (CollUtil.isEmpty(userIds)) {
+                userIds.addAll(
+                        bindUserBOS.stream()
+                                .map(BindUserBO::getId).toList());
+            }
+            if (CollUtil.isEmpty(groupIds)) {
+                groupIds.addAll(
+                        bindUserBOS.stream()
+                                .map(BindUserBO::getGroupId).distinct().toList());
+            }
+        }
+        if (CollUtil.isEmpty(userIds) || CollUtil.isEmpty(groupIds)) {
+            return new PageResult<SysCustomerFollowListBO>()
+                    .setCurrent(1)
+                    .setSize(dto.getPageSize())
+                    .setTotal(0)
+                    .setRecords(Lists.newArrayList());
+        }
+        Page<SysCustomerFollowEntity> entityPage = sysCustomerFollowMapper.selectPage(
+                new Page<>(dto.getPageNum(), dto.getPageSize()),
+                new QueryWrapper<SysCustomerFollowEntity>()
+                        .lambda()
+                        // 名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getQueryStr()), SysCustomerFollowEntity::getCustomerName, CharSequenceUtil.cleanBlank(dto.getQueryStr()))
+                        //用户判断
+                        .in(SysCustomerFollowEntity::getClaimUserId, userIds)
+                        // 学段id(param_base_data表主id)
+                        .eq(Objects.nonNull(dto.getStageId()), SysCustomerFollowEntity::getStageId, dto.getStageId())
+                        // 省份id(省份表主id)
+                        .eq(Objects.nonNull(dto.getProvinceId()), SysCustomerFollowEntity::getProvinceId, dto.getProvinceId())
+                        // 城市id(不为空时)
+                        .eq(Objects.nonNull(dto.getCityId()), SysCustomerFollowEntity::getCityId, dto.getCityId())
+                        // 城市id(为空时)
+                        .in(Objects.isNull(dto.getCityId()), SysCustomerFollowEntity::getCityId, cityIds)
+                        //小组
+                        .in(SysCustomerFollowEntity::getClaimUserGroupId, groupIds)
+                        //开始时间
+                        .ge(dto.getStartDatetime() != null, SysCustomerFollowEntity::getClaimDatetime, dto.getStartDatetime())
+                        //结束时间
+                        .le(dto.getEndDatetime() != null, SysCustomerFollowEntity::getClaimDatetime, dto.getEndDatetime())
+                        //产品
+                        .eq(dto.getProductId() != null, SysCustomerFollowEntity::getProductId, dto.getProductId())
+                        // 排序
+                        .orderByDesc(SysCustomerFollowEntity::getClaimDatetime)
+        );
+        return this.entityPage2BOPage(entityPage);
+    }
+
+    /**
+     * 实体分页转 BO 分页
+     *
+     * @param entityPage 实体分页
+     * @return BO 分页
+     */
+    private PageResult<SysCustomerFollowListBO> entityPage2BOPage(Page<SysCustomerFollowEntity> entityPage) {
+        return new PageResult<SysCustomerFollowListBO>()
+                .setCurrent(entityPage.getCurrent())
+                .setSize(entityPage.getSize())
+                .setTotal(entityPage.getTotal())
+                .setRecords(this.entityList2BOs(entityPage.getRecords()));
+    }
+
+    /**
+     * 实体 List 转 BO List
+     *
+     * @param entityList 实体 List
+     * @return BO List
+     */
+    private List<SysCustomerFollowListBO> entityList2BOs(List<SysCustomerFollowEntity> entityList) {
+        if (CollUtil.isEmpty(entityList)) {
+            return Collections.emptyList();
+        }
+
+        //查询小组数据
+        List<Long> groupIds =
+                entityList.stream()
+                        .map(SysCustomerFollowEntity::getClaimUserGroupId)
+                        .filter(Objects::nonNull).distinct().toList();
+        List<SysGroupEntity> groupEntityList = sysGroupMapper.selectByIds(groupIds);
+        //查询学段
+        List<Long> stageIds =
+                entityList.stream()
+                        .map(SysCustomerFollowEntity::getStageId)
+                        .filter(Objects::nonNull).distinct().toList();
+        List<SysParamBaseDataEntity> stageDataList = sysParamBaseDataMapper.selectByIds(stageIds);
+        //查询产品
+        List<Long> productIds =
+                entityList.stream()
+                        .map(SysCustomerFollowEntity::getProductId)
+                        .filter(Objects::nonNull).distinct().toList();
+        List<SysParamBaseDataEntity> productDataList = sysParamBaseDataMapper.selectByIds(productIds);
+        //查询跟进状态
+        List<Long> followIds =
+                entityList.stream()
+                        .map(SysCustomerFollowEntity::getFollowId)
+                        .filter(Objects::nonNull).distinct().toList();
+        List<SysParamBaseDataEntity> followDataList = sysParamBaseDataMapper.selectByIds(followIds);
+        // 深拷贝
+        List<SysCustomerFollowListBO> ret = new ArrayList<>(entityList.size());
+        entityList.forEach(
+                entity -> {
+                    SysCustomerFollowListBO bo = this.entity2BO(entity);
+                    if (CollUtil.isNotEmpty(groupEntityList)) {
+                        groupEntityList.stream()
+                                .filter(x -> x.getId().equals(entity.getClaimUserGroupId()))
+                                .findFirst()
+                                .ifPresent(e -> bo.setGroupName(e.getTitle()));
+                    }
+                    if (CollUtil.isNotEmpty(stageDataList)) {
+                        stageDataList.stream()
+                                .filter(x -> x.getId().equals(entity.getStageId()))
+                                .findFirst()
+                                .ifPresent(e -> bo.setStageName(e.getTitle()));
+                    }
+                    if (CollUtil.isNotEmpty(productDataList)) {
+                        productDataList.stream()
+                                .filter(x -> x.getId().equals(entity.getProductId()))
+                                .findFirst()
+                                .ifPresent(e -> bo.setProductName(e.getTitle()));
+                    }
+                    if (CollUtil.isNotEmpty(followDataList)) {
+                        followDataList.stream()
+                                .filter(x -> x.getId().equals(entity.getFollowId()))
+                                .findFirst()
+                                .ifPresent(e -> bo.setFollowName(e.getTitle()));
+                    }
+                    ret.add(bo);
+                }
+        );
+        return ret;
+    }
+
+    /**
+     * 实体转 BO
+     *
+     * @param entity 实体
+     * @return BO
+     */
+    private SysCustomerFollowListBO entity2BO(SysCustomerFollowEntity entity) {
+        if (entity == null) {
+            return null;
+        }
+        SysCustomerFollowListBO bo = new SysCustomerFollowListBO();
+        BeanUtil.copyProperties(entity, bo);
+        // 可以在此处为BO填充字段
+        bo.setClaimUserName(entity.getClaimUserName());
+        bo.setClaimDatetime(entity.getClaimDatetime());
+        return bo;
+    }
+
+}

+ 247 - 0
src/main/java/com/jtzx/crm/module/sys/service/SysCustomerDataPrivateService.java

@@ -0,0 +1,247 @@
+package com.jtzx.crm.module.sys.service;
+
+
+import cc.uncarbon.framework.core.page.PageResult;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.text.CharSequenceUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.google.common.collect.Lists;
+import com.jtzx.crm.module.sys.entity.SysCustomerDataEntity;
+import com.jtzx.crm.module.sys.entity.SysCustomerFollowEntity;
+import com.jtzx.crm.module.sys.entity.SysGroupEntity;
+import com.jtzx.crm.module.sys.entity.SysParamBaseDataEntity;
+import com.jtzx.crm.module.sys.mapper.SysCustomerDataMapper;
+import com.jtzx.crm.module.sys.mapper.SysCustomerFollowMapper;
+import com.jtzx.crm.module.sys.mapper.SysGroupMapper;
+import com.jtzx.crm.module.sys.mapper.SysParamBaseDataMapper;
+import com.jtzx.crm.module.sys.model.request.AdminSysCustomerDataPrivateListDTO;
+import com.jtzx.crm.module.sys.model.response.BindUserBO;
+import com.jtzx.crm.module.sys.model.response.SysCustomerDataPrivateBO;
+import com.jtzx.crm.module.sys.model.response.SysCustomerPrivateSelectBO;
+import com.jtzx.crm.module.sys.model.response.SysCustomerSelectBO;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+
+/**
+ * 我的(私域)客户(学校)数据
+ */
+@RequiredArgsConstructor
+@Service
+@Slf4j
+public class SysCustomerDataPrivateService {
+
+    private final SysCustomerDataMapper sysCustomerDataMapper;
+
+    private final SysGroupMapper sysGroupMapper;
+
+    private final SysParamBaseDataMapper sysParamBaseDataMapper;
+
+    private final SysCustomerFollowMapper sysCustomerFollowMapper;
+
+    private final SysCustomerUserCommonService sysCustomerUserCommonService;
+
+
+    /**
+     * 查询私域(我的)客户页面顶部筛选条件数据
+     * @return
+     */
+    public SysCustomerPrivateSelectBO queryHeadSelectPrivateData() {
+
+        SysCustomerPrivateSelectBO resultData = SysCustomerPrivateSelectBO.builder().build();
+        //类型
+        resultData.setTypeData(sysCustomerUserCommonService.handleTypeData());
+        //学段信息
+        resultData.setStageData(sysCustomerUserCommonService.handleStageData());
+        //省市信息
+        SysCustomerSelectBO sysCustomerSelectBO = sysCustomerUserCommonService.querySelectDataByType1(true, true);
+        if (sysCustomerSelectBO != null) {
+            resultData.setRegionData(sysCustomerSelectBO.getRegionData());
+            resultData.setGroupData(sysCustomerSelectBO.getGroupData());
+            resultData.setMemberData(sysCustomerSelectBO.getMemberData());
+        }
+        return resultData;
+    }
+
+
+    /**
+     * 后台管理-我的客户(学校)分页列表
+     */
+    public PageResult<SysCustomerDataPrivateBO> adminList(AdminSysCustomerDataPrivateListDTO dto) {
+        List<Long> userIds = Lists.newArrayList();
+        if (dto.getUserId() != null) {
+            userIds.add(dto.getUserId());
+        }
+        List<Long> groupIds = Lists.newArrayList();
+        if (dto.getGroupId() != null) {
+            groupIds.add(dto.getGroupId());
+        }
+        List<Long> cityIds = sysCustomerUserCommonService.queryUserCityIds(dto.getProvinceId());
+        List<BindUserBO> bindUserBOS = sysCustomerUserCommonService.queryBindUserListByCityId(cityIds);
+        if (CollUtil.isNotEmpty(bindUserBOS)) {
+            if (CollUtil.isEmpty(userIds)) {
+                userIds.addAll(
+                        bindUserBOS.stream()
+                                .map(BindUserBO::getId).toList());
+            }
+
+            if (CollUtil.isEmpty(groupIds)) {
+                groupIds.addAll(
+                        bindUserBOS.stream()
+                                .map(BindUserBO::getGroupId).distinct().toList());
+            }
+        }
+
+        if (CollUtil.isEmpty(userIds) || CollUtil.isEmpty(groupIds)) {
+            return new PageResult<SysCustomerDataPrivateBO>()
+                    .setCurrent(1)
+                    .setSize(dto.getPageSize())
+                    .setTotal(0)
+                    .setRecords(Lists.newArrayList());
+        }
+
+        Page<SysCustomerDataEntity> entityPage = sysCustomerDataMapper.selectPage(
+                new Page<>(dto.getPageNum(), dto.getPageSize()),
+                new QueryWrapper<SysCustomerDataEntity>()
+                        .lambda()
+                        // 名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getQueryStr()), SysCustomerDataEntity::getName, CharSequenceUtil.cleanBlank(dto.getQueryStr()))
+                        //存量客户判断
+                        .in(dto.getTypeId() != null && dto.getTypeId().equals(2), SysCustomerDataEntity::getSuccessUserId, userIds)
+                        //查询存量客户判断
+                        .eq(dto.getTypeId() != null && dto.getTypeId().equals(2), SysCustomerDataEntity::getSuccessStatus, 1)
+                        //首单客户判断
+                        .in(dto.getTypeId() != null && dto.getTypeId().equals(1), SysCustomerDataEntity::getClaimUserId, userIds)
+                        // 学段id(param_base_data表主id)
+                        .eq(Objects.nonNull(dto.getStageId()), SysCustomerDataEntity::getStageId, dto.getStageId())
+                        // 省份id(省份表主id)
+                        .eq(Objects.nonNull(dto.getProvinceId()), SysCustomerDataEntity::getProvinceId, dto.getProvinceId())
+                        // 城市id(不为空时)
+                        .eq(Objects.nonNull(dto.getCityId()), SysCustomerDataEntity::getCityId, dto.getCityId())
+                        // 城市id(为空时)
+                        .in(Objects.isNull(dto.getCityId()), SysCustomerDataEntity::getCityId, cityIds)
+                        .in(SysCustomerDataEntity::getClaimUserGroupId, groupIds)
+                        //必须是已认领的,不认领的是不能出现在我的客户里面的,所有必须加这个过滤,不管是存量还是首单客户都要加这个参数
+                        .eq(SysCustomerDataEntity::getClaimStatus, 1)
+                        // 排序
+                        .orderByDesc(SysCustomerDataEntity::getClaimDatetime)
+        );
+        return this.entityPage2BOPage(entityPage);
+    }
+
+    /**
+     * 实体分页转 BO 分页
+     *
+     * @param entityPage 实体分页
+     * @return BO 分页
+     */
+    private PageResult<SysCustomerDataPrivateBO> entityPage2BOPage(Page<SysCustomerDataEntity> entityPage) {
+        return new PageResult<SysCustomerDataPrivateBO>()
+                .setCurrent(entityPage.getCurrent())
+                .setSize(entityPage.getSize())
+                .setTotal(entityPage.getTotal())
+                .setRecords(this.entityList2BOs(entityPage.getRecords()));
+    }
+
+    /**
+     * 实体 List 转 BO List
+     *
+     * @param entityList 实体 List
+     * @return BO List
+     */
+    private List<SysCustomerDataPrivateBO> entityList2BOs(List<SysCustomerDataEntity> entityList) {
+        if (CollUtil.isEmpty(entityList)) {
+            return Collections.emptyList();
+        }
+
+        //查询小组数据
+        List<Long> groupIds =
+                entityList.stream()
+                        .map(SysCustomerDataEntity::getClaimUserGroupId)
+                        .filter(Objects::nonNull).distinct().toList();
+        List<SysGroupEntity> groupEntityList = sysGroupMapper.selectByIds(groupIds);
+        //查询学段
+        List<Long> stageIds =
+                entityList.stream()
+                        .map(SysCustomerDataEntity::getStageId)
+                        .filter(Objects::nonNull).distinct().toList();
+        List<SysParamBaseDataEntity> stageDataList = sysParamBaseDataMapper.selectByIds(stageIds);
+
+        //查询跟进
+        List<SysCustomerFollowEntity> followEntityList =
+                sysCustomerFollowMapper.selectList(
+                        new LambdaQueryWrapper<>(SysCustomerFollowEntity.class)
+                                .select(
+                                        SysCustomerFollowEntity::getCustomerId,
+                                        SysCustomerFollowEntity::getClaimUserGroupId,
+                                        SysCustomerFollowEntity::getClaimUserGroupId,
+                                        SysCustomerFollowEntity::getClaimDatetime
+                                )
+                                .in(SysCustomerFollowEntity::getCustomerId, entityList.stream().map(SysCustomerDataEntity::getId).toList())
+                                .in(SysCustomerFollowEntity::getClaimUserGroupId, groupIds)
+                                .in(SysCustomerFollowEntity::getClaimUserId, entityList.stream().map(SysCustomerDataEntity::getClaimUserGroupId).distinct().toList())
+                );
+
+
+        // 深拷贝
+        List<SysCustomerDataPrivateBO> ret = new ArrayList<>(entityList.size());
+        entityList.forEach(
+                entity -> {
+                    SysCustomerDataPrivateBO bo = this.entity2BO(entity);
+                    if (CollUtil.isNotEmpty(groupEntityList)) {
+                        groupEntityList.stream()
+                                .filter(x -> x.getId().equals(entity.getClaimUserGroupId()))
+                                .findFirst()
+                                .ifPresent(e -> bo.setGroupName(e.getTitle()));
+                    }
+                    if (CollUtil.isNotEmpty(stageDataList)) {
+                        stageDataList.stream()
+                                .filter(x -> x.getId().equals(entity.getStageId()))
+                                .findFirst()
+                                .ifPresent(e -> bo.setStageName(e.getTitle()));
+                    }
+                    if (CollUtil.isNotEmpty(followEntityList)) {
+                        List<SysCustomerFollowEntity> userFollowEntityList =
+                                followEntityList.stream()
+                                        .filter(x -> x.getCustomerId().equals(entity.getId()))
+                                        .filter(x -> x.getClaimUserGroupId().equals(entity.getClaimUserGroupId()))
+                                        .filter(x -> x.getClaimUserId().equals(entity.getClaimUserId()))
+                                        .sorted(Comparator.comparing(SysCustomerFollowEntity::getClaimDatetime).reversed())
+                                        .toList();
+                        if (CollUtil.isNotEmpty(userFollowEntityList)) {
+                            bo.setLastFollowDatetime(userFollowEntityList.getFirst().getClaimDatetime());
+                        }
+                    }
+                    ret.add(bo);
+                }
+        );
+        return ret;
+    }
+
+    /**
+     * 实体转 BO
+     *
+     * @param entity 实体
+     * @return BO
+     */
+    private SysCustomerDataPrivateBO entity2BO(SysCustomerDataEntity entity) {
+        if (entity == null) {
+            return null;
+        }
+        SysCustomerDataPrivateBO bo = new SysCustomerDataPrivateBO();
+        BeanUtil.copyProperties(entity, bo);
+        // 可以在此处为BO填充字段
+        bo.setClaimUserName(entity.getClaimUserName());
+        bo.setClaimDatetime(entity.getClaimDatetime());
+        bo.setTypeName("首单客户");
+        if (entity.getSuccessStatus() == 1 && entity.getSuccessUserId() != null) {
+            bo.setTypeName("存量客户");
+        }
+        return bo;
+    }
+}

+ 167 - 0
src/main/java/com/jtzx/crm/module/sys/service/SysCustomerDataPublicService.java

@@ -0,0 +1,167 @@
+package com.jtzx.crm.module.sys.service;
+
+
+import cc.uncarbon.framework.core.page.PageResult;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.text.CharSequenceUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.jtzx.crm.module.sys.entity.SysCustomerDataEntity;
+import com.jtzx.crm.module.sys.entity.SysParamBaseDataEntity;
+import com.jtzx.crm.module.sys.mapper.SysCustomerDataMapper;
+import com.jtzx.crm.module.sys.mapper.SysParamBaseDataMapper;
+import com.jtzx.crm.module.sys.model.request.AdminSysCustomerDataPublicListDTO;
+import com.jtzx.crm.module.sys.model.response.SysCustomerDataPublicBO;
+import com.jtzx.crm.module.sys.model.response.SysCustomerPublicSelectBO;
+import com.jtzx.crm.module.sys.model.response.SysCustomerSelectBO;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 公海客户(学校)数据
+ */
+@RequiredArgsConstructor
+@Service
+@Slf4j
+public class SysCustomerDataPublicService {
+
+    private final SysCustomerDataMapper sysCustomerDataMapper;
+
+    private final SysParamBaseDataMapper sysParamBaseDataMapper;
+
+    private final SysCustomerUserCommonService sysCustomerUserCommonService;
+
+    /**
+     * 查询公海客户页面顶部筛选条件数据
+     * @return
+     */
+    public SysCustomerPublicSelectBO queryHeadSelectData() {
+        //返回数据对象
+        SysCustomerPublicSelectBO resultData = SysCustomerPublicSelectBO.builder().build();
+        //学段信息
+        resultData.setStageData(sysCustomerUserCommonService.handleStageData());
+        //省市信息
+        SysCustomerSelectBO sysCustomerSelectBO = sysCustomerUserCommonService.querySelectDataByType1(true, false);
+        if (sysCustomerSelectBO != null) {
+            resultData.setRegionData(sysCustomerSelectBO.getRegionData());
+        }
+        return resultData;
+    }
+
+
+    /**
+     * 后台管理-公海客户(学校)分页列表
+     */
+    public PageResult<SysCustomerDataPublicBO> adminList(AdminSysCustomerDataPublicListDTO dto) {
+        Page<SysCustomerDataEntity> entityPage = sysCustomerDataMapper.selectPage(
+                new Page<>(dto.getPageNum(), dto.getPageSize()),
+                new QueryWrapper<SysCustomerDataEntity>()
+                        .lambda()
+                        // 名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getQueryStr()), SysCustomerDataEntity::getName, CharSequenceUtil.cleanBlank(dto.getQueryStr()))
+                        // 学段id(param_base_data表主id)
+                        .eq(Objects.nonNull(dto.getStageId()), SysCustomerDataEntity::getStageId, dto.getStageId())
+                        // 省份id(省份表主id)
+                        .eq(Objects.nonNull(dto.getProvinceId()), SysCustomerDataEntity::getProvinceId, dto.getProvinceId())
+                        // 城市id(不为空时)
+                        .eq(Objects.nonNull(dto.getCityId()), SysCustomerDataEntity::getCityId, dto.getCityId())
+                        // 城市id(为空时)
+                        .in(Objects.isNull(dto.getCityId()), SysCustomerDataEntity::getCityId, sysCustomerUserCommonService.queryUserCityIds(dto.getProvinceId()))
+                        //认领状态
+                        .eq(SysCustomerDataEntity::getClaimStatus, 0)
+                        // 排序
+                        .orderByDesc(SysCustomerDataEntity::getCreatedAt)
+        );
+
+        return this.entityPage2BOPage(entityPage);
+    }
+
+
+     /*
+    ----------------------------------------------------------------
+                        私有方法 private methods
+    ----------------------------------------------------------------
+     */
+
+    /**
+     * 实体分页转 BO 分页
+     *
+     * @param entityPage 实体分页
+     * @return BO 分页
+     */
+    private PageResult<SysCustomerDataPublicBO> entityPage2BOPage(Page<SysCustomerDataEntity> entityPage) {
+        return new PageResult<SysCustomerDataPublicBO>()
+                .setCurrent(entityPage.getCurrent())
+                .setSize(entityPage.getSize())
+                .setTotal(entityPage.getTotal())
+                .setRecords(this.entityList2BOs(entityPage.getRecords()));
+    }
+
+    /**
+     * 实体 List 转 BO List
+     *
+     * @param entityList 实体 List
+     * @return BO List
+     */
+    private List<SysCustomerDataPublicBO> entityList2BOs(List<SysCustomerDataEntity> entityList) {
+        if (CollUtil.isEmpty(entityList)) {
+            return Collections.emptyList();
+        }
+        //查询学段
+        List<Long> stageIds =
+                entityList.stream()
+                        .map(SysCustomerDataEntity::getStageId)
+                        .filter(Objects::nonNull).distinct().toList();
+        List<SysParamBaseDataEntity> stageDataList = sysParamBaseDataMapper.selectByIds(stageIds);
+        // 深拷贝
+        List<SysCustomerDataPublicBO> ret = new ArrayList<>(entityList.size());
+        entityList.forEach(
+                entity -> {
+                    SysCustomerDataPublicBO bo = this.entity2BO(entity);
+                    if (CollUtil.isNotEmpty(stageDataList)) {
+                        stageDataList.stream()
+                                .filter(x -> x.getId().equals(entity.getStageId()))
+                                .findFirst()
+                                .ifPresent(e -> bo.setStageName(e.getTitle()));
+                    }
+                    ret.add(bo);
+                });
+        return ret;
+    }
+
+
+    /**
+     * 实体转 BO
+     *
+     * @param entity 实体
+     * @return BO
+     */
+    private SysCustomerDataPublicBO entity2BO(SysCustomerDataEntity entity) {
+        if (entity == null) {
+            return null;
+        }
+        SysCustomerDataPublicBO bo = new SysCustomerDataPublicBO();
+        BeanUtil.copyProperties(entity, bo);
+        // 可以在此处为BO填充字段
+        bo.setUnUsedDayCount("1天");
+        long days = DateUtil.betweenDay(DateUtil.date(),
+                DateUtil.parse(entity.getResetDatetime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))),
+                false);
+        //向上取整
+        double ceilDays = Math.ceil(days);
+        if ((int) ceilDays >= 1) {
+            bo.setUnUsedDayCount(days + "天");
+        }
+        bo.setDelStatus(true);
+        return bo;
+    }
+}

+ 239 - 0
src/main/java/com/jtzx/crm/module/sys/service/SysCustomerDataService.java

@@ -0,0 +1,239 @@
+package com.jtzx.crm.module.sys.service;
+
+import cc.uncarbon.framework.core.exception.BusinessException;
+import cc.uncarbon.framework.core.page.PageParam;
+import cc.uncarbon.framework.core.page.PageResult;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.text.CharSequenceUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.jtzx.crm.module.sys.entity.SysCustomerDataEntity;
+import com.jtzx.crm.module.sys.mapper.SysCustomerDataMapper;
+import com.jtzx.crm.module.sys.model.request.AdminSysCustomerDataInsertOrUpdateDTO;
+import com.jtzx.crm.module.sys.model.request.AdminSysCustomerDataListDTO;
+import com.jtzx.crm.module.sys.model.response.SysCustomerDataBO;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+
+
+/**
+ * 客户(学校)数据
+ */
+@RequiredArgsConstructor
+@Service
+@Slf4j
+public class SysCustomerDataService {
+
+    private final SysCustomerDataMapper sysCustomerDataMapper;
+
+
+    /**
+     * 后台管理-分页列表
+     */
+    public PageResult<SysCustomerDataBO> adminList(PageParam pageParam, AdminSysCustomerDataListDTO dto) {
+        Page<SysCustomerDataEntity> entityPage = sysCustomerDataMapper.selectPage(
+                new Page<>(pageParam.getPageNum(), pageParam.getPageSize()),
+                new QueryWrapper<SysCustomerDataEntity>()
+                        .lambda()
+                        // 名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getName()), SysCustomerDataEntity::getName, CharSequenceUtil.cleanBlank(dto.getName()))
+                        // 学段id(param_base_data表主id)
+                        .eq(Objects.nonNull(dto.getStageId()), SysCustomerDataEntity::getStageId, dto.getStageId())
+                        // 学段名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getStageName()), SysCustomerDataEntity::getStageName, CharSequenceUtil.cleanBlank(dto.getStageName()))
+                        // 省份id(省份表主id)
+                        .eq(Objects.nonNull(dto.getProvinceId()), SysCustomerDataEntity::getProvinceId, dto.getProvinceId())
+                        // 省份名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getProvinceName()), SysCustomerDataEntity::getProvinceName, CharSequenceUtil.cleanBlank(dto.getProvinceName()))
+                        // 城市id(城市表主id)
+                        .eq(Objects.nonNull(dto.getCityId()), SysCustomerDataEntity::getCityId, dto.getCityId())
+                        // 城市名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getCityName()), SysCustomerDataEntity::getCityName, CharSequenceUtil.cleanBlank(dto.getCityName()))
+                        // 地址
+                        .like(CharSequenceUtil.isNotBlank(dto.getAddress()), SysCustomerDataEntity::getAddress, CharSequenceUtil.cleanBlank(dto.getAddress()))
+                        // 放入公海时间
+                        .eq(Objects.nonNull(dto.getResetDatetime()), SysCustomerDataEntity::getResetDatetime, dto.getResetDatetime())
+                        // 认领人id
+                        .eq(Objects.nonNull(dto.getClaimUserId()), SysCustomerDataEntity::getClaimUserId, dto.getClaimUserId())
+                        // 认领人名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getClaimUserName()), SysCustomerDataEntity::getClaimUserName, CharSequenceUtil.cleanBlank(dto.getClaimUserName()))
+                        // 认领状态,0-否,1-是
+                        .eq(Objects.nonNull(dto.getClaimStatus()), SysCustomerDataEntity::getClaimStatus, dto.getClaimStatus())
+                        // 认领时间
+                        .eq(Objects.nonNull(dto.getClaimDatetime()), SysCustomerDataEntity::getClaimDatetime, dto.getClaimDatetime())
+                        // 认领人员所在的项目组id
+                        .eq(Objects.nonNull(dto.getClaimUserGroupId()), SysCustomerDataEntity::getClaimUserGroupId, dto.getClaimUserGroupId())
+                        // 跟进状态id,赢单状态id固定-37,方案演示id固定-33
+                        .eq(Objects.nonNull(dto.getFollowId()), SysCustomerDataEntity::getFollowId, dto.getFollowId())
+                        // 跟进状态的名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getFollowName()), SysCustomerDataEntity::getFollowName, CharSequenceUtil.cleanBlank(dto.getFollowName()))
+                        // 赢单人id(不为null时为赢单人id)
+                        .eq(Objects.nonNull(dto.getSuccessUserId()), SysCustomerDataEntity::getSuccessUserId, dto.getSuccessUserId())
+                        // 赢单人名称(不为null时为赢单人名称)
+                        .like(CharSequenceUtil.isNotBlank(dto.getSuccessUserName()), SysCustomerDataEntity::getSuccessUserName, CharSequenceUtil.cleanBlank(dto.getSuccessUserName()))
+                        // 是否赢单,0-否,1-是
+                        .eq(Objects.nonNull(dto.getSuccessStatus()), SysCustomerDataEntity::getSuccessStatus, dto.getSuccessStatus())
+                        // 时间区间
+                        .between(Objects.nonNull(dto.getBeginAt()) && Objects.nonNull(dto.getEndAt()), SysCustomerDataEntity::getCreatedAt, dto.getBeginAt(), dto.getEndAt())
+                        // 排序
+                        .orderByDesc(SysCustomerDataEntity::getCreatedAt)
+        );
+
+        return this.entityPage2BOPage(entityPage);
+    }
+
+    /**
+     * 根据 ID 取详情
+     *
+     * @param id 主键ID
+     * @return null or BO
+     */
+    public SysCustomerDataBO getOneById(Long id) {
+        return this.getOneById(id, false);
+    }
+
+    /**
+     * 根据 ID 取详情
+     *
+     * @param id 主键ID
+     * @param throwIfInvalidId 是否在 ID 无效时抛出异常
+     * @return null or BO
+     */
+    public SysCustomerDataBO getOneById(Long id, boolean throwIfInvalidId) throws BusinessException {
+        SysCustomerDataEntity entity = sysCustomerDataMapper.selectById(id);
+        if (throwIfInvalidId && entity == null) {
+            throw new BusinessException(400, "无效客户(学校)数据ID");
+        }
+
+        return this.entity2BO(entity);
+    }
+
+    /**
+     * 后台管理-新增
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public Long adminInsert(AdminSysCustomerDataInsertOrUpdateDTO dto) {
+        log.info("[后台管理-客户(学校)数据-新增] >> DTO={}", dto);
+        this.checkExistence(dto);
+
+        dto.setId(null);
+        SysCustomerDataEntity entity = new SysCustomerDataEntity();
+        BeanUtil.copyProperties(dto, entity);
+
+        sysCustomerDataMapper.insert(entity);
+
+        return entity.getId();
+    }
+
+    /**
+     * 后台管理-编辑
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public void adminUpdate(AdminSysCustomerDataInsertOrUpdateDTO dto) {
+        log.info("[后台管理-客户(学校)数据-编辑] >> DTO={}", dto);
+        this.checkExistence(dto);
+
+        SysCustomerDataEntity entity = new SysCustomerDataEntity();
+        BeanUtil.copyProperties(dto, entity);
+
+        sysCustomerDataMapper.updateById(entity);
+    }
+
+    /**
+     * 后台管理-删除
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public void adminDelete(Collection<Long> ids) {
+        log.info("[后台管理-客户(学校)数据-删除] >> ids={}", ids);
+        sysCustomerDataMapper.deleteByIds(ids);
+    }
+
+
+    /*
+    ----------------------------------------------------------------
+                        私有方法 private methods
+    ----------------------------------------------------------------
+     */
+
+    /**
+     * 实体转 BO
+     *
+     * @param entity 实体
+     * @return BO
+     */
+    private SysCustomerDataBO entity2BO(SysCustomerDataEntity entity) {
+        if (entity == null) {
+            return null;
+        }
+
+        SysCustomerDataBO bo = new SysCustomerDataBO();
+        BeanUtil.copyProperties(entity, bo);
+
+        // 可以在此处为BO填充字段
+
+        return bo;
+    }
+
+    /**
+     * 实体 List 转 BO List
+     *
+     * @param entityList 实体 List
+     * @return BO List
+     */
+    private List<SysCustomerDataBO> entityList2BOs(List<SysCustomerDataEntity> entityList) {
+        if (CollUtil.isEmpty(entityList)) {
+            return Collections.emptyList();
+        }
+
+        // 深拷贝
+        List<SysCustomerDataBO> ret = new ArrayList<>(entityList.size());
+        entityList.forEach(
+            entity -> ret.add(this.entity2BO(entity))
+        );
+
+        return ret;
+    }
+
+    /**
+     * 实体分页转 BO 分页
+     *
+     * @param entityPage 实体分页
+     * @return BO 分页
+     */
+    private PageResult<SysCustomerDataBO> entityPage2BOPage(Page<SysCustomerDataEntity> entityPage) {
+        return new PageResult<SysCustomerDataBO>()
+            .setCurrent(entityPage.getCurrent())
+            .setSize(entityPage.getSize())
+            .setTotal(entityPage.getTotal())
+            .setRecords(this.entityList2BOs(entityPage.getRecords()));
+    }
+
+    /**
+     * 检查是否已存在同名数据
+     *
+     * @param dto DTO
+     */
+    private void checkExistence(AdminSysCustomerDataInsertOrUpdateDTO dto) {
+        /*
+        可以根据自己业务需要,解禁这段代码,修改判断条件和文案
+
+        SysCustomerDataEntity existingEntity = sysCustomerDataMapper.selectOne(
+                new QueryWrapper<SysCustomerDataEntity>()
+                        .lambda()
+                        .select(SysCustomerDataEntity::getId)
+                        .eq(SysCustomerDataEntity::getTitle, dto.getTitle())
+                        .last(HelioConstant.CRUD.SQL_LIMIT_1)
+        );
+
+        if (existingEntity != null && !existingEntity.getId().equals(dto.getId())) {
+            throw new BusinessException(400, "已存在相同客户(学校)数据,请重新输入");
+        }
+        */
+    }
+
+}

+ 238 - 0
src/main/java/com/jtzx/crm/module/sys/service/SysCustomerFollowAuditService.java

@@ -0,0 +1,238 @@
+package com.jtzx.crm.module.sys.service;
+
+import cc.uncarbon.framework.core.constant.HelioConstant;
+import cc.uncarbon.framework.core.exception.BusinessException;
+import cc.uncarbon.framework.core.page.PageParam;
+import cc.uncarbon.framework.core.page.PageResult;
+import com.jtzx.crm.module.sys.entity.SysCustomerFollowAuditEntity;
+import com.jtzx.crm.module.sys.mapper.SysCustomerFollowAuditMapper;
+import com.jtzx.crm.module.sys.model.request.AdminSysCustomerFollowAuditInsertOrUpdateDTO;
+import com.jtzx.crm.module.sys.model.request.AdminSysCustomerFollowAuditListDTO;
+import com.jtzx.crm.module.sys.model.response.SysCustomerFollowAuditBO;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.text.CharSequenceUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+
+
+/**
+ * 赢单跟进审核信息
+ */
+@RequiredArgsConstructor
+@Service
+@Slf4j
+public class SysCustomerFollowAuditService {
+
+    private final SysCustomerFollowAuditMapper sysCustomerFollowAuditMapper;
+
+
+    /**
+     * 后台管理-分页列表
+     */
+    public PageResult<SysCustomerFollowAuditBO> adminList(PageParam pageParam, AdminSysCustomerFollowAuditListDTO dto) {
+        Page<SysCustomerFollowAuditEntity> entityPage = sysCustomerFollowAuditMapper.selectPage(
+                new Page<>(pageParam.getPageNum(), pageParam.getPageSize()),
+                new QueryWrapper<SysCustomerFollowAuditEntity>()
+                        .lambda()
+                        // 认领人(跟进)id
+                        .eq(Objects.nonNull(dto.getClaimUserId()), SysCustomerFollowAuditEntity::getClaimUserId, dto.getClaimUserId())
+                        // 审核人id
+                        .like(CharSequenceUtil.isNotBlank(dto.getClaimUserName()), SysCustomerFollowAuditEntity::getClaimUserName, CharSequenceUtil.cleanBlank(dto.getClaimUserName()))
+                        // 认领人员所在的项目组id
+                        .eq(Objects.nonNull(dto.getClaimUserGroupId()), SysCustomerFollowAuditEntity::getClaimUserGroupId, dto.getClaimUserGroupId())
+                        // 跟进时间
+                        .eq(Objects.nonNull(dto.getClaimDatetime()), SysCustomerFollowAuditEntity::getClaimDatetime, dto.getClaimDatetime())
+                        // 客户表主id
+                        .eq(Objects.nonNull(dto.getCustomerId()), SysCustomerFollowAuditEntity::getCustomerId, dto.getCustomerId())
+                        // 客户名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getCustomerName()), SysCustomerFollowAuditEntity::getCustomerName, CharSequenceUtil.cleanBlank(dto.getCustomerName()))
+                        // 跟进状态id,赢单状态id固定-37,方案演示id固定-33
+                        .eq(Objects.nonNull(dto.getFollowId()), SysCustomerFollowAuditEntity::getFollowId, dto.getFollowId())
+                        // 跟进状态的名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getFollowName()), SysCustomerFollowAuditEntity::getFollowName, CharSequenceUtil.cleanBlank(dto.getFollowName()))
+                        // 联系人id(customer_person表主id)
+                        .eq(Objects.nonNull(dto.getCustomerPersonId()), SysCustomerFollowAuditEntity::getCustomerPersonId, dto.getCustomerPersonId())
+                        // 联系人名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getCustomerPersonName()), SysCustomerFollowAuditEntity::getCustomerPersonName, CharSequenceUtil.cleanBlank(dto.getCustomerPersonName()))
+                        // 职务id(param_base_data表主id)
+                        .eq(Objects.nonNull(dto.getPositionId()), SysCustomerFollowAuditEntity::getPositionId, dto.getPositionId())
+                        // 职务名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getPositionName()), SysCustomerFollowAuditEntity::getPositionName, CharSequenceUtil.cleanBlank(dto.getPositionName()))
+                        // 跟进内容
+                        .like(CharSequenceUtil.isNotBlank(dto.getFollowContent()), SysCustomerFollowAuditEntity::getFollowContent, CharSequenceUtil.cleanBlank(dto.getFollowContent()))
+                        // 产品id(param_base_data表主id)
+                        .eq(Objects.nonNull(dto.getProductId()), SysCustomerFollowAuditEntity::getProductId, dto.getProductId())
+                        // 产品名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getProductName()), SysCustomerFollowAuditEntity::getProductName, CharSequenceUtil.cleanBlank(dto.getProductName()))
+                        // 审核状态:0-待审核,1-已通过,2-已驳回
+                        .eq(Objects.nonNull(dto.getAuditStatus()), SysCustomerFollowAuditEntity::getAuditStatus, dto.getAuditStatus())
+                        // 审核人id
+                        .eq(Objects.nonNull(dto.getAuditUserId()), SysCustomerFollowAuditEntity::getAuditUserId, dto.getAuditUserId())
+                        // 审核人名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getAuditUserName()), SysCustomerFollowAuditEntity::getAuditUserName, CharSequenceUtil.cleanBlank(dto.getAuditUserName()))
+                        // 时间区间
+                        .between(Objects.nonNull(dto.getBeginAt()) && Objects.nonNull(dto.getEndAt()), SysCustomerFollowAuditEntity::getCreatedAt, dto.getBeginAt(), dto.getEndAt())
+                        // 排序
+                        .orderByDesc(SysCustomerFollowAuditEntity::getCreatedAt)
+        );
+
+        return this.entityPage2BOPage(entityPage);
+    }
+
+    /**
+     * 根据 ID 取详情
+     *
+     * @param id 主键ID
+     * @return null or BO
+     */
+    public SysCustomerFollowAuditBO getOneById(Long id) {
+        return this.getOneById(id, false);
+    }
+
+    /**
+     * 根据 ID 取详情
+     *
+     * @param id 主键ID
+     * @param throwIfInvalidId 是否在 ID 无效时抛出异常
+     * @return null or BO
+     */
+    public SysCustomerFollowAuditBO getOneById(Long id, boolean throwIfInvalidId) throws BusinessException {
+        SysCustomerFollowAuditEntity entity = sysCustomerFollowAuditMapper.selectById(id);
+        if (throwIfInvalidId && entity == null) {
+            throw new BusinessException(400, "无效赢单跟进审核信息ID");
+        }
+
+        return this.entity2BO(entity);
+    }
+
+    /**
+     * 后台管理-新增
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public Long adminInsert(AdminSysCustomerFollowAuditInsertOrUpdateDTO dto) {
+        log.info("[后台管理-赢单跟进审核信息-新增] >> DTO={}", dto);
+        this.checkExistence(dto);
+
+        dto.setId(null);
+        SysCustomerFollowAuditEntity entity = new SysCustomerFollowAuditEntity();
+        BeanUtil.copyProperties(dto, entity);
+
+        sysCustomerFollowAuditMapper.insert(entity);
+
+        return entity.getId();
+    }
+
+    /**
+     * 后台管理-编辑
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public void adminUpdate(AdminSysCustomerFollowAuditInsertOrUpdateDTO dto) {
+        log.info("[后台管理-赢单跟进审核信息-编辑] >> DTO={}", dto);
+        this.checkExistence(dto);
+
+        SysCustomerFollowAuditEntity entity = new SysCustomerFollowAuditEntity();
+        BeanUtil.copyProperties(dto, entity);
+
+        sysCustomerFollowAuditMapper.updateById(entity);
+    }
+
+    /**
+     * 后台管理-删除
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public void adminDelete(Collection<Long> ids) {
+        log.info("[后台管理-赢单跟进审核信息-删除] >> ids={}", ids);
+        sysCustomerFollowAuditMapper.deleteByIds(ids);
+    }
+
+
+    /*
+    ----------------------------------------------------------------
+                        私有方法 private methods
+    ----------------------------------------------------------------
+     */
+
+    /**
+     * 实体转 BO
+     *
+     * @param entity 实体
+     * @return BO
+     */
+    private SysCustomerFollowAuditBO entity2BO(SysCustomerFollowAuditEntity entity) {
+        if (entity == null) {
+            return null;
+        }
+
+        SysCustomerFollowAuditBO bo = new SysCustomerFollowAuditBO();
+        BeanUtil.copyProperties(entity, bo);
+
+        // 可以在此处为BO填充字段
+
+        return bo;
+    }
+
+    /**
+     * 实体 List 转 BO List
+     *
+     * @param entityList 实体 List
+     * @return BO List
+     */
+    private List<SysCustomerFollowAuditBO> entityList2BOs(List<SysCustomerFollowAuditEntity> entityList) {
+        if (CollUtil.isEmpty(entityList)) {
+            return Collections.emptyList();
+        }
+
+        // 深拷贝
+        List<SysCustomerFollowAuditBO> ret = new ArrayList<>(entityList.size());
+        entityList.forEach(
+            entity -> ret.add(this.entity2BO(entity))
+        );
+
+        return ret;
+    }
+
+    /**
+     * 实体分页转 BO 分页
+     *
+     * @param entityPage 实体分页
+     * @return BO 分页
+     */
+    private PageResult<SysCustomerFollowAuditBO> entityPage2BOPage(Page<SysCustomerFollowAuditEntity> entityPage) {
+        return new PageResult<SysCustomerFollowAuditBO>()
+            .setCurrent(entityPage.getCurrent())
+            .setSize(entityPage.getSize())
+            .setTotal(entityPage.getTotal())
+            .setRecords(this.entityList2BOs(entityPage.getRecords()));
+    }
+
+    /**
+     * 检查是否已存在同名数据
+     *
+     * @param dto DTO
+     */
+    private void checkExistence(AdminSysCustomerFollowAuditInsertOrUpdateDTO dto) {
+        /*
+        可以根据自己业务需要,解禁这段代码,修改判断条件和文案
+
+        SysCustomerFollowAuditEntity existingEntity = sysCustomerFollowAuditMapper.selectOne(
+                new QueryWrapper<SysCustomerFollowAuditEntity>()
+                        .lambda()
+                        .select(SysCustomerFollowAuditEntity::getId)
+                        .eq(SysCustomerFollowAuditEntity::getTitle, dto.getTitle())
+                        .last(HelioConstant.CRUD.SQL_LIMIT_1)
+        );
+
+        if (existingEntity != null && !existingEntity.getId().equals(dto.getId())) {
+            throw new BusinessException(400, "已存在相同赢单跟进审核信息,请重新输入");
+        }
+        */
+    }
+
+}

+ 242 - 0
src/main/java/com/jtzx/crm/module/sys/service/SysCustomerFollowService.java

@@ -0,0 +1,242 @@
+package com.jtzx.crm.module.sys.service;
+
+import cc.uncarbon.framework.core.constant.HelioConstant;
+import cc.uncarbon.framework.core.exception.BusinessException;
+import cc.uncarbon.framework.core.page.PageParam;
+import cc.uncarbon.framework.core.page.PageResult;
+import com.jtzx.crm.module.sys.entity.SysCustomerFollowEntity;
+import com.jtzx.crm.module.sys.mapper.SysCustomerFollowMapper;
+import com.jtzx.crm.module.sys.model.request.AdminSysCustomerFollowInsertOrUpdateDTO;
+import com.jtzx.crm.module.sys.model.request.AdminSysCustomerFollowListDTO;
+import com.jtzx.crm.module.sys.model.response.SysCustomerFollowBO;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.text.CharSequenceUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+
+
+/**
+ * 客户跟进数据信息
+ */
+@RequiredArgsConstructor
+@Service
+@Slf4j
+public class SysCustomerFollowService {
+
+    private final SysCustomerFollowMapper sysCustomerFollowMapper;
+
+
+    /**
+     * 后台管理-分页列表
+     */
+    public PageResult<SysCustomerFollowBO> adminList(PageParam pageParam, AdminSysCustomerFollowListDTO dto) {
+        Page<SysCustomerFollowEntity> entityPage = sysCustomerFollowMapper.selectPage(
+                new Page<>(pageParam.getPageNum(), pageParam.getPageSize()),
+                new QueryWrapper<SysCustomerFollowEntity>()
+                        .lambda()
+                        // 认领人(跟进)id
+                        .eq(Objects.nonNull(dto.getClaimUserId()), SysCustomerFollowEntity::getClaimUserId, dto.getClaimUserId())
+                        // 认领人(跟进)名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getClaimUserName()), SysCustomerFollowEntity::getClaimUserName, CharSequenceUtil.cleanBlank(dto.getClaimUserName()))
+                        // 认领人员所在的项目组id
+                        .eq(Objects.nonNull(dto.getClaimUserGroupId()), SysCustomerFollowEntity::getClaimUserGroupId, dto.getClaimUserGroupId())
+                        // 跟进时间
+                        .eq(Objects.nonNull(dto.getClaimDatetime()), SysCustomerFollowEntity::getClaimDatetime, dto.getClaimDatetime())
+                        // 客户表主id
+                        .eq(Objects.nonNull(dto.getCustomerId()), SysCustomerFollowEntity::getCustomerId, dto.getCustomerId())
+                        // 客户名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getCustomerName()), SysCustomerFollowEntity::getCustomerName, CharSequenceUtil.cleanBlank(dto.getCustomerName()))
+                        // 产品id(param_base_data表主id)
+                        .eq(Objects.nonNull(dto.getProductId()), SysCustomerFollowEntity::getProductId, dto.getProductId())
+                        // 产品名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getProductName()), SysCustomerFollowEntity::getProductName, CharSequenceUtil.cleanBlank(dto.getProductName()))
+                        // 跟进状态id,赢单状态id固定-37,方案演示id固定-33
+                        .eq(Objects.nonNull(dto.getFollowId()), SysCustomerFollowEntity::getFollowId, dto.getFollowId())
+                        // 跟进状态的名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getFollowName()), SysCustomerFollowEntity::getFollowName, CharSequenceUtil.cleanBlank(dto.getFollowName()))
+                        // 是否赢单,0-否,1-是
+                        .eq(Objects.nonNull(dto.getSuccessStatus()), SysCustomerFollowEntity::getSuccessStatus, dto.getSuccessStatus())
+                        // 联系人id(customer_person表主id)
+                        .eq(Objects.nonNull(dto.getCustomerPersonId()), SysCustomerFollowEntity::getCustomerPersonId, dto.getCustomerPersonId())
+                        // 联系人名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getCustomerPersonName()), SysCustomerFollowEntity::getCustomerPersonName, CharSequenceUtil.cleanBlank(dto.getCustomerPersonName()))
+                        // 职务id(param_base_data表主id)
+                        .eq(Objects.nonNull(dto.getPositionId()), SysCustomerFollowEntity::getPositionId, dto.getPositionId())
+                        // 职务名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getPositionName()), SysCustomerFollowEntity::getPositionName, CharSequenceUtil.cleanBlank(dto.getPositionName()))
+                        // 跟进内容
+                        .like(CharSequenceUtil.isNotBlank(dto.getFollowContent()), SysCustomerFollowEntity::getFollowContent, CharSequenceUtil.cleanBlank(dto.getFollowContent()))
+                        // 预约待办类型,0-预约,1-待办,(follow_id=33时为预约,其它为待办),这个数据可以为null
+                        .eq(Objects.nonNull(dto.getOrderType()), SysCustomerFollowEntity::getOrderType, dto.getOrderType())
+                        // 待办时间
+                        .eq(Objects.nonNull(dto.getOrderDatetime()), SysCustomerFollowEntity::getOrderDatetime, dto.getOrderDatetime())
+                        // 预约,待办取消状态,0-未取消,1-已取消(预约的取消需要填写取消原因,待办的取消是一个开关,不需要填写原因)
+                        .eq(Objects.nonNull(dto.getOrderStatus()), SysCustomerFollowEntity::getOrderStatus, dto.getOrderStatus())
+                        // 预约取消原因
+                        .like(CharSequenceUtil.isNotBlank(dto.getCancelRemark()), SysCustomerFollowEntity::getCancelRemark, CharSequenceUtil.cleanBlank(dto.getCancelRemark()))
+                        // 时间区间
+                        .between(Objects.nonNull(dto.getBeginAt()) && Objects.nonNull(dto.getEndAt()), SysCustomerFollowEntity::getCreatedAt, dto.getBeginAt(), dto.getEndAt())
+                        // 排序
+                        .orderByDesc(SysCustomerFollowEntity::getCreatedAt)
+        );
+
+        return this.entityPage2BOPage(entityPage);
+    }
+
+    /**
+     * 根据 ID 取详情
+     *
+     * @param id 主键ID
+     * @return null or BO
+     */
+    public SysCustomerFollowBO getOneById(Long id) {
+        return this.getOneById(id, false);
+    }
+
+    /**
+     * 根据 ID 取详情
+     *
+     * @param id 主键ID
+     * @param throwIfInvalidId 是否在 ID 无效时抛出异常
+     * @return null or BO
+     */
+    public SysCustomerFollowBO getOneById(Long id, boolean throwIfInvalidId) throws BusinessException {
+        SysCustomerFollowEntity entity = sysCustomerFollowMapper.selectById(id);
+        if (throwIfInvalidId && entity == null) {
+            throw new BusinessException(400, "无效客户跟进数据信息ID");
+        }
+
+        return this.entity2BO(entity);
+    }
+
+    /**
+     * 后台管理-新增
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public Long adminInsert(AdminSysCustomerFollowInsertOrUpdateDTO dto) {
+        log.info("[后台管理-客户跟进数据信息-新增] >> DTO={}", dto);
+        this.checkExistence(dto);
+
+        dto.setId(null);
+        SysCustomerFollowEntity entity = new SysCustomerFollowEntity();
+        BeanUtil.copyProperties(dto, entity);
+
+        sysCustomerFollowMapper.insert(entity);
+
+        return entity.getId();
+    }
+
+    /**
+     * 后台管理-编辑
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public void adminUpdate(AdminSysCustomerFollowInsertOrUpdateDTO dto) {
+        log.info("[后台管理-客户跟进数据信息-编辑] >> DTO={}", dto);
+        this.checkExistence(dto);
+
+        SysCustomerFollowEntity entity = new SysCustomerFollowEntity();
+        BeanUtil.copyProperties(dto, entity);
+
+        sysCustomerFollowMapper.updateById(entity);
+    }
+
+    /**
+     * 后台管理-删除
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public void adminDelete(Collection<Long> ids) {
+        log.info("[后台管理-客户跟进数据信息-删除] >> ids={}", ids);
+        sysCustomerFollowMapper.deleteByIds(ids);
+    }
+
+
+    /*
+    ----------------------------------------------------------------
+                        私有方法 private methods
+    ----------------------------------------------------------------
+     */
+
+    /**
+     * 实体转 BO
+     *
+     * @param entity 实体
+     * @return BO
+     */
+    private SysCustomerFollowBO entity2BO(SysCustomerFollowEntity entity) {
+        if (entity == null) {
+            return null;
+        }
+
+        SysCustomerFollowBO bo = new SysCustomerFollowBO();
+        BeanUtil.copyProperties(entity, bo);
+
+        // 可以在此处为BO填充字段
+
+        return bo;
+    }
+
+    /**
+     * 实体 List 转 BO List
+     *
+     * @param entityList 实体 List
+     * @return BO List
+     */
+    private List<SysCustomerFollowBO> entityList2BOs(List<SysCustomerFollowEntity> entityList) {
+        if (CollUtil.isEmpty(entityList)) {
+            return Collections.emptyList();
+        }
+
+        // 深拷贝
+        List<SysCustomerFollowBO> ret = new ArrayList<>(entityList.size());
+        entityList.forEach(
+            entity -> ret.add(this.entity2BO(entity))
+        );
+
+        return ret;
+    }
+
+    /**
+     * 实体分页转 BO 分页
+     *
+     * @param entityPage 实体分页
+     * @return BO 分页
+     */
+    private PageResult<SysCustomerFollowBO> entityPage2BOPage(Page<SysCustomerFollowEntity> entityPage) {
+        return new PageResult<SysCustomerFollowBO>()
+            .setCurrent(entityPage.getCurrent())
+            .setSize(entityPage.getSize())
+            .setTotal(entityPage.getTotal())
+            .setRecords(this.entityList2BOs(entityPage.getRecords()));
+    }
+
+    /**
+     * 检查是否已存在同名数据
+     *
+     * @param dto DTO
+     */
+    private void checkExistence(AdminSysCustomerFollowInsertOrUpdateDTO dto) {
+        /*
+        可以根据自己业务需要,解禁这段代码,修改判断条件和文案
+
+        SysCustomerFollowEntity existingEntity = sysCustomerFollowMapper.selectOne(
+                new QueryWrapper<SysCustomerFollowEntity>()
+                        .lambda()
+                        .select(SysCustomerFollowEntity::getId)
+                        .eq(SysCustomerFollowEntity::getTitle, dto.getTitle())
+                        .last(HelioConstant.CRUD.SQL_LIMIT_1)
+        );
+
+        if (existingEntity != null && !existingEntity.getId().equals(dto.getId())) {
+            throw new BusinessException(400, "已存在相同客户跟进数据信息,请重新输入");
+        }
+        */
+    }
+
+}

+ 229 - 0
src/main/java/com/jtzx/crm/module/sys/service/SysCustomerOperLogService.java

@@ -0,0 +1,229 @@
+package com.jtzx.crm.module.sys.service;
+
+import cc.uncarbon.framework.core.exception.BusinessException;
+import cc.uncarbon.framework.core.page.PageParam;
+import cc.uncarbon.framework.core.page.PageResult;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.text.CharSequenceUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.jtzx.crm.module.sys.entity.SysCustomerOperLogEntity;
+import com.jtzx.crm.module.sys.mapper.SysCustomerOperLogMapper;
+import com.jtzx.crm.module.sys.model.request.AdminSysCustomerOperLogInsertOrUpdateDTO;
+import com.jtzx.crm.module.sys.model.request.AdminSysCustomerOperLogListDTO;
+import com.jtzx.crm.module.sys.model.response.SysCustomerOperLogBO;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+
+
+/**
+ * 客户操作日志信息数据
+ */
+@RequiredArgsConstructor
+@Service
+@Slf4j
+public class SysCustomerOperLogService {
+
+    private final SysCustomerOperLogMapper sysCustomerOperLogMapper;
+
+
+    /**
+     * 后台管理-分页列表
+     */
+    public PageResult<SysCustomerOperLogBO> adminList(PageParam pageParam, AdminSysCustomerOperLogListDTO dto) {
+        Page<SysCustomerOperLogEntity> entityPage = sysCustomerOperLogMapper.selectPage(
+                new Page<>(pageParam.getPageNum(), pageParam.getPageSize()),
+                new QueryWrapper<SysCustomerOperLogEntity>()
+                        .lambda()
+                        // 客户表主id
+                        .eq(Objects.nonNull(dto.getCustomerId()), SysCustomerOperLogEntity::getCustomerId, dto.getCustomerId())
+                        // 客户名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getCustomerName()), SysCustomerOperLogEntity::getCustomerName, CharSequenceUtil.cleanBlank(dto.getCustomerName()))
+                        // 操作人id
+                        .eq(Objects.nonNull(dto.getOperUserId()), SysCustomerOperLogEntity::getOperUserId, dto.getOperUserId())
+                        // 操作人名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getOperName()), SysCustomerOperLogEntity::getOperName, CharSequenceUtil.cleanBlank(dto.getOperName()))
+                        // 操作类型,0-认领,1-跟进,2-转让,3-转到公海,11-新增,12-修改,13-删除
+                        .eq(Objects.nonNull(dto.getOperAction()), SysCustomerOperLogEntity::getOperAction, dto.getOperAction())
+                        // 操作时间
+                        .eq(Objects.nonNull(dto.getOperDatetime()), SysCustomerOperLogEntity::getOperDatetime, dto.getOperDatetime())
+                        // 操作人员所在的项目组id
+                        .eq(Objects.nonNull(dto.getOperUserGroupId()), SysCustomerOperLogEntity::getOperUserGroupId, dto.getOperUserGroupId())
+                        // 省份id(省份表主id)
+                        .eq(Objects.nonNull(dto.getProvinceId()), SysCustomerOperLogEntity::getProvinceId, dto.getProvinceId())
+                        // 省份名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getProvinceName()), SysCustomerOperLogEntity::getProvinceName, CharSequenceUtil.cleanBlank(dto.getProvinceName()))
+                        // 城市id(城市表主id)
+                        .eq(Objects.nonNull(dto.getCityId()), SysCustomerOperLogEntity::getCityId, dto.getCityId())
+                        // 城市名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getCityName()), SysCustomerOperLogEntity::getCityName, CharSequenceUtil.cleanBlank(dto.getCityName()))
+                        // 跟进状态id,赢单状态id固定-37,方案演示id固定-33
+                        .eq(Objects.nonNull(dto.getFollowId()), SysCustomerOperLogEntity::getFollowId, dto.getFollowId())
+                        // 跟进状态的名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getFollowName()), SysCustomerOperLogEntity::getFollowName, CharSequenceUtil.cleanBlank(dto.getFollowName()))
+                        // 操作内容,的数据内容json格式保存
+                        .like(CharSequenceUtil.isNotBlank(dto.getContent()), SysCustomerOperLogEntity::getContent, CharSequenceUtil.cleanBlank(dto.getContent()))
+                        // 时间区间
+                        .between(Objects.nonNull(dto.getBeginAt()) && Objects.nonNull(dto.getEndAt()), SysCustomerOperLogEntity::getCreatedAt, dto.getBeginAt(), dto.getEndAt())
+                        // 排序
+                        .orderByDesc(SysCustomerOperLogEntity::getCreatedAt)
+        );
+
+        return this.entityPage2BOPage(entityPage);
+    }
+
+    /**
+     * 根据 ID 取详情
+     *
+     * @param id 主键ID
+     * @return null or BO
+     */
+    public SysCustomerOperLogBO getOneById(Long id) {
+        return this.getOneById(id, false);
+    }
+
+    /**
+     * 根据 ID 取详情
+     *
+     * @param id 主键ID
+     * @param throwIfInvalidId 是否在 ID 无效时抛出异常
+     * @return null or BO
+     */
+    public SysCustomerOperLogBO getOneById(Long id, boolean throwIfInvalidId) throws BusinessException {
+        SysCustomerOperLogEntity entity = sysCustomerOperLogMapper.selectById(id);
+        if (throwIfInvalidId && entity == null) {
+            throw new BusinessException(400, "无效客户操作日志信息数据ID");
+        }
+
+        return this.entity2BO(entity);
+    }
+
+    /**
+     * 后台管理-新增
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public Long adminInsert(AdminSysCustomerOperLogInsertOrUpdateDTO dto) {
+        log.info("[后台管理-客户操作日志信息数据-新增] >> DTO={}", dto);
+        this.checkExistence(dto);
+
+        dto.setId(null);
+        SysCustomerOperLogEntity entity = new SysCustomerOperLogEntity();
+        BeanUtil.copyProperties(dto, entity);
+
+        sysCustomerOperLogMapper.insert(entity);
+
+        return entity.getId();
+    }
+
+    /**
+     * 后台管理-编辑
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public void adminUpdate(AdminSysCustomerOperLogInsertOrUpdateDTO dto) {
+        log.info("[后台管理-客户操作日志信息数据-编辑] >> DTO={}", dto);
+        this.checkExistence(dto);
+
+        SysCustomerOperLogEntity entity = new SysCustomerOperLogEntity();
+        BeanUtil.copyProperties(dto, entity);
+
+        sysCustomerOperLogMapper.updateById(entity);
+    }
+
+    /**
+     * 后台管理-删除
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public void adminDelete(Collection<Long> ids) {
+        log.info("[后台管理-客户操作日志信息数据-删除] >> ids={}", ids);
+        sysCustomerOperLogMapper.deleteByIds(ids);
+    }
+
+
+    /*
+    ----------------------------------------------------------------
+                        私有方法 private methods
+    ----------------------------------------------------------------
+     */
+
+    /**
+     * 实体转 BO
+     *
+     * @param entity 实体
+     * @return BO
+     */
+    private SysCustomerOperLogBO entity2BO(SysCustomerOperLogEntity entity) {
+        if (entity == null) {
+            return null;
+        }
+
+        SysCustomerOperLogBO bo = new SysCustomerOperLogBO();
+        BeanUtil.copyProperties(entity, bo);
+
+        // 可以在此处为BO填充字段
+
+        return bo;
+    }
+
+    /**
+     * 实体 List 转 BO List
+     *
+     * @param entityList 实体 List
+     * @return BO List
+     */
+    private List<SysCustomerOperLogBO> entityList2BOs(List<SysCustomerOperLogEntity> entityList) {
+        if (CollUtil.isEmpty(entityList)) {
+            return Collections.emptyList();
+        }
+
+        // 深拷贝
+        List<SysCustomerOperLogBO> ret = new ArrayList<>(entityList.size());
+        entityList.forEach(
+            entity -> ret.add(this.entity2BO(entity))
+        );
+
+        return ret;
+    }
+
+    /**
+     * 实体分页转 BO 分页
+     *
+     * @param entityPage 实体分页
+     * @return BO 分页
+     */
+    private PageResult<SysCustomerOperLogBO> entityPage2BOPage(Page<SysCustomerOperLogEntity> entityPage) {
+        return new PageResult<SysCustomerOperLogBO>()
+            .setCurrent(entityPage.getCurrent())
+            .setSize(entityPage.getSize())
+            .setTotal(entityPage.getTotal())
+            .setRecords(this.entityList2BOs(entityPage.getRecords()));
+    }
+
+    /**
+     * 检查是否已存在同名数据
+     *
+     * @param dto DTO
+     */
+    private void checkExistence(AdminSysCustomerOperLogInsertOrUpdateDTO dto) {
+        /*
+        可以根据自己业务需要,解禁这段代码,修改判断条件和文案
+
+        SysCustomerOperLogEntity existingEntity = sysCustomerOperLogMapper.selectOne(
+                new QueryWrapper<SysCustomerOperLogEntity>()
+                        .lambda()
+                        .select(SysCustomerOperLogEntity::getId)
+                        .eq(SysCustomerOperLogEntity::getTitle, dto.getTitle())
+                        .last(HelioConstant.CRUD.SQL_LIMIT_1)
+        );
+
+        if (existingEntity != null && !existingEntity.getId().equals(dto.getId())) {
+            throw new BusinessException(400, "已存在相同客户操作日志信息数据,请重新输入");
+        }
+        */
+    }
+
+}

+ 216 - 0
src/main/java/com/jtzx/crm/module/sys/service/SysCustomerPersonService.java

@@ -0,0 +1,216 @@
+package com.jtzx.crm.module.sys.service;
+
+import cc.uncarbon.framework.core.constant.HelioConstant;
+import cc.uncarbon.framework.core.exception.BusinessException;
+import cc.uncarbon.framework.core.page.PageParam;
+import cc.uncarbon.framework.core.page.PageResult;
+import com.jtzx.crm.module.sys.entity.SysCustomerPersonEntity;
+import com.jtzx.crm.module.sys.mapper.SysCustomerPersonMapper;
+import com.jtzx.crm.module.sys.model.request.AdminSysCustomerPersonInsertOrUpdateDTO;
+import com.jtzx.crm.module.sys.model.request.AdminSysCustomerPersonListDTO;
+import com.jtzx.crm.module.sys.model.response.SysCustomerPersonBO;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.text.CharSequenceUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+
+
+/**
+ * 客户(学校)联系人数据
+ */
+@RequiredArgsConstructor
+@Service
+@Slf4j
+public class SysCustomerPersonService {
+
+    private final SysCustomerPersonMapper sysCustomerPersonMapper;
+
+
+    /**
+     * 后台管理-分页列表
+     */
+    public PageResult<SysCustomerPersonBO> adminList(PageParam pageParam, AdminSysCustomerPersonListDTO dto) {
+        Page<SysCustomerPersonEntity> entityPage = sysCustomerPersonMapper.selectPage(
+                new Page<>(pageParam.getPageNum(), pageParam.getPageSize()),
+                new QueryWrapper<SysCustomerPersonEntity>()
+                        .lambda()
+                        // 客户表主id
+                        .eq(Objects.nonNull(dto.getCustomerId()), SysCustomerPersonEntity::getCustomerId, dto.getCustomerId())
+                        // 姓名
+                        .like(CharSequenceUtil.isNotBlank(dto.getName()), SysCustomerPersonEntity::getName, CharSequenceUtil.cleanBlank(dto.getName()))
+                        // 职务id(param_base_data表主id)
+                        .eq(Objects.nonNull(dto.getPositionId()), SysCustomerPersonEntity::getPositionId, dto.getPositionId())
+                        // 职务名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getPositionName()), SysCustomerPersonEntity::getPositionName, CharSequenceUtil.cleanBlank(dto.getPositionName()))
+                        // 手机号
+                        .like(CharSequenceUtil.isNotBlank(dto.getPhoneNo()), SysCustomerPersonEntity::getPhoneNo, CharSequenceUtil.cleanBlank(dto.getPhoneNo()))
+                        // 办公电话
+                        .like(CharSequenceUtil.isNotBlank(dto.getOfficePhoneNo()), SysCustomerPersonEntity::getOfficePhoneNo, CharSequenceUtil.cleanBlank(dto.getOfficePhoneNo()))
+                        // 备注
+                        .like(CharSequenceUtil.isNotBlank(dto.getRemark()), SysCustomerPersonEntity::getRemark, CharSequenceUtil.cleanBlank(dto.getRemark()))
+                        // 时间区间
+                        .between(Objects.nonNull(dto.getBeginAt()) && Objects.nonNull(dto.getEndAt()), SysCustomerPersonEntity::getCreatedAt, dto.getBeginAt(), dto.getEndAt())
+                        // 排序
+                        .orderByDesc(SysCustomerPersonEntity::getCreatedAt)
+        );
+
+        return this.entityPage2BOPage(entityPage);
+    }
+
+    /**
+     * 根据 ID 取详情
+     *
+     * @param id 主键ID
+     * @return null or BO
+     */
+    public SysCustomerPersonBO getOneById(Long id) {
+        return this.getOneById(id, false);
+    }
+
+    /**
+     * 根据 ID 取详情
+     *
+     * @param id 主键ID
+     * @param throwIfInvalidId 是否在 ID 无效时抛出异常
+     * @return null or BO
+     */
+    public SysCustomerPersonBO getOneById(Long id, boolean throwIfInvalidId) throws BusinessException {
+        SysCustomerPersonEntity entity = sysCustomerPersonMapper.selectById(id);
+        if (throwIfInvalidId && entity == null) {
+            throw new BusinessException(400, "无效客户(学校)联系人数据ID");
+        }
+
+        return this.entity2BO(entity);
+    }
+
+    /**
+     * 后台管理-新增
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public Long adminInsert(AdminSysCustomerPersonInsertOrUpdateDTO dto) {
+        log.info("[后台管理-客户(学校)联系人数据-新增] >> DTO={}", dto);
+        this.checkExistence(dto);
+
+        dto.setId(null);
+        SysCustomerPersonEntity entity = new SysCustomerPersonEntity();
+        BeanUtil.copyProperties(dto, entity);
+
+        sysCustomerPersonMapper.insert(entity);
+
+        return entity.getId();
+    }
+
+    /**
+     * 后台管理-编辑
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public void adminUpdate(AdminSysCustomerPersonInsertOrUpdateDTO dto) {
+        log.info("[后台管理-客户(学校)联系人数据-编辑] >> DTO={}", dto);
+        this.checkExistence(dto);
+
+        SysCustomerPersonEntity entity = new SysCustomerPersonEntity();
+        BeanUtil.copyProperties(dto, entity);
+
+        sysCustomerPersonMapper.updateById(entity);
+    }
+
+    /**
+     * 后台管理-删除
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public void adminDelete(Collection<Long> ids) {
+        log.info("[后台管理-客户(学校)联系人数据-删除] >> ids={}", ids);
+        sysCustomerPersonMapper.deleteByIds(ids);
+    }
+
+
+    /*
+    ----------------------------------------------------------------
+                        私有方法 private methods
+    ----------------------------------------------------------------
+     */
+
+    /**
+     * 实体转 BO
+     *
+     * @param entity 实体
+     * @return BO
+     */
+    private SysCustomerPersonBO entity2BO(SysCustomerPersonEntity entity) {
+        if (entity == null) {
+            return null;
+        }
+
+        SysCustomerPersonBO bo = new SysCustomerPersonBO();
+        BeanUtil.copyProperties(entity, bo);
+
+        // 可以在此处为BO填充字段
+
+        return bo;
+    }
+
+    /**
+     * 实体 List 转 BO List
+     *
+     * @param entityList 实体 List
+     * @return BO List
+     */
+    private List<SysCustomerPersonBO> entityList2BOs(List<SysCustomerPersonEntity> entityList) {
+        if (CollUtil.isEmpty(entityList)) {
+            return Collections.emptyList();
+        }
+
+        // 深拷贝
+        List<SysCustomerPersonBO> ret = new ArrayList<>(entityList.size());
+        entityList.forEach(
+            entity -> ret.add(this.entity2BO(entity))
+        );
+
+        return ret;
+    }
+
+    /**
+     * 实体分页转 BO 分页
+     *
+     * @param entityPage 实体分页
+     * @return BO 分页
+     */
+    private PageResult<SysCustomerPersonBO> entityPage2BOPage(Page<SysCustomerPersonEntity> entityPage) {
+        return new PageResult<SysCustomerPersonBO>()
+            .setCurrent(entityPage.getCurrent())
+            .setSize(entityPage.getSize())
+            .setTotal(entityPage.getTotal())
+            .setRecords(this.entityList2BOs(entityPage.getRecords()));
+    }
+
+    /**
+     * 检查是否已存在同名数据
+     *
+     * @param dto DTO
+     */
+    private void checkExistence(AdminSysCustomerPersonInsertOrUpdateDTO dto) {
+        /*
+        可以根据自己业务需要,解禁这段代码,修改判断条件和文案
+
+        SysCustomerPersonEntity existingEntity = sysCustomerPersonMapper.selectOne(
+                new QueryWrapper<SysCustomerPersonEntity>()
+                        .lambda()
+                        .select(SysCustomerPersonEntity::getId)
+                        .eq(SysCustomerPersonEntity::getTitle, dto.getTitle())
+                        .last(HelioConstant.CRUD.SQL_LIMIT_1)
+        );
+
+        if (existingEntity != null && !existingEntity.getId().equals(dto.getId())) {
+            throw new BusinessException(400, "已存在相同客户(学校)联系人数据,请重新输入");
+        }
+        */
+    }
+
+}

+ 212 - 0
src/main/java/com/jtzx/crm/module/sys/service/SysCustomerTodoService.java

@@ -0,0 +1,212 @@
+package com.jtzx.crm.module.sys.service;
+
+import cc.uncarbon.framework.core.constant.HelioConstant;
+import cc.uncarbon.framework.core.exception.BusinessException;
+import cc.uncarbon.framework.core.page.PageParam;
+import cc.uncarbon.framework.core.page.PageResult;
+import com.jtzx.crm.module.sys.entity.SysCustomerTodoEntity;
+import com.jtzx.crm.module.sys.mapper.SysCustomerTodoMapper;
+import com.jtzx.crm.module.sys.model.request.AdminSysCustomerTodoInsertOrUpdateDTO;
+import com.jtzx.crm.module.sys.model.request.AdminSysCustomerTodoListDTO;
+import com.jtzx.crm.module.sys.model.response.SysCustomerTodoBO;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.text.CharSequenceUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+
+
+/**
+ * 自定义待办信息数据
+ */
+@RequiredArgsConstructor
+@Service
+@Slf4j
+public class SysCustomerTodoService {
+
+    private final SysCustomerTodoMapper sysCustomerTodoMapper;
+
+
+    /**
+     * 后台管理-分页列表
+     */
+    public PageResult<SysCustomerTodoBO> adminList(PageParam pageParam, AdminSysCustomerTodoListDTO dto) {
+        Page<SysCustomerTodoEntity> entityPage = sysCustomerTodoMapper.selectPage(
+                new Page<>(pageParam.getPageNum(), pageParam.getPageSize()),
+                new QueryWrapper<SysCustomerTodoEntity>()
+                        .lambda()
+                        // 认领人(跟进)id
+                        .eq(Objects.nonNull(dto.getClaimUserId()), SysCustomerTodoEntity::getClaimUserId, dto.getClaimUserId())
+                        // 认领人(跟进)名称
+                        .like(CharSequenceUtil.isNotBlank(dto.getClaimUserName()), SysCustomerTodoEntity::getClaimUserName, CharSequenceUtil.cleanBlank(dto.getClaimUserName()))
+                        // 认领人员所在的项目组id
+                        .eq(Objects.nonNull(dto.getClaimUserGroupId()), SysCustomerTodoEntity::getClaimUserGroupId, dto.getClaimUserGroupId())
+                        // 跟进(待办)内容
+                        .like(CharSequenceUtil.isNotBlank(dto.getFollowContent()), SysCustomerTodoEntity::getFollowContent, CharSequenceUtil.cleanBlank(dto.getFollowContent()))
+                        // 待办时间
+                        .eq(Objects.nonNull(dto.getOrderDatetime()), SysCustomerTodoEntity::getOrderDatetime, dto.getOrderDatetime())
+                        // 时间区间
+                        .between(Objects.nonNull(dto.getBeginAt()) && Objects.nonNull(dto.getEndAt()), SysCustomerTodoEntity::getCreatedAt, dto.getBeginAt(), dto.getEndAt())
+                        // 排序
+                        .orderByDesc(SysCustomerTodoEntity::getCreatedAt)
+        );
+
+        return this.entityPage2BOPage(entityPage);
+    }
+
+    /**
+     * 根据 ID 取详情
+     *
+     * @param id 主键ID
+     * @return null or BO
+     */
+    public SysCustomerTodoBO getOneById(Long id) {
+        return this.getOneById(id, false);
+    }
+
+    /**
+     * 根据 ID 取详情
+     *
+     * @param id 主键ID
+     * @param throwIfInvalidId 是否在 ID 无效时抛出异常
+     * @return null or BO
+     */
+    public SysCustomerTodoBO getOneById(Long id, boolean throwIfInvalidId) throws BusinessException {
+        SysCustomerTodoEntity entity = sysCustomerTodoMapper.selectById(id);
+        if (throwIfInvalidId && entity == null) {
+            throw new BusinessException(400, "无效自定义待办信息数据ID");
+        }
+
+        return this.entity2BO(entity);
+    }
+
+    /**
+     * 后台管理-新增
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public Long adminInsert(AdminSysCustomerTodoInsertOrUpdateDTO dto) {
+        log.info("[后台管理-自定义待办信息数据-新增] >> DTO={}", dto);
+        this.checkExistence(dto);
+
+        dto.setId(null);
+        SysCustomerTodoEntity entity = new SysCustomerTodoEntity();
+        BeanUtil.copyProperties(dto, entity);
+
+        sysCustomerTodoMapper.insert(entity);
+
+        return entity.getId();
+    }
+
+    /**
+     * 后台管理-编辑
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public void adminUpdate(AdminSysCustomerTodoInsertOrUpdateDTO dto) {
+        log.info("[后台管理-自定义待办信息数据-编辑] >> DTO={}", dto);
+        this.checkExistence(dto);
+
+        SysCustomerTodoEntity entity = new SysCustomerTodoEntity();
+        BeanUtil.copyProperties(dto, entity);
+
+        sysCustomerTodoMapper.updateById(entity);
+    }
+
+    /**
+     * 后台管理-删除
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public void adminDelete(Collection<Long> ids) {
+        log.info("[后台管理-自定义待办信息数据-删除] >> ids={}", ids);
+        sysCustomerTodoMapper.deleteByIds(ids);
+    }
+
+
+    /*
+    ----------------------------------------------------------------
+                        私有方法 private methods
+    ----------------------------------------------------------------
+     */
+
+    /**
+     * 实体转 BO
+     *
+     * @param entity 实体
+     * @return BO
+     */
+    private SysCustomerTodoBO entity2BO(SysCustomerTodoEntity entity) {
+        if (entity == null) {
+            return null;
+        }
+
+        SysCustomerTodoBO bo = new SysCustomerTodoBO();
+        BeanUtil.copyProperties(entity, bo);
+
+        // 可以在此处为BO填充字段
+
+        return bo;
+    }
+
+    /**
+     * 实体 List 转 BO List
+     *
+     * @param entityList 实体 List
+     * @return BO List
+     */
+    private List<SysCustomerTodoBO> entityList2BOs(List<SysCustomerTodoEntity> entityList) {
+        if (CollUtil.isEmpty(entityList)) {
+            return Collections.emptyList();
+        }
+
+        // 深拷贝
+        List<SysCustomerTodoBO> ret = new ArrayList<>(entityList.size());
+        entityList.forEach(
+            entity -> ret.add(this.entity2BO(entity))
+        );
+
+        return ret;
+    }
+
+    /**
+     * 实体分页转 BO 分页
+     *
+     * @param entityPage 实体分页
+     * @return BO 分页
+     */
+    private PageResult<SysCustomerTodoBO> entityPage2BOPage(Page<SysCustomerTodoEntity> entityPage) {
+        return new PageResult<SysCustomerTodoBO>()
+            .setCurrent(entityPage.getCurrent())
+            .setSize(entityPage.getSize())
+            .setTotal(entityPage.getTotal())
+            .setRecords(this.entityList2BOs(entityPage.getRecords()));
+    }
+
+    /**
+     * 检查是否已存在同名数据
+     *
+     * @param dto DTO
+     */
+    private void checkExistence(AdminSysCustomerTodoInsertOrUpdateDTO dto) {
+        /*
+        可以根据自己业务需要,解禁这段代码,修改判断条件和文案
+
+        SysCustomerTodoEntity existingEntity = sysCustomerTodoMapper.selectOne(
+                new QueryWrapper<SysCustomerTodoEntity>()
+                        .lambda()
+                        .select(SysCustomerTodoEntity::getId)
+                        .eq(SysCustomerTodoEntity::getTitle, dto.getTitle())
+                        .last(HelioConstant.CRUD.SQL_LIMIT_1)
+        );
+
+        if (existingEntity != null && !existingEntity.getId().equals(dto.getId())) {
+            throw new BusinessException(400, "已存在相同自定义待办信息数据,请重新输入");
+        }
+        */
+    }
+
+}

+ 711 - 0
src/main/java/com/jtzx/crm/module/sys/service/SysCustomerUserCommonService.java

@@ -0,0 +1,711 @@
+package com.jtzx.crm.module.sys.service;
+
+
+import cc.uncarbon.framework.core.context.UserContextHolder;
+import cc.uncarbon.framework.core.exception.BusinessException;
+import cn.hutool.core.collection.CollUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.google.common.collect.Lists;
+import com.jtzx.crm.module.sys.entity.*;
+import com.jtzx.crm.module.sys.mapper.*;
+import com.jtzx.crm.module.sys.model.response.BindUserBO;
+import com.jtzx.crm.module.sys.model.response.SelectBO;
+import com.jtzx.crm.module.sys.model.response.SysCustomerSelectBO;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.Comparator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+
+/**
+ * 查询用户绑定的省市,小组数据
+ */
+@RequiredArgsConstructor
+@Service
+@Slf4j
+public class SysCustomerUserCommonService {
+
+    private final DictProvinceRegionMapper dictProvinceRegionMapper;
+
+    private final DictCityRegionMapper dictCityRegionMapper;
+
+    private final SysParamBaseDataMapper sysParamBaseDataMapper;
+
+    private final SysUserGroupRelationMapper sysUserGroupRelationMapper;
+
+    private final SysGroupMapper sysGroupMapper;
+
+    private final SysUserMapper sysUserMapper;
+
+    private final SysUserRoleRelationMapper sysUserRoleRelationMapper;
+
+    /**
+     * 查询当前用户绑定的城市id集合,已处理权限(公海列表,我的客户页面查询列表使用),这两页面不需要区分小组长
+     * @return
+     */
+    public List<Long> queryUserCityIds(Long provinceId) {
+        List<Long> cityIds = Lists.newArrayList();
+        SysCustomerSelectBO selectBO = this.querySelectDataByType1(false, false);
+        if (CollUtil.isNotEmpty(selectBO.getRegionData())) {
+            selectBO.getRegionData().stream()
+                    .filter(x -> provinceId != null && provinceId.equals(x.getId()))
+                    .forEach(provinceRegion ->
+                            provinceRegion.getSubData()
+                                    .forEach(city -> cityIds.add(city.getId())));
+        }
+        return cityIds;
+    }
+
+
+    /**
+     * 查询当前用户绑定的城市id集合,已处理权限(客户跟进页面查询列表使用),这个页面需要区分小组长
+     * @return
+     */
+    public List<Long> queryUserCityIds1(Long provinceId) {
+        List<Long> cityIds = Lists.newArrayList();
+        SysCustomerSelectBO selectBO = this.querySelectDataByType2(false, false);
+        if (CollUtil.isNotEmpty(selectBO.getRegionData())) {
+            selectBO.getRegionData().stream()
+                    .filter(x -> provinceId != null && provinceId.equals(x.getId()))
+                    .forEach(provinceRegion ->
+                            provinceRegion.getSubData()
+                                    .forEach(city -> cityIds.add(city.getId())));
+        }
+        return cityIds;
+    }
+
+
+    /**
+     * 根据当前用户的一个(多个)城市id查询小组组长或全部小组绑定的人员信息,这个是带权限的,小组组长(绑定的小组下面的所有成员)或者管理员(所有组下面的成员)
+     * @param cityIds
+     * @return
+     */
+    public List<BindUserBO> queryBindUserListByCityId(List<Long> cityIds) {
+        //获取用户角色
+        List<String> roles = UserContextHolder.getUserContext().getRoles();
+        //管理员
+        if (roles.contains("Admin")) {
+            return this.queryBindUserListByCityIdOrGroupId(cityIds, null);
+        }
+        //小组组长
+        if (roles.contains("GroupManager")) {
+            List<SysUserRoleRelationEntity> userRoleRelationEntities =
+                    sysUserRoleRelationMapper.selectList(
+                            new LambdaQueryWrapper<>(SysUserRoleRelationEntity.class)
+                                    .select(
+                                            SysUserRoleRelationEntity::getGroupId,
+                                            SysUserRoleRelationEntity::getUserId
+                                    )
+                                    .eq(SysUserRoleRelationEntity::getUserId, UserContextHolder.getUserId())
+                                    .eq(SysUserRoleRelationEntity::getRoleValue, "GroupManager")
+                    );
+            if (CollUtil.isNotEmpty(userRoleRelationEntities)) {
+                List<Long> groupIds =
+                        userRoleRelationEntities.stream().map(SysUserRoleRelationEntity::getGroupId)
+                                .distinct().toList();
+                return this.queryBindUserListByCityIdOrGroupId(cityIds, groupIds);
+            }
+        }
+        //普通成员
+        if (roles.contains("Member")) {
+            List<SysUserGroupRelationEntity> groupRelationEntityList =
+                    sysUserGroupRelationMapper.selectList(
+                            new LambdaQueryWrapper<>(SysUserGroupRelationEntity.class)
+                                    .select(
+                                            SysUserGroupRelationEntity::getId,
+                                            SysUserGroupRelationEntity::getGroupId
+                                    )
+                                    .in(CollUtil.isNotEmpty(cityIds), SysUserGroupRelationEntity::getCityId, cityIds)
+                                    .eq(SysUserGroupRelationEntity::getUserId, UserContextHolder.getUserId())
+                                    .orderByAsc(SysUserGroupRelationEntity::getProvinceId)
+                    );
+            if (CollUtil.isNotEmpty(groupRelationEntityList)) {
+                List<Long> groupIds =
+                        groupRelationEntityList.stream()
+                                .map(SysUserGroupRelationEntity::getGroupId)
+                                .distinct().toList();
+                return this.queryBindUserListByCityIdOrGroupId(cityIds, groupIds);
+            }
+        }
+        return null;
+    }
+
+
+    /**
+     * 根据非当前用户一个(多个)城市id一个(多个)小组id查询绑定的人员信息,这个是不带权限的
+     * @param cityIds
+     * @param groupIds
+     * @return
+     */
+    public List<BindUserBO> queryBindUserListByCityIdOrGroupId(List<Long> cityIds, List<Long> groupIds) {
+
+        List<SysUserGroupRelationEntity> groupRelationEntityList =
+                sysUserGroupRelationMapper.selectList(
+                        new LambdaQueryWrapper<>(SysUserGroupRelationEntity.class)
+                                .select(
+                                        SysUserGroupRelationEntity::getId,
+                                        SysUserGroupRelationEntity::getGroupId,
+                                        SysUserGroupRelationEntity::getProvinceId,
+                                        SysUserGroupRelationEntity::getCityId,
+                                        SysUserGroupRelationEntity::getUserId
+                                )
+                                .in(CollUtil.isNotEmpty(cityIds), SysUserGroupRelationEntity::getCityId, cityIds)
+                                .in(CollUtil.isNotEmpty(groupIds), SysUserGroupRelationEntity::getGroupId, groupIds)
+                                .orderByAsc(SysUserGroupRelationEntity::getProvinceId)
+                );
+        List<BindUserBO> resultData = Lists.newArrayList();
+        if (CollUtil.isNotEmpty(groupRelationEntityList)) {
+            List<Long> userIds =
+                    groupRelationEntityList.stream()
+                            .map(SysUserGroupRelationEntity::getUserId)
+                            .distinct().toList();
+            List<SysUserEntity> sysUserEntityList =
+                    sysUserMapper.selectList(
+                            new LambdaQueryWrapper<>(SysUserEntity.class)
+                                    .select(
+                                            SysUserEntity::getId,
+                                            SysUserEntity::getUserName
+                                    )
+                                    .in(SysUserEntity::getId, userIds)
+                    );
+            groupRelationEntityList.forEach(e -> {
+                if (CollUtil.isNotEmpty(sysUserEntityList)) {
+                    sysUserEntityList.stream()
+                            .filter(x -> x.getId().equals(e.getUserId()))
+                            .findFirst()
+                            .ifPresent(user -> {
+                                BindUserBO bo = BindUserBO.builder().build();
+                                bo.setId(user.getId());
+                                bo.setUsername(user.getUserName());
+                                bo.setProvinceId(e.getProvinceId());
+                                bo.setCityId(e.getCityId());
+                                bo.setGroupId(e.getGroupId());
+                                resultData.add(bo);
+                            });
+                }
+            });
+        }
+        return resultData;
+    }
+
+
+    /**
+     * 查询顶部筛选条件的省,市,小组,人员数据,适用于公海客户,我的(私域)客户页面的顶部筛选条件的查询
+     * 根据身份权限查询用户的省市绑定用户信息(成员和小组组长权限相同),只区分管理员和非管理员权限
+     * @return
+     */
+    public SysCustomerSelectBO querySelectDataByType1(Boolean allStatus, Boolean userStatus) {
+        //获取用户角色
+        List<String> roles = UserContextHolder.getUserContext().getRoles();
+        if (CollUtil.isEmpty(roles)) {
+            return SysCustomerSelectBO.builder().build();
+        }
+        List<Long> provinceIds = Lists.newArrayList();
+        List<Long> cityIds = Lists.newArrayList();
+        List<Long> groupIds = Lists.newArrayList();
+        //管理员
+        if (roles.contains("Admin")) {
+            return this.handleAdminRole(provinceIds, cityIds, groupIds, allStatus, userStatus);
+        }
+        //普通成员或小组组长
+        if (roles.contains("Member") || roles.contains("GroupManager")) {
+            return this.handleMemberRole(provinceIds, cityIds, groupIds, allStatus, userStatus);
+        }
+        return SysCustomerSelectBO.builder().build();
+    }
+
+
+    /**
+     * 查询顶部筛选条件的省,市,小组,人员数据,适用于 客户跟进 页面的顶部筛选条件的查询
+     * 根据身份权限查询用户的省市绑定用户信息,区分管理员,小组组长,普通成员角色权限
+     * @return
+     */
+    public SysCustomerSelectBO querySelectDataByType2(Boolean allStatus, Boolean userStatus) {
+        //获取用户角色
+        List<String> roles = UserContextHolder.getUserContext().getRoles();
+        if (CollUtil.isEmpty(roles)) {
+            throw new BusinessException(400, "无权查看");
+        }
+        List<Long> provinceIds = Lists.newArrayList();
+        List<Long> cityIds = Lists.newArrayList();
+        List<Long> groupIds = Lists.newArrayList();
+        //管理员
+        if (roles.contains("Admin")) {
+            return this.handleAdminRole(provinceIds, cityIds, groupIds, allStatus, userStatus);
+        }
+        //小组组长
+        if (roles.contains("GroupManager")) {
+            return this.handeGroupManagerRole(provinceIds, cityIds, allStatus, userStatus);
+        }
+        //普通成员
+        if (roles.contains("Member")) {
+            return this.handleMemberRole(provinceIds, cityIds, groupIds, allStatus, userStatus);
+        }
+        return SysCustomerSelectBO.builder().build();
+    }
+
+
+    /**
+     * 学段信息
+     * @return
+     */
+    public List<SelectBO> handleStageData() {
+        List<SelectBO> stageData = Lists.newArrayList();
+        //查询所有学段信息
+        List<SysParamBaseDataEntity> stageDataList = this.queryParamBaseData(0);
+        if (CollUtil.isNotEmpty(stageDataList)) {
+            SelectBO all = SelectBO.builder().build();
+            all.setName("全部");
+            all.setSortCode(0);
+            all.setSelectStatus(true);
+            stageData.add(all);
+            stageDataList.forEach(e -> {
+                SelectBO bo = SelectBO.builder().build();
+                bo.setId(e.getId());
+                bo.setName(e.getTitle());
+                bo.setSortCode(e.getSortCode());
+                bo.setSelectStatus(false);
+                stageData.add(bo);
+            });
+        }
+        return stageData;
+    }
+
+    /**
+     * 产品信息
+     * @return
+     */
+    public List<SelectBO> handleProductData() {
+        List<SelectBO> stageData = Lists.newArrayList();
+        //查询所有学段信息
+        List<SysParamBaseDataEntity> stageDataList = this.queryParamBaseData(1);
+        if (CollUtil.isNotEmpty(stageDataList)) {
+            SelectBO all = SelectBO.builder().build();
+            all.setName("全部");
+            all.setSortCode(0);
+            all.setSelectStatus(true);
+            stageData.add(all);
+            stageDataList.forEach(e -> {
+                SelectBO bo = SelectBO.builder().build();
+                bo.setId(e.getId());
+                bo.setName(e.getTitle());
+                bo.setSortCode(e.getSortCode());
+                bo.setSelectStatus(false);
+                stageData.add(bo);
+            });
+        }
+        return stageData;
+    }
+
+
+    /**
+     * 类型信息,首单客户,存量客户
+     * @return
+     */
+    public List<SelectBO> handleTypeData() {
+        List<SelectBO> typeData = Lists.newArrayList();
+        //查询所有类型信息
+        SelectBO all = SelectBO.builder().build();
+        all.setName("全部");
+        all.setSortCode(0);
+        all.setSelectStatus(true);
+        typeData.add(all);
+
+        SelectBO firstBO = SelectBO.builder().build();
+        firstBO.setId(1L);
+        firstBO.setName("首单客户");
+        firstBO.setSortCode(1);
+        firstBO.setSelectStatus(false);
+        typeData.add(firstBO);
+
+        SelectBO selectBO = SelectBO.builder().build();
+        selectBO.setId(2L);
+        selectBO.setName("存量客户");
+        selectBO.setSortCode(2);
+        selectBO.setSelectStatus(false);
+        typeData.add(selectBO);
+        return typeData;
+    }
+
+
+    //------------------------------------------------------------------------------------------------------------------
+
+
+    /**
+     * 处理管理员角色
+     * @param provinceIds
+     * @param cityIds
+     * @param groupIds
+     * @return
+     */
+    public SysCustomerSelectBO handleAdminRole(List<Long> provinceIds,
+                                               List<Long> cityIds,
+                                               List<Long> groupIds,
+                                               Boolean allStatus,
+                                               Boolean userStatus) {
+
+        List<DictProvinceRegionEntity> provinceRegionEntityList =
+                dictProvinceRegionMapper.selectList(
+                        new LambdaQueryWrapper<>(DictProvinceRegionEntity.class)
+                                .select(
+                                        DictProvinceRegionEntity::getId,
+                                        DictProvinceRegionEntity::getCode
+                                )
+                                .eq(DictProvinceRegionEntity::getUseStatus, 1)
+                );
+        if (CollUtil.isNotEmpty(provinceRegionEntityList)) {
+            this.handleAdmin(provinceRegionEntityList, provinceIds, cityIds, groupIds);
+            return this.handleResultData(provinceIds, cityIds, groupIds, allStatus, userStatus);
+        }
+        return SysCustomerSelectBO.builder().build();
+    }
+
+    /**
+     * 处理小组组长角色
+     * @param provinceIds
+     * @param cityIds
+     * @return
+     */
+    public SysCustomerSelectBO handeGroupManagerRole(List<Long> provinceIds,
+                                                     List<Long> cityIds,
+                                                     Boolean allStatus,
+                                                     Boolean userStatus) {
+        List<SysUserRoleRelationEntity> groupManagerRelationEntityList =
+                sysUserRoleRelationMapper.selectList(
+                        new LambdaQueryWrapper<>(SysUserRoleRelationEntity.class)
+                                .select(
+                                        SysUserRoleRelationEntity::getUserId,
+                                        SysUserRoleRelationEntity::getGroupId
+                                )
+                                .eq(SysUserRoleRelationEntity::getUserId, UserContextHolder.getUserId())//当前登陆的用户id
+                                .eq(SysUserRoleRelationEntity::getRoleValue, "GroupManager")
+                );
+        if (CollUtil.isNotEmpty(groupManagerRelationEntityList)) {
+            List<Long> groupIds =
+                    groupManagerRelationEntityList.stream()
+                            .map(SysUserRoleRelationEntity::getGroupId)
+                            .distinct().toList();
+            //开通的省市都有相同的小组,所有这里查询开通的省市查询使用管理员的查询
+            return this.handleAdminRole(provinceIds, cityIds, groupIds, allStatus, userStatus);
+        }
+        return SysCustomerSelectBO.builder().build();
+    }
+
+    /**
+     * 处理普通成员角色
+     * @param provinceIds
+     * @param cityIds
+     * @param groupIds
+     * @return
+     */
+    public SysCustomerSelectBO handleMemberRole(List<Long> provinceIds,
+                                                List<Long> cityIds,
+                                                List<Long> groupIds,
+                                                Boolean allStatus,
+                                                Boolean userStatus) {
+        List<SysUserGroupRelationEntity> userGroupRelationEntityList =
+                sysUserGroupRelationMapper.selectList(
+                        new LambdaQueryWrapper<>(SysUserGroupRelationEntity.class)
+                                .select(
+                                        SysUserGroupRelationEntity::getGroupId,
+                                        SysUserGroupRelationEntity::getProvinceId,
+                                        SysUserGroupRelationEntity::getCityId
+                                )
+                                .eq(SysUserGroupRelationEntity::getUserId, UserContextHolder.getUserId()) //当前登陆的用户id
+                                .orderByAsc(SysUserGroupRelationEntity::getProvinceId)
+                );
+        if (CollUtil.isNotEmpty(userGroupRelationEntityList)) {
+            this.handleMemberData(userGroupRelationEntityList, provinceIds, cityIds, groupIds);
+            return this.handleResultData(provinceIds, cityIds, groupIds, allStatus, userStatus);
+        }
+        return SysCustomerSelectBO.builder().build();
+    }
+
+
+    /**
+     * 处理省市和和成员,小组
+     * @param provinceIds
+     * @param cityIds
+     * @param groupIds
+     */
+    public SysCustomerSelectBO handleResultData(List<Long> provinceIds,
+                                                List<Long> cityIds,
+                                                List<Long> groupIds,
+                                                Boolean allStatus, Boolean userStatus) {
+
+        SysCustomerSelectBO selectBO = SysCustomerSelectBO.builder().build();
+        //小组
+        selectBO.setGroupData(this.handeGroupData(groupIds, allStatus));
+
+        List<SysUserEntity> userEntityList =
+                sysUserMapper.selectList(
+                        new LambdaQueryWrapper<>(SysUserEntity.class)
+                                .eq(SysUserEntity::getStatus, 1)
+                );
+        //省市
+        List<SelectBO> regionData = this.handleRegionData(provinceIds, cityIds, allStatus);
+        //省市
+        selectBO.setRegionData(regionData);
+        //成员
+        List<BindUserBO> userData = Lists.newArrayList();
+
+        List<SysUserGroupRelationEntity> groupRelationEntityList =
+                sysUserGroupRelationMapper.selectList(
+                        new LambdaQueryWrapper<>(SysUserGroupRelationEntity.class)
+                                .select(
+                                        SysUserGroupRelationEntity::getGroupId,
+                                        SysUserGroupRelationEntity::getProvinceId,
+                                        SysUserGroupRelationEntity::getCityId,
+                                        SysUserGroupRelationEntity::getUserId
+                                )
+                                .in(SysUserGroupRelationEntity::getProvinceId, provinceIds)
+                                .in(SysUserGroupRelationEntity::getCityId, cityIds)
+                                .orderByAsc(SysUserGroupRelationEntity::getProvinceId)
+                );
+        if (userStatus) {
+            regionData.forEach(province ->
+                    province.getSubData().forEach(city -> {
+                        if (CollUtil.isNotEmpty(groupIds) && CollUtil.isNotEmpty(groupRelationEntityList)) {
+
+                            groupRelationEntityList.stream()
+                                    .filter(x -> x.getProvinceId().equals(province.getId()))
+                                    .filter(x -> x.getCityId().equals(city.getId()))
+                                    .filter(x -> CollUtil.isNotEmpty(groupIds) && groupIds.contains(x.getGroupId()))
+                                    .sorted(Comparator.comparing(SysUserGroupRelationEntity::getCreatedAt))
+                                    .collect(Collectors.groupingBy(SysUserGroupRelationEntity::getGroupId, LinkedHashMap::new, Collectors.toList()))
+                                    .forEach((key, value) -> {
+                                        if (allStatus) {
+                                            BindUserBO userBO = BindUserBO.builder().build();
+                                            userBO.setProvinceId(province.getId());
+                                            userBO.setCityId(city.getId());
+                                            userBO.setGroupId(key);
+                                            userBO.setUsername("全部");
+                                            userBO.setSortCode(0);
+                                            userBO.setSelectStatus(true);
+                                            userData.add(userBO);
+                                        }
+                                        AtomicInteger sortCode = new AtomicInteger(0);
+                                        value.forEach(user -> {
+                                            if (CollUtil.isNotEmpty(userEntityList)) {
+                                                userEntityList.stream()
+                                                        .filter(x -> x.getId().equals(user.getUserId()))
+                                                        .findFirst()
+                                                        .ifPresent(e -> {
+                                                            BindUserBO userBO = BindUserBO.builder().build();
+                                                            userBO.setId(user.getUserId());
+                                                            userBO.setProvinceId(user.getProvinceId());
+                                                            userBO.setCityId(user.getCityId());
+                                                            userBO.setGroupId(user.getGroupId());
+                                                            userBO.setUsername(e.getUserName());
+                                                            userBO.setSelectStatus(false);
+                                                            userBO.setSortCode(sortCode.incrementAndGet());
+                                                            userData.add(userBO);
+                                                        });
+                                            }
+                                        });
+                                    });
+                        }
+                    }));
+        }
+        selectBO.setMemberData(userData);
+        return selectBO;
+    }
+
+
+    /**
+     * 处理普通成员数据
+     * @param provinceIds
+     * @param cityIds
+     * @param groupIds
+     */
+    public void handleMemberData(List<SysUserGroupRelationEntity> userGroupRelationEntityList,
+                                 List<Long> provinceIds,
+                                 List<Long> cityIds,
+                                 List<Long> groupIds) {
+
+        //普通成员没有配置小组,所有的id集合都是空
+        if (CollUtil.isNotEmpty(userGroupRelationEntityList)) {
+            if (provinceIds != null) {
+                provinceIds.addAll(
+                        userGroupRelationEntityList.stream()
+                                .map(SysUserGroupRelationEntity::getProvinceId)
+                                .filter(Objects::nonNull)
+                                .distinct().toList());
+            }
+            if (cityIds != null) {
+                cityIds.addAll(
+                        userGroupRelationEntityList.stream()
+                                .map(SysUserGroupRelationEntity::getCityId)
+                                .filter(Objects::nonNull)
+                                .distinct().toList());
+            }
+            if (groupIds != null) {
+                groupIds.addAll(
+                        userGroupRelationEntityList.stream()
+                                .map(SysUserGroupRelationEntity::getGroupId)
+                                .filter(Objects::nonNull)
+                                .distinct().toList());
+            }
+        }
+    }
+
+
+    /**
+     * 处理管理员角色数据
+     * @param provinceIds
+     * @param cityIds
+     * @param groupIds
+     */
+    public void handleAdmin(List<DictProvinceRegionEntity> provinceRegionEntityList,
+                            List<Long> provinceIds,
+                            List<Long> cityIds,
+                            List<Long> groupIds) {
+
+
+        //如果没有添加小组,管理员查询,可能小组id也为空
+        if (groupIds != null) {
+            List<SysGroupEntity> groupEntityList = sysGroupMapper.selectList(
+                    new LambdaQueryWrapper<>(SysGroupEntity.class)
+                            .orderByAsc(SysGroupEntity::getSortCode)
+            );
+            if (CollUtil.isNotEmpty(groupEntityList)) {
+                groupIds.addAll(
+                        groupEntityList.stream().map(SysGroupEntity::getId).toList());
+            }
+        }
+        List<String> provinceCodes = provinceRegionEntityList.stream()
+                .map(DictProvinceRegionEntity::getCode).toList();
+        if (provinceIds != null) {
+            provinceIds.addAll(provinceRegionEntityList.stream()
+                    .map(DictProvinceRegionEntity::getId).toList());
+        }
+
+        List<DictCityRegionEntity> cityRegionEntityList =
+                dictCityRegionMapper.selectList(
+                        new LambdaQueryWrapper<>(DictCityRegionEntity.class)
+                                .select(
+                                        DictCityRegionEntity::getId
+                                )
+                                .in(DictCityRegionEntity::getParentCode, provinceCodes)
+                );
+        if (cityIds != null) {
+            cityIds.addAll(cityRegionEntityList.stream()
+                    .map(DictCityRegionEntity::getId).toList());
+        }
+    }
+
+
+    /**
+     * 处理小组
+     * @param groupIds
+     * @return
+     */
+    public List<SelectBO> handeGroupData(List<Long> groupIds,
+                                         Boolean allStatus) {
+        List<SelectBO> groupData = Lists.newArrayList();
+        //小组
+        if (CollUtil.isNotEmpty(groupIds)) {
+            if (allStatus) {
+                SelectBO selectBO = SelectBO.builder()
+                        .name("全部")
+                        .sortCode(0)
+                        .selectStatus(true)
+                        .build();
+                groupData.add(selectBO);
+            }
+            List<SysGroupEntity> groupEntityList = sysGroupMapper.selectByIds(groupIds);
+            if (CollUtil.isNotEmpty(groupEntityList)) {
+                groupEntityList.stream()
+                        .sorted(Comparator.comparing(SysGroupEntity::getSortCode))
+                        .forEach(group -> {
+                            SelectBO groupBO = SelectBO.builder().build();
+                            groupBO.setId(group.getId());
+                            groupBO.setName(group.getTitle());
+                            groupBO.setSortCode(group.getSortCode());
+                            groupBO.setSelectStatus(false);
+                            groupData.add(groupBO);
+                        });
+            }
+        }
+        return groupData;
+    }
+
+
+    /**
+     * 处理省市
+     * @param provinceIds
+     * @param cityIds
+     * @return
+     */
+    public List<SelectBO> handleRegionData(List<Long> provinceIds,
+                                           List<Long> cityIds,
+                                           Boolean allStatus) {
+        List<SelectBO> regionData = Lists.newArrayList();
+        //省份
+        List<DictProvinceRegionEntity> regionEntityList = dictProvinceRegionMapper.selectByIds(provinceIds);
+        //城市
+        List<DictCityRegionEntity> dictCityRegionEntities = dictCityRegionMapper.selectByIds(cityIds);
+        regionEntityList.stream()
+                .sorted(Comparator.comparing(DictProvinceRegionEntity::getSortCode))
+                .forEach(province -> {
+                    SelectBO selectBO = SelectBO.builder()
+                            .name("全部")
+                            .sortCode(0)
+                            .selectStatus(true)
+                            .build();
+                    if (allStatus) {
+                        regionData.add(selectBO);
+                    }
+
+                    SelectBO provinceBO = SelectBO.builder().build();
+                    provinceBO.setId(province.getId());
+                    provinceBO.setName(province.getName());
+                    provinceBO.setSortCode(province.getSortCode());
+                    provinceBO.setSelectStatus(false);
+                    List<SelectBO> subData = Lists.newArrayList();
+                    AtomicInteger sortCode = new AtomicInteger(0);
+                    dictCityRegionEntities.stream()
+                            .filter(x -> x.getParentCode().equals(province.getCode()))
+                            .sorted(Comparator.comparing(DictCityRegionEntity::getCode))
+                            .forEach(city -> {
+                                if (allStatus) {
+                                    subData.add(selectBO);
+                                }
+                                SelectBO cityBo = SelectBO.builder().build();
+                                cityBo.setId(city.getId());
+                                cityBo.setName(city.getName());
+                                cityBo.setSortCode(sortCode.incrementAndGet());
+                                cityBo.setSelectStatus(false);
+                                subData.add(cityBo);
+                            });
+                    provinceBO.setSubData(subData);
+                    regionData.add(provinceBO);
+                });
+        return regionData;
+    }
+
+
+    /**
+     * 查询参数配置的基础信息数据
+     * @param type 类型:0-学段,1-产品,2-职务,3-跟进
+     * @return
+     */
+    public List<SysParamBaseDataEntity> queryParamBaseData(Integer type) {
+        //查询所有在基础配置信息
+        return
+                sysParamBaseDataMapper.selectList(
+                        new LambdaQueryWrapper<>(SysParamBaseDataEntity.class)
+                                .select(
+                                        SysParamBaseDataEntity::getId,
+                                        SysParamBaseDataEntity::getTitle
+                                )
+                                .eq(SysParamBaseDataEntity::getType, type)
+                                .orderByAsc(SysParamBaseDataEntity::getSortCode)
+                );
+    }
+}

+ 6 - 85
src/main/java/com/jtzx/crm/module/sys/service/SysRoleService.java

@@ -19,7 +19,6 @@ import com.jtzx.crm.module.sys.enums.SysErrorEnum;
 import com.jtzx.crm.module.sys.mapper.SysRoleMapper;
 import com.jtzx.crm.module.sys.mapper.SysUserRoleRelationMapper;
 import com.jtzx.crm.module.sys.model.interior.UserRoleContainer;
-import com.jtzx.crm.module.sys.model.request.AdminBindRoleMenuRelationDTO;
 import com.jtzx.crm.module.sys.model.request.AdminInsertOrUpdateSysRoleDTO;
 import com.jtzx.crm.module.sys.model.request.AdminListSysRoleDTO;
 import com.jtzx.crm.module.sys.model.response.SysRoleBO;
@@ -43,7 +42,6 @@ public class SysRoleService {
     private final SysRoleMapper sysRoleMapper;
     private final SysUserRoleRelationMapper sysUserRoleRelationMapper;
     private final SysRoleMenuRelationService sysRoleMenuRelationService;
-    private final SysMenuService sysMenuService;
 
 
     /**
@@ -88,7 +86,7 @@ public class SysRoleService {
             SysErrorEnum.INVALID_ID.assertNotNull(entity);
         }
 
-        return this.entity2BO(entity, true);
+        return this.entity2BO(entity);
     }
 
     /**
@@ -135,56 +133,9 @@ public class SysRoleService {
         sysRoleMapper.deleteByIds(ids);
     }
 
-    /**
-     * 后台管理-绑定角色与菜单关联关系
-     *
-     * @return 新菜单ID集合对应的权限名
-     */
-    @Transactional(rollbackFor = Exception.class)
-    public Set<String> adminBindMenus(AdminBindRoleMenuRelationDTO dto) {
-        preBindRoleMenuRelationCheck(dto);
-        Set<String> newPermissions = sysMenuService.listPermissionsByMenuIds(dto.getMenuIds());
-        sysRoleMenuRelationService.cleanAndBind(dto.getRoleId(), dto.getMenuIds());
 
-        return newPermissions;
-    }
 
-    /**
-     * 后台管理-下拉框数据
-     */
-    public List<SysRoleBO> adminSelectOptions() {
-        List<SysRoleEntity> entityList = sysRoleMapper.selectList(
-                new QueryWrapper<SysRoleEntity>()
-                        .lambda()
-                        // 只取特定字段
-                        .select(SysRoleEntity::getId, SysRoleEntity::getTitle)
-                        // 排序
-                        .orderByAsc(SysRoleEntity::getId)
-        );
-        // 无需填充菜单IDs
-        return entityList2BOs(entityList, false);
-    }
-
-    /**
-     * 后台管理-删除指定租户的特定角色
-     * @param tenantIds 租户IDs,非主键ID,不能为空
-     * @param roleValues 角色值集合,可以为空
-     */
-    @Transactional(rollbackFor = Exception.class)
-    public void adminDeleteTenantRoles(Collection<Long> tenantIds, Collection<String> roleValues) {
-        if (CollUtil.isEmpty(tenantIds)) {
-            return;
-        }
 
-        sysRoleMapper.delete(
-                new QueryWrapper<SysRoleEntity>()
-                        .lambda()
-                        // 租户ID
-                        .in(SysRoleEntity::getTenantId, tenantIds)
-                        // 值相符
-                        .in(CollUtil.isNotEmpty(roleValues), SysRoleEntity::getValue, roleValues)
-        );
-    }
 
     /**
      * 取用户ID拥有角色对应的 角色ID-角色名 map
@@ -203,7 +154,6 @@ public class SysRoleService {
                                 )
                                 .eq(SysUserRoleRelationEntity::getUserId, userId)
                 );
-
         if (CollUtil.isEmpty(relationEntityList)) {
             return Collections.emptyMap();
         }
@@ -257,10 +207,9 @@ public class SysRoleService {
      * 实体转 BO
      *
      * @param entity 实体
-     * @param fillMenuIds 是否根据实体ID,查询关联菜单IDs并填充到BO
      * @return BO
      */
-    private SysRoleBO entity2BO(SysRoleEntity entity, boolean fillMenuIds) {
+    private SysRoleBO entity2BO(SysRoleEntity entity) {
         if (entity == null) {
             return null;
         }
@@ -269,9 +218,7 @@ public class SysRoleService {
         BeanUtil.copyProperties(entity, bo);
 
         // 可以在此处为BO填充字段
-        if (fillMenuIds) {
-            bo.setMenuIds(sysRoleMenuRelationService.listMenuIdsByRoleIds(Collections.singleton(bo.getId())));
-        }
+        bo.setMenuIds(sysRoleMenuRelationService.listMenuIdsByRoleIds(Collections.singleton(bo.getId())));
         return bo;
     }
 
@@ -279,14 +226,13 @@ public class SysRoleService {
      * 实体 List 转 BO List
      *
      * @param entityList 实体 List
-     * @param fillMenuIds 是否根据实体ID,查询关联菜单IDs并填充到BO
      * @return BO List
      */
-    private List<SysRoleBO> entityList2BOs(List<SysRoleEntity> entityList, boolean fillMenuIds) {
+    private List<SysRoleBO> entityList2BOs(List<SysRoleEntity> entityList) {
         // 深拷贝
         List<SysRoleBO> ret = new ArrayList<>(entityList.size());
         entityList.forEach(
-                entity -> ret.add(this.entity2BO(entity, fillMenuIds))
+                entity -> ret.add(this.entity2BO(entity))
         );
 
         return ret;
@@ -304,7 +250,7 @@ public class SysRoleService {
                 .setSize(entityPage.getSize())
                 .setTotal(entityPage.getTotal())
                 // 需填充菜单IDs
-                .setRecords(this.entityList2BOs(entityPage.getRecords(), true));
+                .setRecords(this.entityList2BOs(entityPage.getRecords()));
     }
 
     /**
@@ -368,29 +314,4 @@ public class SysRoleService {
             throw new BusinessException(SysErrorEnum.CANNOT_DELETE_SELF_ROLE);
         }
     }
-
-    /**
-     * 绑定后台角色与菜单关联关系前检查
-     * 防止越权访问漏洞
-     */
-    private void preBindRoleMenuRelationCheck(AdminBindRoleMenuRelationDTO dto) {
-        UserRoleContainer currentUser = getCurrentUserRoleContainer();
-        if (SysConstant.SUPER_ADMIN_ROLE_ID.equals(dto.getRoleId())) {
-            throw new BusinessException(SysErrorEnum.CANNOT_BIND_MENUS_FOR_SUPER_ADMIN_ROLE);
-        }
-
-        if (CollUtil.contains(currentUser.getRelatedRoleIds(), dto.getRoleId())) {
-            // 不能动自身角色
-            throw new BusinessException(SysErrorEnum.CANNOT_BIND_MENUS_FOR_SELF);
-        }
-
-        if (CollUtil.isNotEmpty(dto.getMenuIds()) && !currentUser.isAdmin()) {
-            // 超级管理员之外的角色,都需要校验自身菜单范围是否满足输入值
-            Set<Long> visibleMenuIds = sysRoleMenuRelationService.listMenuIdsByRoleIds(currentUser.getRelatedRoleIds());
-            if (!CollUtil.containsAll(visibleMenuIds, dto.getMenuIds())) {
-                // 可能存在超自身权限赋权
-                throw new BusinessException(SysErrorEnum.BEYOND_AUTHORITY_BIND_MENUS);
-            }
-        }
-    }
 }

+ 27 - 1
src/main/java/com/jtzx/crm/module/sys/service/SysUserGroupRelationService.java

@@ -10,9 +10,11 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
+import com.jtzx.crm.config.AsyncVThreadConfiguration;
 import com.jtzx.crm.module.sys.entity.*;
 import com.jtzx.crm.module.sys.mapper.*;
 import com.jtzx.crm.module.sys.model.request.AdminSysUserGroupRelationDTO;
+import com.jtzx.crm.module.sys.model.request.UserRoleRelationInsertDTO;
 import com.jtzx.crm.module.sys.model.response.*;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -43,6 +45,9 @@ public class SysUserGroupRelationService {
 
     private final SysUserMapper sysUserMapper;
 
+    private final SysUserRoleRelationService sysUserRoleRelationService;
+
+    private final AsyncVThreadConfiguration asyncVThreadConfiguration;
 
     /**
      * 查询所有用户(左侧列表数据)
@@ -379,7 +384,18 @@ public class SysUserGroupRelationService {
         SysUserGroupRelationEntity entity = new SysUserGroupRelationEntity();
         BeanUtil.copyProperties(dto, entity);
         sysUserGroupRelationMapper.insert(entity);
+        //添加普通成员角色
+        UserRoleRelationInsertDTO userRoleRelationDTO = UserRoleRelationInsertDTO.builder().build();
+        userRoleRelationDTO.setUserId(dto.getUserId());
+        userRoleRelationDTO.setRoleId(3L);
+        userRoleRelationDTO.setGroupId(dto.getGroupId());
+        sysUserRoleRelationService.adminInsert(userRoleRelationDTO);
+        //异步执行更新用户角色问题
+        asyncVThreadConfiguration.getAsyncExecutor().execute(() ->
+                sysUserRoleRelationService.updateUserRole(dto.getUserId()));
         return entity.getId();
+
+
     }
 
     /**
@@ -388,7 +404,17 @@ public class SysUserGroupRelationService {
     @Transactional(rollbackFor = Exception.class)
     public void adminDelete(Long id) {
         log.info("[后台管理-数据表注释-删除] >> id={}", id);
-        sysUserGroupRelationMapper.deleteById(id);
+        SysUserGroupRelationEntity sysUserGroupRelationEntity = sysUserGroupRelationMapper.selectById(id);
+        if (sysUserGroupRelationEntity != null) {
+            sysUserGroupRelationMapper.deleteById(id);
+            //删除普通成员角色
+            sysUserRoleRelationService.delete(sysUserGroupRelationEntity.getUserId(),
+                    sysUserGroupRelationEntity.getGroupId(), "Member");
+            //异步执行更新用户角色问题
+            asyncVThreadConfiguration.getAsyncExecutor().execute(() ->
+                    sysUserRoleRelationService.updateUserRole(sysUserGroupRelationEntity.getUserId()));
+        }
+
     }
 
 

+ 87 - 5
src/main/java/com/jtzx/crm/module/sys/service/SysUserRoleRelationService.java

@@ -1,5 +1,6 @@
 package com.jtzx.crm.module.sys.service;
 
+import cc.uncarbon.framework.core.context.UserContextHolder;
 import cc.uncarbon.framework.core.exception.BusinessException;
 import cc.uncarbon.framework.core.page.PageResult;
 import cc.uncarbon.framework.crud.entity.HelioBaseEntity;
@@ -8,6 +9,8 @@ import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.google.common.collect.Lists;
+import com.jtzx.crm.config.AsyncVThreadConfiguration;
+import com.jtzx.crm.module.api.util.CrmAdminStpUtil;
 import com.jtzx.crm.module.sys.entity.SysGroupEntity;
 import com.jtzx.crm.module.sys.entity.SysRoleEntity;
 import com.jtzx.crm.module.sys.entity.SysUserEntity;
@@ -25,6 +28,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.util.Comparator;
+import java.util.HashSet;
 import java.util.List;
 
 
@@ -44,12 +48,54 @@ public class SysUserRoleRelationService {
 
     private final SysGroupMapper sysGroupMapper;
 
+    private final AsyncVThreadConfiguration asyncVThreadConfiguration;
+
+    /**
+     * 更新用户redis中的角色信息
+     * @param userId
+     */
+    public void updateUserRole(Long userId) {
+
+        try {
+            Thread.sleep(200); // 当前线程休眠 200 毫秒
+            List<String> roleValueLists = Lists.newArrayList();
+            List<Long> roleIdList = Lists.newArrayList();
+            List<SysUserRoleRelationEntity> roleRelationEntityList =
+                    sysUserRoleRelationMapper.selectList(
+                            new LambdaQueryWrapper<>(SysUserRoleRelationEntity.class)
+                                    .select(
+                                            SysUserRoleRelationEntity::getRoleValue,
+                                            SysUserRoleRelationEntity::getRoleId
+                                    )
+                                    .eq(SysUserRoleRelationEntity::getUserId, userId)
+                    );
+            if (CollUtil.isNotEmpty(roleRelationEntityList)) {
+                roleValueLists.addAll(
+                        roleRelationEntityList.stream()
+                                .map(SysUserRoleRelationEntity::getRoleValue)
+                                .toList());
+
+                roleIdList.addAll(
+                        roleRelationEntityList.stream()
+                                .map(SysUserRoleRelationEntity::getRoleId)
+                                .toList());
+            }
+            UserContextHolder.getUserContext().setRoles(roleValueLists.stream().distinct().toList());
+            UserContextHolder.getUserContext().setRolesIds(new HashSet<>(roleIdList));
+        } catch (InterruptedException e) {
+            log.error(e.getMessage());
+        }
+    }
+
     /**
      * 查询角色下拉数据列表
      * @return
      */
     public List<SysRoleListBO> queryRoleData() {
-        List<SysRoleEntity> sysRoleEntities = sysRoleMapper.selectList(null);
+        List<SysRoleEntity> sysRoleEntities = sysRoleMapper.selectList(
+                new LambdaQueryWrapper<>(SysRoleEntity.class)
+                        .ne(SysRoleEntity::getValue, "Member")
+        );
         List<SysRoleListBO> result = Lists.newArrayList();
         sysRoleEntities.stream()
                 .sorted(Comparator.comparing(SysRoleEntity::getSortCode).reversed())
@@ -79,6 +125,7 @@ public class SysUserRoleRelationService {
                                         SysRoleEntity::getTitle
                                 )
                                 .eq(dto.getRoleId() != null, SysRoleEntity::getId, dto.getRoleId())
+                                .ne(SysRoleEntity::getValue, "Member")
                 );
         if (CollUtil.isEmpty(roleEntityList)) {
             return new PageResult<UserRoleRelationPageListBO>()
@@ -142,6 +189,7 @@ public class SysUserRoleRelationService {
                                 .eq(dto.getGroupId() != null, SysUserRoleRelationEntity::getGroupId, dto.getGroupId())
                                 .eq(dto.getUserId() != null && StrUtil.isEmpty(dto.getQueryStr()), SysUserRoleRelationEntity::getUserId, dto.getUserId())
                                 .in(StrUtil.isNotEmpty(dto.getQueryStr()), SysUserRoleRelationEntity::getUserId, userIds)
+                                .ne(SysUserRoleRelationEntity::getRoleValue, "Member")
                                 .orderByDesc(SysUserRoleRelationEntity::getCreatedAt)
                 );
         if (CollUtil.isEmpty(entityPage.getRecords())) {
@@ -199,9 +247,15 @@ public class SysUserRoleRelationService {
     public List<UserRoleRelationBO> queryTableData() {
         List<UserRoleRelationBO> userRoleRelationBOList = Lists.newArrayList();
         //查询关系数据
-        List<SysUserRoleRelationEntity> relationEntityList = sysUserRoleRelationMapper.selectList(null);
+        List<SysUserRoleRelationEntity> relationEntityList = sysUserRoleRelationMapper.selectList(
+                new LambdaQueryWrapper<>(SysUserRoleRelationEntity.class)
+                        .ne(SysUserRoleRelationEntity::getRoleValue, "Member")
+        );
         //查询角色
-        List<SysRoleEntity> sysRoleEntities = sysRoleMapper.selectList(null);
+        List<SysRoleEntity> sysRoleEntities = sysRoleMapper.selectList(
+                new LambdaQueryWrapper<>(SysRoleEntity.class)
+                        .ne(SysRoleEntity::getValue, "Member")
+        );
         //查询用户
         List<SysUserEntity> sysUserEntityList =
                 sysUserMapper.selectList(
@@ -354,15 +408,43 @@ public class SysUserRoleRelationService {
         if (count > 0) {
             throw new BusinessException(400, "请勿重复设置");
         }
+        SysRoleEntity sysRoleEntity = sysRoleMapper.selectById(dto.getRoleId());
         SysUserRoleRelationEntity entity = new SysUserRoleRelationEntity()
-                .setUserId(dto.getUserId()).setRoleId(dto.getRoleId()).setGroupId(dto.getGroupId());
+                .setUserId(dto.getUserId()).setRoleId(dto.getRoleId()).setGroupId(dto.getGroupId()).setRoleValue(sysRoleEntity.getValue());
         sysUserRoleRelationMapper.insert(entity);
+        if (sysRoleEntity.getValue().equals("Admin")) {
+            CrmAdminStpUtil.kickout(dto.getUserId());
+        } else {
+            //异步执行更新用户角色问题
+            asyncVThreadConfiguration.getAsyncExecutor().execute(() ->
+                    this.updateUserRole(dto.getUserId()));
+        }
         return entity.getId();
     }
 
 
     public void delete(Long id) {
-        sysUserRoleRelationMapper.deleteById(id);
+        SysUserRoleRelationEntity roleRelationEntity = sysUserRoleRelationMapper.selectById(id);
+        if (roleRelationEntity != null) {
+            sysUserRoleRelationMapper.deleteById(id);
+            //如果处理的是管理员,需要重新登陆
+            if (roleRelationEntity.getRoleValue().equals("Admin")) {
+                CrmAdminStpUtil.kickout(roleRelationEntity.getUserId());
+            } else {
+                //异步执行更新用户角色问题
+                asyncVThreadConfiguration.getAsyncExecutor().execute(() ->
+                        this.updateUserRole(roleRelationEntity.getUserId()));
+            }
+        }
+    }
+
+    public void delete(Long userId, Long groupId, String roleValue) {
+        sysUserRoleRelationMapper.delete(
+                new LambdaQueryWrapper<>(SysUserRoleRelationEntity.class)
+                        .eq(SysUserRoleRelationEntity::getUserId, userId)
+                        .eq(SysUserRoleRelationEntity::getGroupId, groupId)
+                        .eq(SysUserRoleRelationEntity::getRoleValue, roleValue)
+        );
     }
 
 }

+ 72 - 0
src/main/java/com/jtzx/crm/module/sys/util/DateSelectUtil.java

@@ -0,0 +1,72 @@
+package com.jtzx.crm.module.sys.util;
+
+import cn.hutool.core.date.DateField;
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.json.JSONUtil;
+import com.google.common.collect.Lists;
+import com.jtzx.crm.module.sys.model.response.DateSelectBO;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+public class DateSelectUtil {
+
+
+    public static List<DateSelectBO> getDateData() {
+        List<DateSelectBO> dateData = Lists.newArrayList();
+        dateData.add(getBO("全部",0,null));
+        dateData.add(getBO("今天",0, DateField.DAY_OF_YEAR));
+        dateData.add(getBO("本周",1,DateField.DAY_OF_WEEK));
+        dateData.add(getBO("最近1个月",1,DateField.MONTH));
+        dateData.add(getBO("最近3个月",3,DateField.MONTH));
+        dateData.add(getBO("最近半年",6,DateField.MONTH));
+        dateData.add(getBO("自定义",0,null));
+        return dateData;
+    }
+
+    public static void main(String[] args) {
+//        DateSelectBO bo = getBO("本周", 7, DateField.DAY_OF_WEEK);
+//        DateSelectBO bo = getBO("最近1个月",1,DateField.MONTH);
+        DateSelectBO bo = getBO("今天",0,DateField.DAY_OF_YEAR);
+        System.out.println(JSONUtil.toJsonStr(bo));
+    }
+
+
+    /**
+     * 获取对象
+     * @param name
+     * @param number
+     * @param dateField
+     * @return
+     */
+    public static DateSelectBO getBO(String name, Integer number, DateField dateField) {
+        DateSelectBO bo = DateSelectBO.builder().build();
+        bo.setName(name);
+        bo.setSortCode(number);
+        bo.setSelectStatus(false);
+        if (dateField == null) {
+            bo.setSelectStatus(true);
+        } else {
+            //取当天的最后一秒,往前计算开始时间
+            DateTime endDay = DateUtil.endOfDay(DateUtil.date());
+            LocalDateTime endDateTime = DateUtil.toLocalDateTime(endDay);
+            bo.setEndDate(endDateTime);
+
+            if(dateField.equals(DateField.DAY_OF_WEEK)){
+                DateTime dateTime = DateUtil.beginOfWeek(endDay);
+                bo.setStartDate(DateUtil.toLocalDateTime(dateTime));
+            }else {
+                bo.setStartDate(getStartDate(number, dateField, endDay));
+            }
+        }
+        return bo;
+    }
+
+
+    public static LocalDateTime getStartDate(Integer number, DateField dateField, DateTime endDay) {
+        DateTime startDate = DateUtil.offset(endDay, dateField, -number);
+        String str = DateUtil.formatDate(startDate)+" 00:00:00";
+        return DateUtil.toLocalDateTime(DateUtil.parse(str));
+    }
+}