Commit b444312ea888baa950a829c1105445ab463a9eaf

Authored by YunaiV
1 parent 3e652493

✨ CRM:优化客户的过期到公海的逻辑

yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java
@@ -45,6 +45,8 @@ public interface LogRecordConstants { @@ -45,6 +45,8 @@ public interface LogRecordConstants {
45 String CRM_CUSTOMER_IMPORT_SUCCESS = "{{#isUpdate ? '导入并更新了客户【'+ #customer.name +'】' : '导入了客户【'+ #customer.name +'】'}}"; 45 String CRM_CUSTOMER_IMPORT_SUCCESS = "{{#isUpdate ? '导入并更新了客户【'+ #customer.name +'】' : '导入了客户【'+ #customer.name +'】'}}";
46 String CRM_CUSTOMER_UPDATE_DEAL_STATUS_SUB_TYPE = "更新客户成交状态"; 46 String CRM_CUSTOMER_UPDATE_DEAL_STATUS_SUB_TYPE = "更新客户成交状态";
47 String CRM_CUSTOMER_UPDATE_DEAL_STATUS_SUCCESS = "更新了客户【{{#customerName}}】的成交状态为【{{#dealStatus ? '已成交' : '未成交'}}】"; 47 String CRM_CUSTOMER_UPDATE_DEAL_STATUS_SUCCESS = "更新了客户【{{#customerName}}】的成交状态为【{{#dealStatus ? '已成交' : '未成交'}}】";
  48 + String CRM_CUSTOMER_FOLLOW_UP_SUB_TYPE = "客户跟进";
  49 + String CRM_CUSTOMER_FOLLOW_UP_SUCCESS = "客户跟进【{{#customerName}}】";
48 50
49 // ======================= CRM_CUSTOMER_LIMIT_CONFIG 客户限制配置 ======================= 51 // ======================= CRM_CUSTOMER_LIMIT_CONFIG 客户限制配置 =======================
50 52
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java
@@ -43,6 +43,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. @@ -43,6 +43,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
43 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 43 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
44 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; 44 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
45 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; 45 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  46 +import static java.util.Collections.singletonList;
46 47
47 @Tag(name = "管理后台 - 线索") 48 @Tag(name = "管理后台 - 线索")
48 @RestController 49 @RestController
@@ -93,11 +94,11 @@ public class CrmClueController { @@ -93,11 +94,11 @@ public class CrmClueController {
93 return success(buildClueDetail(clue)); 94 return success(buildClueDetail(clue));
94 } 95 }
95 96
96 - public CrmClueRespVO buildClueDetail(CrmClueDO clue) { 97 + private CrmClueRespVO buildClueDetail(CrmClueDO clue) {
97 if (clue == null) { 98 if (clue == null) {
98 return null; 99 return null;
99 } 100 }
100 - return buildClueDetailList(Collections.singletonList(clue)).get(0); 101 + return buildClueDetailList(singletonList(clue)).get(0);
101 } 102 }
102 103
103 @GetMapping("/page") 104 @GetMapping("/page")
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java
@@ -2,9 +2,9 @@ package cn.iocoder.yudao.module.crm.controller.admin.customer; @@ -2,9 +2,9 @@ package cn.iocoder.yudao.module.crm.controller.admin.customer;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.map.MapUtil; 4 import cn.hutool.core.map.MapUtil;
5 -import cn.hutool.core.util.ObjUtil;  
6 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
7 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  7 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
8 import cn.iocoder.yudao.framework.common.util.collection.MapUtils; 8 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
9 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; 9 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
10 import cn.iocoder.yudao.framework.common.util.number.NumberUtils; 10 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
@@ -15,7 +15,6 @@ import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; @@ -15,7 +15,6 @@ import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
15 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer.*; 15 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer.*;
16 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 16 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
17 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO; 17 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;
18 -import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;  
19 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerPoolConfigService; 18 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerPoolConfigService;
20 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; 19 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
21 import cn.iocoder.yudao.module.system.api.dept.DeptApi; 20 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
@@ -40,13 +39,11 @@ import java.util.List; @@ -40,13 +39,11 @@ import java.util.List;
40 import java.util.Map; 39 import java.util.Map;
41 import java.util.stream.Stream; 40 import java.util.stream.Stream;
42 41
43 -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;  
44 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 42 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
45 import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; 43 import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
46 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; 44 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
47 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; 45 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
48 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; 46 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
49 -import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CUSTOMER_POOL_CONFIG_NOT_EXISTS_OR_DISABLED;  
50 import static java.util.Collections.singletonList; 47 import static java.util.Collections.singletonList;
51 48
52 @Tag(name = "管理后台 - CRM 客户") 49 @Tag(name = "管理后台 - CRM 客户")
@@ -123,6 +120,7 @@ public class CrmCustomerController { @@ -123,6 +120,7 @@ public class CrmCustomerController {
123 @Operation(summary = "获得客户分页") 120 @Operation(summary = "获得客户分页")
124 @PreAuthorize("@ss.hasPermission('crm:customer:query')") 121 @PreAuthorize("@ss.hasPermission('crm:customer:query')")
125 public CommonResult<PageResult<CrmCustomerRespVO>> getCustomerPage(@Valid CrmCustomerPageReqVO pageVO) { 122 public CommonResult<PageResult<CrmCustomerRespVO>> getCustomerPage(@Valid CrmCustomerPageReqVO pageVO) {
  123 + customerService.autoPutCustomerPool();
126 // 1. 查询客户分页 124 // 1. 查询客户分页
127 PageResult<CrmCustomerDO> pageResult = customerService.getCustomerPage(pageVO, getLoginUserId()); 125 PageResult<CrmCustomerDO> pageResult = customerService.getCustomerPage(pageVO, getLoginUserId());
128 if (CollUtil.isEmpty(pageResult.getList())) { 126 if (CollUtil.isEmpty(pageResult.getList())) {
@@ -159,45 +157,21 @@ public class CrmCustomerController { @@ -159,45 +157,21 @@ public class CrmCustomerController {
159 }); 157 });
160 } 158 }
161 159
162 - // TODO @芋艿:需要 review 下  
163 - @GetMapping("/put-in-pool-remind-page") 160 + @GetMapping("/put-pool-remind-page")
164 @Operation(summary = "获得待进入公海客户分页") 161 @Operation(summary = "获得待进入公海客户分页")
165 @PreAuthorize("@ss.hasPermission('crm:customer:query')") 162 @PreAuthorize("@ss.hasPermission('crm:customer:query')")
166 - public CommonResult<PageResult<CrmCustomerRespVO>> getPutInPoolRemindCustomerPage(@Valid CrmCustomerPageReqVO pageVO) {  
167 - // 获取公海配置 TODO @dbh52:合并到 getPutInPoolRemindCustomerPage 会更合适哈;  
168 - CrmCustomerPoolConfigDO poolConfigDO = customerPoolConfigService.getCustomerPoolConfig();  
169 - if (ObjUtil.isNull(poolConfigDO)  
170 - || Boolean.FALSE.equals(poolConfigDO.getEnabled())  
171 - || Boolean.FALSE.equals(poolConfigDO.getNotifyEnabled())) {  
172 - throw exception(CUSTOMER_POOL_CONFIG_NOT_EXISTS_OR_DISABLED);  
173 - }  
174 - 163 + public CommonResult<PageResult<CrmCustomerRespVO>> getPutPoolRemindCustomerPage(@Valid CrmCustomerPageReqVO pageVO) {
175 // 1. 查询客户分页 164 // 1. 查询客户分页
176 - PageResult<CrmCustomerDO> pageResult = customerService.getPutInPoolRemindCustomerPage(pageVO, poolConfigDO, getLoginUserId());  
177 - if (CollUtil.isEmpty(pageResult.getList())) {  
178 - return success(PageResult.empty(pageResult.getTotal()));  
179 - }  
180 - 165 + PageResult<CrmCustomerDO> pageResult = customerService.getPutPoolRemindCustomerPage(pageVO, getLoginUserId());
181 // 2. 拼接数据 166 // 2. 拼接数据
182 return success(new PageResult<>(buildCustomerDetailList(pageResult.getList()), pageResult.getTotal())); 167 return success(new PageResult<>(buildCustomerDetailList(pageResult.getList()), pageResult.getTotal()));
183 } 168 }
184 169
185 - @GetMapping("/put-in-pool-remind-count") 170 + @GetMapping("/put-pool-remind-count")
186 @Operation(summary = "获得待进入公海客户数量") 171 @Operation(summary = "获得待进入公海客户数量")
187 @PreAuthorize("@ss.hasPermission('crm:customer:query')") 172 @PreAuthorize("@ss.hasPermission('crm:customer:query')")
188 - public CommonResult<Long> getPutInPoolRemindCustomerCount() {  
189 - // 获取公海配置  
190 - CrmCustomerPoolConfigDO poolConfigDO = customerPoolConfigService.getCustomerPoolConfig();  
191 - if (ObjUtil.isNull(poolConfigDO)  
192 - || Boolean.FALSE.equals(poolConfigDO.getEnabled())  
193 - || Boolean.FALSE.equals(poolConfigDO.getNotifyEnabled())) {  
194 - throw exception(CUSTOMER_POOL_CONFIG_NOT_EXISTS_OR_DISABLED);  
195 - }  
196 - CrmCustomerPageReqVO pageVO = new CrmCustomerPageReqVO()  
197 - .setPool(null)  
198 - .setContactStatus(CrmCustomerPageReqVO.CONTACT_TODAY)  
199 - .setSceneType(CrmSceneTypeEnum.OWNER.getType());  
200 - return success(customerService.getPutInPoolRemindCustomerCount(pageVO, poolConfigDO, getLoginUserId())); 173 + public CommonResult<Long> getPutPoolRemindCustomerCount() {
  174 + return success(customerService.getPutPoolRemindCustomerCount(getLoginUserId()));
201 } 175 }
202 176
203 @GetMapping("/today-customer-count") 177 @GetMapping("/today-customer-count")
@@ -225,24 +199,26 @@ public class CrmCustomerController { @@ -225,24 +199,26 @@ public class CrmCustomerController {
225 if (poolConfig == null || !poolConfig.getEnabled()) { 199 if (poolConfig == null || !poolConfig.getEnabled()) {
226 return MapUtil.empty(); 200 return MapUtil.empty();
227 } 201 }
  202 + list = CollectionUtils.filterList(list, customer -> {
  203 + // 特殊:如果没负责人,则说明已经在公海,不用计算
  204 + if (customer.getOwnerUserId() == null) {
  205 + return false;
  206 + }
  207 + // 已成交 or 已锁定,不进入公海
  208 + return !customer.getDealStatus() && !customer.getLockStatus();
  209 + });
228 return convertMap(list, CrmCustomerDO::getId, customer -> { 210 return convertMap(list, CrmCustomerDO::getId, customer -> {
229 - // TODO 芋艿:这样计算,貌似有点问题  
230 // 1.1 未成交放入公海天数 211 // 1.1 未成交放入公海天数
231 - long dealExpireDay = 0;  
232 - if (!customer.getDealStatus()) {  
233 - dealExpireDay = poolConfig.getDealExpireDays() - LocalDateTimeUtils.between(customer.getCreateTime());  
234 - }  
235 - if (dealExpireDay < 0) {  
236 - dealExpireDay = 0;  
237 - } 212 + long dealExpireDay = poolConfig.getDealExpireDays() - LocalDateTimeUtils.between(customer.getOwnerTime());
238 // 1.2 未跟进放入公海天数 213 // 1.2 未跟进放入公海天数
239 - LocalDateTime lastTime = ObjUtil.defaultIfNull(customer.getContactLastTime(), customer.getCreateTime());  
240 - long contactExpireDay = poolConfig.getContactExpireDays() - LocalDateTimeUtils.between(lastTime);  
241 - if (contactExpireDay < 0) {  
242 - contactExpireDay = 0; 214 + LocalDateTime lastTime = customer.getOwnerTime();
  215 + if (customer.getContactLastTime() != null && customer.getContactLastTime().isAfter(lastTime)) {
  216 + lastTime = customer.getContactLastTime();
243 } 217 }
  218 + long contactExpireDay = poolConfig.getContactExpireDays() - LocalDateTimeUtils.between(lastTime);
244 // 2. 返回最小的天数 219 // 2. 返回最小的天数
245 - return Math.min(dealExpireDay, contactExpireDay); 220 + long poolDay = Math.min(dealExpireDay, contactExpireDay);
  221 + return poolDay > 0 ? poolDay : 0;
246 }); 222 });
247 } 223 }
248 224
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerDO.java
@@ -57,6 +57,10 @@ public class CrmCustomerDO extends BaseDO { @@ -57,6 +57,10 @@ public class CrmCustomerDO extends BaseDO {
57 * 关联 AdminUserDO 的 id 字段 57 * 关联 AdminUserDO 的 id 字段
58 */ 58 */
59 private Long ownerUserId; 59 private Long ownerUserId;
  60 + /**
  61 + * 成为负责人的时间
  62 + */
  63 + private LocalDateTime ownerTime;
60 64
61 /** 65 /**
62 * 锁定状态 66 * 锁定状态
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java
@@ -31,40 +31,6 @@ import java.util.List; @@ -31,40 +31,6 @@ import java.util.List;
31 @Mapper 31 @Mapper
32 public interface CrmCustomerMapper extends BaseMapperX<CrmCustomerDO> { 32 public interface CrmCustomerMapper extends BaseMapperX<CrmCustomerDO> {
33 33
34 - private static MPJLambdaWrapperX<CrmCustomerDO> buildPutInPoolRemindCustomerWrapper(CrmCustomerPageReqVO pageReqVO, CrmCustomerPoolConfigDO poolConfigDO, Long userId) {  
35 - MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>();  
36 - // 拼接数据权限的查询条件  
37 - CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(),  
38 - CrmCustomerDO::getId, userId, pageReqVO.getSceneType(), null);  
39 -  
40 - // 锁定状态不需要提醒  
41 - query.ne(CrmCustomerDO::getLockStatus, true);  
42 -  
43 - // 情况一:未成交提醒日期区间  
44 - Integer dealExpireDays = poolConfigDO.getDealExpireDays();  
45 - LocalDateTime startDealRemindDate = LocalDateTimeUtil.beginOfDay(LocalDateTime.now())  
46 - .minusDays(dealExpireDays);  
47 - LocalDateTime endDealRemindDate = LocalDateTimeUtil.endOfDay(LocalDateTime.now())  
48 - .minusDays(Math.max(dealExpireDays - poolConfigDO.getNotifyDays(), 0));  
49 - // 情况二:未跟进提醒日期区间  
50 - Integer contactExpireDays = poolConfigDO.getContactExpireDays();  
51 - LocalDateTime startContactRemindDate = LocalDateTimeUtil.beginOfDay(LocalDateTime.now())  
52 - .minusDays(contactExpireDays);  
53 - LocalDateTime endContactRemindDate = LocalDateTimeUtil.endOfDay(LocalDateTime.now())  
54 - .minusDays(Math.max(contactExpireDays - poolConfigDO.getNotifyDays(), 0));  
55 - query  
56 - // 情况一:1. 未成交放入公海提醒  
57 - .eq(CrmCustomerDO::getDealStatus, false)  
58 - .between(CrmCustomerDO::getCreateTime, startDealRemindDate, endDealRemindDate)  
59 - // 情况二:未跟进放入公海提醒  
60 - .or() // 2.1 contactLastTime 为空 TODO 芋艿:这个要不要搞个默认值;  
61 - .isNull(CrmCustomerDO::getContactLastTime)  
62 - .between(CrmCustomerDO::getCreateTime, startContactRemindDate, endContactRemindDate)  
63 - .or() // 2.2 ContactLastTime 不为空  
64 - .between(CrmCustomerDO::getContactLastTime, startContactRemindDate, endContactRemindDate);  
65 - return query;  
66 - }  
67 -  
68 default Long selectCountByLockStatusAndOwnerUserId(Boolean lockStatus, Long ownerUserId) { 34 default Long selectCountByLockStatusAndOwnerUserId(Boolean lockStatus, Long ownerUserId) {
69 return selectCount(new LambdaUpdateWrapper<CrmCustomerDO>() 35 return selectCount(new LambdaUpdateWrapper<CrmCustomerDO>()
70 .eq(CrmCustomerDO::getLockStatus, lockStatus) 36 .eq(CrmCustomerDO::getLockStatus, lockStatus)
@@ -124,30 +90,80 @@ public interface CrmCustomerMapper extends BaseMapperX&lt;CrmCustomerDO&gt; { @@ -124,30 +90,80 @@ public interface CrmCustomerMapper extends BaseMapperX&lt;CrmCustomerDO&gt; {
124 return selectJoinList(CrmCustomerDO.class, query); 90 return selectJoinList(CrmCustomerDO.class, query);
125 } 91 }
126 92
127 - default List<CrmCustomerDO> selectListByLockAndNotPool(Boolean lockStatus) {  
128 - return selectList(new LambdaQueryWrapper<CrmCustomerDO>()  
129 - .eq(CrmCustomerDO::getLockStatus, lockStatus)  
130 - .gt(CrmCustomerDO::getOwnerUserId, 0));  
131 - }  
132 -  
133 default CrmCustomerDO selectByCustomerName(String name) { 93 default CrmCustomerDO selectByCustomerName(String name) {
134 return selectOne(CrmCustomerDO::getName, name); 94 return selectOne(CrmCustomerDO::getName, name);
135 } 95 }
136 96
137 - default PageResult<CrmCustomerDO> selectPutInPoolRemindCustomerPage(CrmCustomerPageReqVO pageReqVO,  
138 - CrmCustomerPoolConfigDO poolConfigDO,  
139 - Long userId) {  
140 - final MPJLambdaWrapperX<CrmCustomerDO> query = buildPutInPoolRemindCustomerWrapper(pageReqVO, poolConfigDO, userId); 97 + default PageResult<CrmCustomerDO> selectPutPoolRemindCustomerPage(CrmCustomerPageReqVO pageReqVO,
  98 + CrmCustomerPoolConfigDO poolConfig,
  99 + Long userId) {
  100 + final MPJLambdaWrapperX<CrmCustomerDO> query = buildPutPoolRemindCustomerQuery(pageReqVO, poolConfig, userId);
141 return selectJoinPage(pageReqVO, CrmCustomerDO.class, query.selectAll(CrmCustomerDO.class)); 101 return selectJoinPage(pageReqVO, CrmCustomerDO.class, query.selectAll(CrmCustomerDO.class));
142 } 102 }
143 103
144 - default Long selectPutInPoolRemindCustomerCount(CrmCustomerPageReqVO pageReqVO,  
145 - CrmCustomerPoolConfigDO poolConfigDO,  
146 - Long userId) {  
147 - final MPJLambdaWrapperX<CrmCustomerDO> query = buildPutInPoolRemindCustomerWrapper(pageReqVO, poolConfigDO, userId); 104 + default Long selectPutPoolRemindCustomerCount(CrmCustomerPageReqVO pageReqVO,
  105 + CrmCustomerPoolConfigDO poolConfigDO,
  106 + Long userId) {
  107 + final MPJLambdaWrapperX<CrmCustomerDO> query = buildPutPoolRemindCustomerQuery(pageReqVO, poolConfigDO, userId);
148 return selectCount(query); 108 return selectCount(query);
149 } 109 }
150 110
  111 + private static MPJLambdaWrapperX<CrmCustomerDO> buildPutPoolRemindCustomerQuery(CrmCustomerPageReqVO pageReqVO,
  112 + CrmCustomerPoolConfigDO poolConfig,
  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.eq(CrmCustomerDO::getLockStatus, false).eq(CrmCustomerDO::getDealStatus, false);
  121 +
  122 + // 情况一:未成交提醒日期区间
  123 + Integer dealExpireDays = poolConfig.getDealExpireDays();
  124 + LocalDateTime startDealRemindTime = LocalDateTime.now().minusDays(dealExpireDays);
  125 + LocalDateTime endDealRemindTime = LocalDateTime.now()
  126 + .minusDays(Math.max(dealExpireDays - poolConfig.getNotifyDays(), 0));
  127 + // 情况二:未跟进提醒日期区间
  128 + Integer contactExpireDays = poolConfig.getContactExpireDays();
  129 + LocalDateTime startContactRemindTime = LocalDateTime.now().minusDays(contactExpireDays);
  130 + LocalDateTime endContactRemindTime = LocalDateTime.now()
  131 + .minusDays(Math.max(contactExpireDays - poolConfig.getNotifyDays(), 0));
  132 + query.and(q -> {
  133 + // 情况一:成交超时提醒
  134 + q.between(CrmCustomerDO::getOwnerTime, startDealRemindTime, endDealRemindTime)
  135 + // 情况二:跟进超时提醒
  136 + .or(w -> w.between(CrmCustomerDO::getOwnerTime, startContactRemindTime, endContactRemindTime)
  137 + .and(p -> p.between(CrmCustomerDO::getContactLastTime, startContactRemindTime, endContactRemindTime)
  138 + .or().isNull(CrmCustomerDO::getContactLastTime)));
  139 + });
  140 + return query;
  141 + }
  142 +
  143 + /**
  144 + * 获得需要过期到公海的客户列表
  145 + *
  146 + * @return 客户列表
  147 + */
  148 + default List<CrmCustomerDO> selectListByAutoPool(CrmCustomerPoolConfigDO poolConfig) {
  149 + LambdaQueryWrapper<CrmCustomerDO> query = new LambdaQueryWrapper<>();
  150 + query.gt(CrmCustomerDO::getOwnerUserId, 0);
  151 + // 未锁定 + 未成交
  152 + query.eq(CrmCustomerDO::getLockStatus, false).eq(CrmCustomerDO::getDealStatus, false);
  153 + // 已经超时
  154 + LocalDateTime dealExpireTime = LocalDateTime.now().minusDays(poolConfig.getDealExpireDays());
  155 + LocalDateTime contactExpireTime = LocalDateTime.now().minusDays(poolConfig.getContactExpireDays());
  156 + query.and(q -> {
  157 + // 情况一:成交超时
  158 + q.lt(CrmCustomerDO::getOwnerTime, dealExpireTime)
  159 + // 情况二:跟进超时
  160 + .or(w -> w.lt(CrmCustomerDO::getOwnerTime, contactExpireTime)
  161 + .and(p -> p.lt(CrmCustomerDO::getContactLastTime, contactExpireTime)
  162 + .or().isNull(CrmCustomerDO::getContactLastTime)));
  163 + });
  164 + return selectList(query);
  165 + }
  166 +
151 default Long selectTodayCustomerCount(Long userId) { 167 default Long selectTodayCustomerCount(Long userId) {
152 MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>(); 168 MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>();
153 // 我负责的 + 非公海 169 // 我负责的 + 非公海
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java
@@ -78,8 +78,7 @@ public class CrmClueServiceImpl implements CrmClueService { @@ -78,8 +78,7 @@ public class CrmClueServiceImpl implements CrmClueService {
78 adminUserApi.validateUser(createReqVO.getOwnerUserId()); 78 adminUserApi.validateUser(createReqVO.getOwnerUserId());
79 79
80 // 2. 插入线索 80 // 2. 插入线索
81 - CrmClueDO clue = BeanUtils.toBean(createReqVO, CrmClueDO.class)  
82 - .setContactLastTime(LocalDateTime.now()); 81 + CrmClueDO clue = BeanUtils.toBean(createReqVO, CrmClueDO.class);
83 clueMapper.insert(clue); 82 clueMapper.insert(clue);
84 83
85 // 3. 创建数据权限 84 // 3. 创建数据权限
@@ -129,7 +128,7 @@ public class CrmClueServiceImpl implements CrmClueService { @@ -129,7 +128,7 @@ public class CrmClueServiceImpl implements CrmClueService {
129 // 校验线索是否存在 128 // 校验线索是否存在
130 CrmClueDO oldClue = validateClueExists(id); 129 CrmClueDO oldClue = validateClueExists(id);
131 130
132 - // 更新 131 + // 更新线索
133 clueMapper.updateById(new CrmClueDO().setId(id).setFollowUpStatus(true).setContactNextTime(contactNextTime) 132 clueMapper.updateById(new CrmClueDO().setId(id).setFollowUpStatus(true).setContactNextTime(contactNextTime)
134 .setContactLastTime(LocalDateTime.now()).setContactLastContent(contactLastContent)); 133 .setContactLastTime(LocalDateTime.now()).setContactLastContent(contactLastContent));
135 134
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java
@@ -3,11 +3,10 @@ package cn.iocoder.yudao.module.crm.service.customer; @@ -3,11 +3,10 @@ package cn.iocoder.yudao.module.crm.service.customer;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer.*; 4 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer.*;
5 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 5 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
6 -import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;  
7 import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO; 6 import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO;
8 -import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;  
9 import jakarta.validation.Valid; 7 import jakarta.validation.Valid;
10 8
  9 +import java.time.LocalDateTime;
11 import java.util.Collection; 10 import java.util.Collection;
12 import java.util.List; 11 import java.util.List;
13 import java.util.Map; 12 import java.util.Map;
@@ -46,6 +45,15 @@ public interface CrmCustomerService { @@ -46,6 +45,15 @@ public interface CrmCustomerService {
46 void updateCustomerDealStatus(Long id, Boolean dealStatus); 45 void updateCustomerDealStatus(Long id, Boolean dealStatus);
47 46
48 /** 47 /**
  48 + * 更新客户相关的跟进信息
  49 + *
  50 + * @param id 编号
  51 + * @param contactNextTime 下次联系时间
  52 + * @param contactLastContent 最后联系内容
  53 + */
  54 + void updateCustomerFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent);
  55 +
  56 + /**
49 * 删除客户 57 * 删除客户
50 * 58 *
51 * @param id 编号 59 * @param id 编号
@@ -89,6 +97,23 @@ public interface CrmCustomerService { @@ -89,6 +97,23 @@ public interface CrmCustomerService {
89 PageResult<CrmCustomerDO> getCustomerPage(CrmCustomerPageReqVO pageReqVO, Long userId); 97 PageResult<CrmCustomerDO> getCustomerPage(CrmCustomerPageReqVO pageReqVO, Long userId);
90 98
91 /** 99 /**
  100 + * 获得放入公海提醒的客户分页
  101 + *
  102 + * @param pageVO 分页查询
  103 + * @param userId 用户编号
  104 + * @return 客户分页
  105 + */
  106 + PageResult<CrmCustomerDO> getPutPoolRemindCustomerPage(CrmCustomerPageReqVO pageVO, Long userId);
  107 +
  108 + /**
  109 + * 获得待进入公海的客户数量
  110 + *
  111 + * @param userId 用户编号
  112 + * @return 提醒数量
  113 + */
  114 + Long getPutPoolRemindCustomerCount(Long userId);
  115 +
  116 + /**
92 * 校验客户是否存在 117 * 校验客户是否存在
93 * 118 *
94 * @param customerId 客户 id 119 * @param customerId 客户 id
@@ -112,13 +137,6 @@ public interface CrmCustomerService { @@ -112,13 +137,6 @@ public interface CrmCustomerService {
112 void lockCustomer(@Valid CrmCustomerLockReqVO lockReqVO, Long userId); 137 void lockCustomer(@Valid CrmCustomerLockReqVO lockReqVO, Long userId);
113 138
114 /** 139 /**
115 - * 更新客户相关更进信息  
116 - *  
117 - * @param customerUpdateFollowUpReqBO 请求  
118 - */  
119 - void updateCustomerFollowUp(CrmUpdateFollowUpReqBO customerUpdateFollowUpReqBO);  
120 -  
121 - /**  
122 * 创建客户 140 * 创建客户
123 * 141 *
124 * @param customerCreateReq 请求信息 142 * @param customerCreateReq 请求信息
@@ -162,18 +180,6 @@ public interface CrmCustomerService { @@ -162,18 +180,6 @@ public interface CrmCustomerService {
162 int autoPutCustomerPool(); 180 int autoPutCustomerPool();
163 181
164 /** 182 /**
165 - * 获得放入公海提醒的客户分页数据  
166 - *  
167 - * @param pageVO 分页查询  
168 - * @param poolConfigDO 公海配置  
169 - * @param userId 用户编号  
170 - * @return 客户分页  
171 - */  
172 - PageResult<CrmCustomerDO> getPutInPoolRemindCustomerPage(CrmCustomerPageReqVO pageVO,  
173 - CrmCustomerPoolConfigDO poolConfigDO,  
174 - Long userId);  
175 -  
176 - /**  
177 * 获得今日需联系客户数量 183 * 获得今日需联系客户数量
178 * 184 *
179 * @param userId 用户编号 185 * @param userId 用户编号
@@ -182,18 +188,6 @@ public interface CrmCustomerService { @@ -182,18 +188,6 @@ public interface CrmCustomerService {
182 Long getTodayCustomerCount(Long userId); 188 Long getTodayCustomerCount(Long userId);
183 189
184 /** 190 /**
185 - * 获得待进入公海的客户数量  
186 - *  
187 - * @param pageVO 分页查询  
188 - * @param poolConfigDO 公海配置  
189 - * @param userId 用户编号  
190 - * @return 提醒数量  
191 - */  
192 - Long getPutInPoolRemindCustomerCount(CrmCustomerPageReqVO pageVO,  
193 - CrmCustomerPoolConfigDO poolConfigDO,  
194 - Long userId);  
195 -  
196 - /**  
197 * 获得分配给我的客户数量 191 * 获得分配给我的客户数量
198 * 192 *
199 * @param userId 用户编号 193 * @param userId 用户编号
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
@@ -8,7 +8,6 @@ import cn.hutool.extra.spring.SpringUtil; @@ -8,7 +8,6 @@ import cn.hutool.extra.spring.SpringUtil;
8 import cn.iocoder.yudao.framework.common.exception.ServiceException; 8 import cn.iocoder.yudao.framework.common.exception.ServiceException;
9 import cn.iocoder.yudao.framework.common.pojo.PageResult; 9 import cn.iocoder.yudao.framework.common.pojo.PageResult;
10 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 10 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
11 -import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;  
12 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 11 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
13 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer.*; 12 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer.*;
14 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 13 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
@@ -16,6 +15,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfi @@ -16,6 +15,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfi
16 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO; 15 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;
17 import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper; 16 import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper;
18 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 17 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
  18 +import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;
19 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; 19 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
20 import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; 20 import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
21 import cn.iocoder.yudao.module.crm.framework.permission.core.util.CrmPermissionUtils; 21 import cn.iocoder.yudao.module.crm.framework.permission.core.util.CrmPermissionUtils;
@@ -23,7 +23,6 @@ import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; @@ -23,7 +23,6 @@ import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
23 import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; 23 import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
24 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; 24 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
25 import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO; 25 import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO;
26 -import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;  
27 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; 26 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
28 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; 27 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
29 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO; 28 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
@@ -43,7 +42,6 @@ import java.time.LocalDateTime; @@ -43,7 +42,6 @@ import java.time.LocalDateTime;
43 import java.util.*; 42 import java.util.*;
44 43
45 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 44 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
46 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;  
47 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; 45 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
48 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; 46 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
49 import static cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum.CUSTOMER_LOCK_LIMIT; 47 import static cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum.CUSTOMER_LOCK_LIMIT;
@@ -114,7 +112,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -114,7 +112,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
114 */ 112 */
115 private static CrmCustomerDO initCustomer(Object customer, Long ownerUserId) { 113 private static CrmCustomerDO initCustomer(Object customer, Long ownerUserId) {
116 return BeanUtils.toBean(customer, CrmCustomerDO.class).setOwnerUserId(ownerUserId) 114 return BeanUtils.toBean(customer, CrmCustomerDO.class).setOwnerUserId(ownerUserId)
117 - .setLockStatus(false).setDealStatus(false).setContactLastTime(LocalDateTime.now()); 115 + .setOwnerTime(LocalDateTime.now());
118 } 116 }
119 117
120 @Override 118 @Override
@@ -158,6 +156,22 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -158,6 +156,22 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
158 } 156 }
159 157
160 @Override 158 @Override
  159 + @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_FOLLOW_UP_SUB_TYPE, bizNo = "{{#id}",
  160 + success = CRM_CUSTOMER_FOLLOW_UP_SUCCESS)
  161 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.WRITE)
  162 + public void updateCustomerFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent) {
  163 + // 1.1 校验存在
  164 + CrmCustomerDO customer = validateCustomerExists(id);
  165 +
  166 + // 2. 更新客户的跟进信息
  167 + customerMapper.updateById(new CrmCustomerDO().setId(id).setFollowUpStatus(true).setContactNextTime(contactNextTime)
  168 + .setContactLastTime(LocalDateTime.now()));
  169 +
  170 + // 3. 记录操作日志上下文
  171 + LogRecordContext.putVariable("customerName", customer.getName());
  172 + }
  173 +
  174 + @Override
161 @Transactional(rollbackFor = Exception.class) 175 @Transactional(rollbackFor = Exception.class)
162 @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_DELETE_SUB_TYPE, bizNo = "{{#id}}", 176 @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_DELETE_SUB_TYPE, bizNo = "{{#id}}",
163 success = CRM_CUSTOMER_DELETE_SUCCESS) 177 success = CRM_CUSTOMER_DELETE_SUCCESS)
@@ -168,7 +182,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -168,7 +182,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
168 // 1.2 检查引用 182 // 1.2 检查引用
169 validateCustomerReference(id); 183 validateCustomerReference(id);
170 184
171 - // 2. 删除 185 + // 2. 删除客户
172 customerMapper.deleteById(id); 186 customerMapper.deleteById(id);
173 // 3. 删除数据权限 187 // 3. 删除数据权限
174 permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), id); 188 permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), id);
@@ -209,7 +223,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -209,7 +223,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
209 permissionService.transferPermission(new CrmPermissionTransferReqBO(userId, CrmBizTypeEnum.CRM_CUSTOMER.getType(), 223 permissionService.transferPermission(new CrmPermissionTransferReqBO(userId, CrmBizTypeEnum.CRM_CUSTOMER.getType(),
210 reqVO.getId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel())); 224 reqVO.getId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel()));
211 // 2.2 转移后重新设置负责人 225 // 2.2 转移后重新设置负责人
212 - customerMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId()); 226 + customerMapper.updateById(new CrmCustomerDO().setId(reqVO.getId())
  227 + .setOwnerUserId(reqVO.getNewOwnerUserId()).setOwnerTime(LocalDateTime.now()));
213 228
214 // 3. 记录转移日志 229 // 3. 记录转移日志
215 LogRecordContext.putVariable("customer", customer); 230 LogRecordContext.putVariable("customer", customer);
@@ -226,7 +241,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -226,7 +241,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
226 if (customer.getLockStatus().equals(lockReqVO.getLockStatus())) { 241 if (customer.getLockStatus().equals(lockReqVO.getLockStatus())) {
227 throw exception(customer.getLockStatus() ? CUSTOMER_LOCK_FAIL_IS_LOCK : CUSTOMER_UNLOCK_FAIL_IS_UNLOCK); 242 throw exception(customer.getLockStatus() ? CUSTOMER_LOCK_FAIL_IS_LOCK : CUSTOMER_UNLOCK_FAIL_IS_UNLOCK);
228 } 243 }
229 - // 1.3 校验锁定上限。 244 + // 1.3 校验锁定上限
230 if (lockReqVO.getLockStatus()) { 245 if (lockReqVO.getLockStatus()) {
231 validateCustomerExceedLockLimit(userId); 246 validateCustomerExceedLockLimit(userId);
232 } 247 }
@@ -240,11 +255,6 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -240,11 +255,6 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
240 } 255 }
241 256
242 @Override 257 @Override
243 - public void updateCustomerFollowUp(CrmUpdateFollowUpReqBO customerUpdateFollowUpReqBO) {  
244 - customerMapper.updateById(BeanUtils.toBean(customerUpdateFollowUpReqBO, CrmCustomerDO.class).setId(customerUpdateFollowUpReqBO.getBizId()));  
245 - }  
246 -  
247 - @Override  
248 @Transactional(rollbackFor = Exception.class) 258 @Transactional(rollbackFor = Exception.class)
249 @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_CREATE_SUB_TYPE, bizNo = "{{#customer.id}}", 259 @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_CREATE_SUB_TYPE, bizNo = "{{#customer.id}}",
250 success = CRM_CUSTOMER_CREATE_SUCCESS) 260 success = CRM_CUSTOMER_CREATE_SUCCESS)
@@ -263,7 +273,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -263,7 +273,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
263 } 273 }
264 274
265 @Override 275 @Override
266 - public CrmCustomerImportRespVO importCustomerList(List<CrmCustomerImportExcelVO> importCustomers, CrmCustomerImportReqVO importReqVO) { 276 + public CrmCustomerImportRespVO importCustomerList(List<CrmCustomerImportExcelVO> importCustomers,
  277 + CrmCustomerImportReqVO importReqVO) {
267 if (CollUtil.isEmpty(importCustomers)) { 278 if (CollUtil.isEmpty(importCustomers)) {
268 throw exception(CUSTOMER_IMPORT_LIST_IS_EMPTY); 279 throw exception(CUSTOMER_IMPORT_LIST_IS_EMPTY);
269 } 280 }
@@ -383,12 +394,13 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -383,12 +394,13 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
383 // 1.4 校验负责人是否到达上限 394 // 1.4 校验负责人是否到达上限
384 validateCustomerExceedOwnerLimit(ownerUserId, customers.size()); 395 validateCustomerExceedOwnerLimit(ownerUserId, customers.size());
385 396
386 - // 2.1 领取公海数据 397 + // 2. 领取公海数据
387 List<CrmCustomerDO> updateCustomers = new ArrayList<>(); 398 List<CrmCustomerDO> updateCustomers = new ArrayList<>();
388 List<CrmPermissionCreateReqBO> createPermissions = new ArrayList<>(); 399 List<CrmPermissionCreateReqBO> createPermissions = new ArrayList<>();
389 customers.forEach(customer -> { 400 customers.forEach(customer -> {
390 // 2.1. 设置负责人 401 // 2.1. 设置负责人
391 - updateCustomers.add(new CrmCustomerDO().setId(customer.getId()).setOwnerUserId(ownerUserId)); 402 + updateCustomers.add(new CrmCustomerDO().setId(customer.getId())
  403 + .setOwnerUserId(ownerUserId).setOwnerTime(LocalDateTime.now()));
392 // 2.2. 创建负责人数据权限 404 // 2.2. 创建负责人数据权限
393 createPermissions.add(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()) 405 createPermissions.add(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())
394 .setBizId(customer.getId()).setUserId(ownerUserId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); 406 .setBizId(customer.getId()).setUserId(ownerUserId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
@@ -415,34 +427,23 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -415,34 +427,23 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
415 if (poolConfig == null || !poolConfig.getEnabled()) { 427 if (poolConfig == null || !poolConfig.getEnabled()) {
416 return 0; 428 return 0;
417 } 429 }
418 - // 1.1 获取没有锁定的不在公海的客户  
419 - List<CrmCustomerDO> customerList = customerMapper.selectListByLockAndNotPool(Boolean.FALSE);  
420 - // TODO @puhui999:下面也搞到 sql 里去哈;写 or 查询,问题不大的;低 393 到 402;原因是,避免无用的太多数据查询到 java 进程里;  
421 - List<CrmCustomerDO> poolCustomerList = new ArrayList<>();  
422 - poolCustomerList.addAll(filterList(customerList, customer ->  
423 - !customer.getDealStatus() && (poolConfig.getDealExpireDays() - LocalDateTimeUtils.between(customer.getCreateTime())) <= 0));  
424 - poolCustomerList.addAll(filterList(customerList, customer -> {  
425 - if (!customer.getDealStatus()) { // 这里只处理成交的  
426 - return false;  
427 - }  
428 - LocalDateTime lastTime = ObjUtil.defaultIfNull(customer.getContactLastTime(), customer.getCreateTime());  
429 - return (poolConfig.getContactExpireDays() - LocalDateTimeUtils.between(lastTime)) <= 0;  
430 - }));  
431 - 430 + // 1. 获得需要放到的客户列表
  431 + List<CrmCustomerDO> customerList = customerMapper.selectListByAutoPool(poolConfig);
432 // 2. 逐个放入公海 432 // 2. 逐个放入公海
433 int count = 0; 433 int count = 0;
434 - for (CrmCustomerDO customer : poolCustomerList) { 434 + for (CrmCustomerDO customer : customerList) {
435 try { 435 try {
436 getSelf().putCustomerPool(customer); 436 getSelf().putCustomerPool(customer);
437 count++; 437 count++;
438 } catch (Throwable e) { 438 } catch (Throwable e) {
439 - log.error("[autoPutCustomerPool][Customer 客户({}) 放入公海异常]", customer.getId(), e); 439 + log.error("[autoPutCustomerPool][客户({}) 放入公海异常]", customer.getId(), e);
440 } 440 }
441 } 441 }
442 return count; 442 return count;
443 } 443 }
444 444
445 - private void putCustomerPool(CrmCustomerDO customer) { 445 + @Transactional // 需要 protected 修饰,因为需要在事务中调用
  446 + protected void putCustomerPool(CrmCustomerDO customer) {
446 // 1. 设置负责人为 NULL 447 // 1. 设置负责人为 NULL
447 int updateOwnerUserIncr = customerMapper.updateOwnerUserIdById(customer.getId(), null); 448 int updateOwnerUserIncr = customerMapper.updateOwnerUserIdById(customer.getId(), null);
448 if (updateOwnerUserIncr == 0) { 449 if (updateOwnerUserIncr == 0) {
@@ -486,17 +487,29 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -486,17 +487,29 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
486 } 487 }
487 488
488 @Override 489 @Override
489 - public PageResult<CrmCustomerDO> getPutInPoolRemindCustomerPage(CrmCustomerPageReqVO pageReqVO,  
490 - CrmCustomerPoolConfigDO poolConfigDO,  
491 - Long userId) {  
492 - return customerMapper.selectPutInPoolRemindCustomerPage(pageReqVO, poolConfigDO, userId); 490 + public PageResult<CrmCustomerDO> getPutPoolRemindCustomerPage(CrmCustomerPageReqVO pageVO, Long userId) {
  491 + CrmCustomerPoolConfigDO poolConfig = customerPoolConfigService.getCustomerPoolConfig();
  492 + if (ObjUtil.isNull(poolConfig)
  493 + || Boolean.FALSE.equals(poolConfig.getEnabled())
  494 + || Boolean.FALSE.equals(poolConfig.getNotifyEnabled())) {
  495 + return PageResult.empty();
  496 + }
  497 + return customerMapper.selectPutPoolRemindCustomerPage(pageVO, poolConfig, userId);
493 } 498 }
494 499
495 @Override 500 @Override
496 - public Long getPutInPoolRemindCustomerCount(CrmCustomerPageReqVO pageReqVO,  
497 - CrmCustomerPoolConfigDO poolConfigDO,  
498 - Long userId) {  
499 - return customerMapper.selectPutInPoolRemindCustomerCount(pageReqVO, poolConfigDO, userId); 501 + public Long getPutPoolRemindCustomerCount(Long userId) {
  502 + CrmCustomerPoolConfigDO poolConfig = customerPoolConfigService.getCustomerPoolConfig();
  503 + if (ObjUtil.isNull(poolConfig)
  504 + || Boolean.FALSE.equals(poolConfig.getEnabled())
  505 + || Boolean.FALSE.equals(poolConfig.getNotifyEnabled())) {
  506 + return 0L;
  507 + }
  508 + CrmCustomerPageReqVO pageVO = new CrmCustomerPageReqVO()
  509 + .setPool(null)
  510 + .setContactStatus(CrmCustomerPageReqVO.CONTACT_TODAY)
  511 + .setSceneType(CrmSceneTypeEnum.OWNER.getType());
  512 + return customerMapper.selectPutPoolRemindCustomerCount(pageVO, poolConfig, userId);
500 } 513 }
501 514
502 @Override 515 @Override
@@ -596,7 +609,6 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -596,7 +609,6 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
596 } 609 }
597 } 610 }
598 611
599 -  
600 /** 612 /**
601 * 获得自身的代理对象,解决 AOP 生效问题 613 * 获得自身的代理对象,解决 AOP 生效问题
602 * 614 *
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordServiceImpl.java
@@ -72,24 +72,24 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService { @@ -72,24 +72,24 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService {
72 CrmFollowUpRecordDO followUpRecord = BeanUtils.toBean(createReqVO, CrmFollowUpRecordDO.class); 72 CrmFollowUpRecordDO followUpRecord = BeanUtils.toBean(createReqVO, CrmFollowUpRecordDO.class);
73 crmFollowUpRecordMapper.insert(followUpRecord); 73 crmFollowUpRecordMapper.insert(followUpRecord);
74 74
75 - LocalDateTime now = LocalDateTime.now();  
76 - CrmUpdateFollowUpReqBO updateFollowUpReqBO = new CrmUpdateFollowUpReqBO().setBizId(followUpRecord.getBizId())  
77 - .setContactLastTime(now).setContactNextTime(followUpRecord.getNextTime()).setContactLastContent(followUpRecord.getContent());  
78 // 2. 更新 bizId 对应的记录; 75 // 2. 更新 bizId 对应的记录;
79 - if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_BUSINESS.getType(), followUpRecord.getBizType())) { // 更新商机跟进信息 76 + CrmUpdateFollowUpReqBO updateFollowUpReqBO = new CrmUpdateFollowUpReqBO().setBizId(followUpRecord.getBizId())
  77 + .setContactLastTime(LocalDateTime.now())
  78 + .setContactNextTime(followUpRecord.getNextTime()).setContactLastContent(followUpRecord.getContent());
  79 + if (ObjUtil.equal(CrmBizTypeEnum.CRM_BUSINESS.getType(), followUpRecord.getBizType())) { // 更新商机跟进信息
80 businessService.updateBusinessFollowUpBatch(Collections.singletonList(updateFollowUpReqBO)); 80 businessService.updateBusinessFollowUpBatch(Collections.singletonList(updateFollowUpReqBO));
81 } 81 }
82 - if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_CLUE.getType(), followUpRecord.getBizType())) { // 更新线索跟进信息 82 + if (ObjUtil.equal(CrmBizTypeEnum.CRM_CLUE.getType(), followUpRecord.getBizType())) { // 更新线索跟进信息
83 clueService.updateClueFollowUp(followUpRecord.getId(), followUpRecord.getNextTime(), followUpRecord.getContent()); 83 clueService.updateClueFollowUp(followUpRecord.getId(), followUpRecord.getNextTime(), followUpRecord.getContent());
84 } 84 }
85 - if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_CONTACT.getType(), followUpRecord.getBizType())) { // 更新联系人跟进信息 85 + if (ObjUtil.equal(CrmBizTypeEnum.CRM_CONTACT.getType(), followUpRecord.getBizType())) { // 更新联系人跟进信息
86 contactService.updateContactFollowUpBatch(Collections.singletonList(updateFollowUpReqBO)); 86 contactService.updateContactFollowUpBatch(Collections.singletonList(updateFollowUpReqBO));
87 } 87 }
88 - if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_CONTRACT.getType(), followUpRecord.getBizType())) { // 更新合同跟进信息 88 + if (ObjUtil.equal(CrmBizTypeEnum.CRM_CONTRACT.getType(), followUpRecord.getBizType())) { // 更新合同跟进信息
89 contractService.updateContractFollowUp(updateFollowUpReqBO); 89 contractService.updateContractFollowUp(updateFollowUpReqBO);
90 } 90 }
91 - if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_CUSTOMER.getType(), followUpRecord.getBizType())) { // 更新客户跟进信息  
92 - customerService.updateCustomerFollowUp(updateFollowUpReqBO); 91 + if (ObjUtil.equal(CrmBizTypeEnum.CRM_CUSTOMER.getType(), followUpRecord.getBizType())) { // 更新客户跟进信息
  92 + customerService.updateCustomerFollowUp(followUpRecord.getBizId(), followUpRecord.getNextTime(), followUpRecord.getContent());
93 } 93 }
94 94
95 // 3.1 更新 contactIds 对应的记录,不更新 lastTime 和 lastContent 95 // 3.1 更新 contactIds 对应的记录,不更新 lastTime 和 lastContent