Commit 5d8ca19b175019fb5b45e6084f98bd6288e8c09c

Authored by dhb52
1 parent 6e005f46

feat: CRM-backlog-待进入公海的客户

yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
@@ -26,8 +26,8 @@ public interface ErrorCodeConstants { @@ -26,8 +26,8 @@ public interface ErrorCodeConstants {
26 26
27 // ========== 联系人管理 1-020-003-000 ========== 27 // ========== 联系人管理 1-020-003-000 ==========
28 ErrorCode CONTACT_NOT_EXISTS = new ErrorCode(1_020_003_000, "联系人不存在"); 28 ErrorCode CONTACT_NOT_EXISTS = new ErrorCode(1_020_003_000, "联系人不存在");
29 - ErrorCode CONTACT_BUSINESS_LINK_NOT_EXISTS = new ErrorCode( 1_020_003_001, "联系人商机关联不存在");  
30 - ErrorCode CONTACT_DELETE_FAIL_CONTRACT_LINK_EXISTS = new ErrorCode( 1_020_003_002, "联系人已关联合同,不能删除"); 29 + ErrorCode CONTACT_BUSINESS_LINK_NOT_EXISTS = new ErrorCode(1_020_003_001, "联系人商机关联不存在");
  30 + ErrorCode CONTACT_DELETE_FAIL_CONTRACT_LINK_EXISTS = new ErrorCode(1_020_003_002, "联系人已关联合同,不能删除");
31 31
32 // ========== 回款 1-020-004-000 ========== 32 // ========== 回款 1-020-004-000 ==========
33 ErrorCode RECEIVABLE_NOT_EXISTS = new ErrorCode(1_020_004_000, "回款不存在"); 33 ErrorCode RECEIVABLE_NOT_EXISTS = new ErrorCode(1_020_004_000, "回款不存在");
@@ -80,7 +80,8 @@ public interface ErrorCodeConstants { @@ -80,7 +80,8 @@ public interface ErrorCodeConstants {
80 ErrorCode BUSINESS_STATUS_NOT_EXISTS = new ErrorCode(1_020_011_000, "商机状态不存在"); 80 ErrorCode BUSINESS_STATUS_NOT_EXISTS = new ErrorCode(1_020_011_000, "商机状态不存在");
81 81
82 // ========== 客户公海规则设置 1_020_012_000 ========== 82 // ========== 客户公海规则设置 1_020_012_000 ==========
83 - ErrorCode CUSTOMER_LIMIT_CONFIG_NOT_EXISTS = new ErrorCode(1_020_012_000, "客户限制配置不存在"); 83 + ErrorCode CUSTOMER_POOL_CONFIG_NOT_EXISTS_OR_DISABLED = new ErrorCode(1_020_012_000, "客户公海配置不存在或未启用");
  84 + ErrorCode CUSTOMER_LIMIT_CONFIG_NOT_EXISTS = new ErrorCode(1_020_012_001, "客户限制配置不存在");
84 85
85 // ========== 跟进记录 1_020_013_000 ========== 86 // ========== 跟进记录 1_020_013_000 ==========
86 ErrorCode FOLLOW_UP_RECORD_NOT_EXISTS = new ErrorCode(1_020_013_000, "跟进记录不存在"); 87 ErrorCode FOLLOW_UP_RECORD_NOT_EXISTS = new ErrorCode(1_020_013_000, "跟进记录不存在");
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java
@@ -36,11 +36,13 @@ import java.util.List; @@ -36,11 +36,13 @@ import java.util.List;
36 import java.util.Map; 36 import java.util.Map;
37 import java.util.stream.Stream; 37 import java.util.stream.Stream;
38 38
  39 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
39 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 40 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
40 import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; 41 import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
41 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; 42 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
42 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; 43 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
43 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; 44 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  45 +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CUSTOMER_POOL_CONFIG_NOT_EXISTS_OR_DISABLED;
44 46
45 @Tag(name = "管理后台 - CRM 客户") 47 @Tag(name = "管理后台 - CRM 客户")
46 @RestController 48 @RestController
@@ -118,6 +120,35 @@ public class CrmCustomerController { @@ -118,6 +120,35 @@ public class CrmCustomerController {
118 return success(CrmCustomerConvert.INSTANCE.convertPage(pageResult, userMap, deptMap, poolDayMap)); 120 return success(CrmCustomerConvert.INSTANCE.convertPage(pageResult, userMap, deptMap, poolDayMap));
119 } 121 }
120 122
  123 +
  124 + @GetMapping("/put-in-pool-remind-page")
  125 + @Operation(summary = "获得待进入公海客户分页")
  126 + @PreAuthorize("@ss.hasPermission('crm:customer:query')")
  127 + public CommonResult<PageResult<CrmCustomerRespVO>> getPutInPoolRemindCustomerPage(@Valid CrmCustomerPageReqVO pageVO) {
  128 + // 获取公海配置
  129 + CrmCustomerPoolConfigDO poolConfigDO = customerPoolConfigService.getCustomerPoolConfig();
  130 + if (ObjUtil.isNull(poolConfigDO)
  131 + || Boolean.FALSE.equals(poolConfigDO.getEnabled())
  132 + || Boolean.FALSE.equals(poolConfigDO.getNotifyEnabled())
  133 + ) {
  134 + throw exception(CUSTOMER_POOL_CONFIG_NOT_EXISTS_OR_DISABLED);
  135 + }
  136 +
  137 + // 1. 查询客户分页
  138 + PageResult<CrmCustomerDO> pageResult = customerService.getPutInPoolRemindCustomerPage(pageVO, poolConfigDO, getLoginUserId());
  139 + if (CollUtil.isEmpty(pageResult.getList())) {
  140 + return success(PageResult.empty(pageResult.getTotal()));
  141 + }
  142 +
  143 + // 2. 拼接数据
  144 + Map<Long, Long> poolDayMap = getPoolDayMap(pageResult.getList()); // 客户界面,需要查看距离进入公海的时间
  145 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
  146 + convertSetByFlatMap(pageResult.getList(), user -> Stream.of(Long.parseLong(user.getCreator()), user.getOwnerUserId())));
  147 + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
  148 +
  149 + return success(CrmCustomerConvert.INSTANCE.convertPage(pageResult, userMap, deptMap, poolDayMap));
  150 + }
  151 +
121 /** 152 /**
122 * 获取距离进入公海的时间 153 * 获取距离进入公海的时间
123 * 154 *
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java
@@ -8,6 +8,7 @@ import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; @@ -8,6 +8,7 @@ import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
8 import cn.iocoder.yudao.module.crm.controller.admin.backlog.vo.CrmTodayCustomerPageReqVO; 8 import cn.iocoder.yudao.module.crm.controller.admin.backlog.vo.CrmTodayCustomerPageReqVO;
9 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO; 9 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO;
10 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 10 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
  11 +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;
11 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 12 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
12 import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils; 13 import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils;
13 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 14 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -107,4 +108,55 @@ public interface CrmCustomerMapper extends BaseMapperX&lt;CrmCustomerDO&gt; { @@ -107,4 +108,55 @@ public interface CrmCustomerMapper extends BaseMapperX&lt;CrmCustomerDO&gt; {
107 .isNotNull(CrmCustomerDO::getOwnerUserId)); 108 .isNotNull(CrmCustomerDO::getOwnerUserId));
108 } 109 }
109 110
  111 + default PageResult<CrmCustomerDO> selectPutInPoolRemindCustomerPage(CrmCustomerPageReqVO pageReqVO,
  112 + CrmCustomerPoolConfigDO poolConfigDO,
  113 + Long userId) {
  114 + MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>();
  115 + // 拼接数据权限的查询条件
  116 + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(),
  117 + CrmCustomerDO::getId, userId, pageReqVO.getSceneType(), null);
  118 +
  119 + // 拼接自身的查询条件
  120 + query.selectAll(CrmCustomerDO.class);
  121 +
  122 + // 未跟进放入公海天数
  123 + final Integer contactExpireDays = poolConfigDO.getContactExpireDays();
  124 + // 未成交放入公海天数
  125 + final Integer dealExpireDays = poolConfigDO.getDealExpireDays();
  126 + // 提前提醒天数
  127 + final Integer notifyDays = poolConfigDO.getNotifyDays();
  128 +
  129 + // 未成交提醒日期区间
  130 + // dealExpireDays 开始提醒
  131 + LocalDateTime startDealRemindDate = LocalDateTimeUtil.beginOfDay(LocalDateTime.now())
  132 + .minusDays(dealExpireDays);
  133 + // dealExpireDays - notifyDays 结束提醒
  134 + LocalDateTime endDealRemindDate = LocalDateTimeUtil.endOfDay(LocalDateTime.now())
  135 + .minusDays(Math.max(dealExpireDays - notifyDays, 0));
  136 +
  137 + // 未跟进提醒日期区间
  138 + // contactExpireDays 开始提醒
  139 + LocalDateTime startContactRemindDate = LocalDateTimeUtil.beginOfDay(LocalDateTime.now())
  140 + .minusDays(contactExpireDays);
  141 + // contactExpireDays - notifyDays 结束提醒
  142 + LocalDateTime endContactRemindDate = LocalDateTimeUtil.endOfDay(LocalDateTime.now())
  143 + .minusDays(Math.max(contactExpireDays - notifyDays, 0));
  144 +
  145 + query
  146 + // 1.1 未成交放入公海提醒
  147 + .eq(CrmCustomerDO::getDealStatus, false)
  148 + .between(CrmCustomerDO::getCreateTime, startDealRemindDate, endDealRemindDate)
  149 + .or()
  150 + // 1.2 未跟进放入公海提醒
  151 + // 1.2.1 ContactLastTime 为空
  152 + .isNull(CrmCustomerDO::getContactLastTime)
  153 + .between(CrmCustomerDO::getCreateTime, startContactRemindDate, endContactRemindDate)
  154 + .or()
  155 + // 1.2.2 ContactLastTime 不为空
  156 + .between(CrmCustomerDO::getContactLastTime, startContactRemindDate, endContactRemindDate)
  157 + ;
  158 +
  159 + return selectJoinPage(pageReqVO, CrmCustomerDO.class, query);
  160 + }
  161 +
110 } 162 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java
@@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageR @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageR
6 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerSaveReqVO; 6 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerSaveReqVO;
7 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerTransferReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerTransferReqVO;
8 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 8 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
  9 +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;
9 import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO; 10 import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO;
10 import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; 11 import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
11 import jakarta.validation.Valid; 12 import jakarta.validation.Valid;
@@ -134,4 +135,7 @@ public interface CrmCustomerService { @@ -134,4 +135,7 @@ public interface CrmCustomerService {
134 */ 135 */
135 int customerAutoPutPoolBySystem(); 136 int customerAutoPutPoolBySystem();
136 137
  138 + PageResult<CrmCustomerDO> getPutInPoolRemindCustomerPage(CrmCustomerPageReqVO pageVO,
  139 + CrmCustomerPoolConfigDO poolConfigDO,
  140 + Long loginUserId);
137 } 141 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
@@ -387,6 +387,12 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -387,6 +387,12 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
387 return customerMapper.selectPage(pageReqVO, userId); 387 return customerMapper.selectPage(pageReqVO, userId);
388 } 388 }
389 389
  390 + public PageResult<CrmCustomerDO> getPutInPoolRemindCustomerPage(CrmCustomerPageReqVO pageReqVO,
  391 + CrmCustomerPoolConfigDO poolConfigDO,
  392 + Long userId) {
  393 + return customerMapper.selectPutInPoolRemindCustomerPage(pageReqVO, poolConfigDO, userId);
  394 + }
  395 +
390 // ======================= 校验相关 ======================= 396 // ======================= 校验相关 =======================
391 397
392 /** 398 /**