Commit ed92bf45ce2b157fe309e59c24dfb12bd1cfb72e

Authored by puhui999
1 parent 6850c7c7

CRM: 客户自动掉入公海

yudao-module-crm/yudao-module-crm-biz/pom.xml
@@ -70,5 +70,9 @@ @@ -70,5 +70,9 @@
70 <groupId>cn.iocoder.boot</groupId> 70 <groupId>cn.iocoder.boot</groupId>
71 <artifactId>yudao-spring-boot-starter-test</artifactId> 71 <artifactId>yudao-spring-boot-starter-test</artifactId>
72 </dependency> 72 </dependency>
  73 + <dependency>
  74 + <groupId>cn.iocoder.boot</groupId>
  75 + <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
  76 + </dependency>
73 </dependencies> 77 </dependencies>
74 </project> 78 </project>
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java
@@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageR @@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageR
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.enums.common.CrmBizTypeEnum; 11 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
12 import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils; 12 import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils;
  13 +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
13 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 14 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
14 import org.apache.ibatis.annotations.Mapper; 15 import org.apache.ibatis.annotations.Mapper;
15 import org.springframework.lang.Nullable; 16 import org.springframework.lang.Nullable;
@@ -99,4 +100,10 @@ public interface CrmCustomerMapper extends BaseMapperX&lt;CrmCustomerDO&gt; { @@ -99,4 +100,10 @@ public interface CrmCustomerMapper extends BaseMapperX&lt;CrmCustomerDO&gt; {
99 return selectJoinPage(pageReqVO, CrmCustomerDO.class, query); 100 return selectJoinPage(pageReqVO, CrmCustomerDO.class, query);
100 } 101 }
101 102
  103 + default List<CrmCustomerDO> selectListByLockStatusAndOwnerUserIdNotNull(Boolean lockStatus) {
  104 + return selectList(new LambdaQueryWrapper<CrmCustomerDO>()
  105 + .eq(CrmCustomerDO::getLockStatus, lockStatus)
  106 + .isNotNull(CrmCustomerDO::getOwnerUserId));
  107 + }
  108 +
102 } 109 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/job/customer/CrmCustomerAutoPutPoolJob.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.job.customer;
  2 +
  3 +import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
  4 +import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
  5 +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
  6 +import jakarta.annotation.Resource;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +/**
  10 + * 客户自动掉入公海 Job
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +@Component
  15 +public class CrmCustomerAutoPutPoolJob implements JobHandler {
  16 +
  17 + @Resource
  18 + private CrmCustomerService customerService;
  19 +
  20 + @Override
  21 + @TenantJob
  22 + public String execute(String param) {
  23 + int count = customerService.customerAutoPutPoolBySystem();
  24 + return String.format("掉入公海客户 %s 个", count);
  25 + }
  26 +
  27 +}
0 \ No newline at end of file 28 \ No newline at end of file
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/job/package-info.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.job;
0 \ No newline at end of file 2 \ No newline at end of file
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java
@@ -126,4 +126,11 @@ public interface CrmCustomerService { @@ -126,4 +126,11 @@ public interface CrmCustomerService {
126 */ 126 */
127 void receiveCustomer(List<Long> ids, Long ownerUserId, Boolean isReceive); 127 void receiveCustomer(List<Long> ids, Long ownerUserId, Boolean isReceive);
128 128
  129 + /**
  130 + * 【系统】客户自动掉入公海
  131 + *
  132 + * @return 掉入公海数量
  133 + */
  134 + int customerAutoPutPoolBySystem();
  135 +
129 } 136 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
@@ -2,9 +2,11 @@ package cn.iocoder.yudao.module.crm.service.customer; @@ -2,9 +2,11 @@ package cn.iocoder.yudao.module.crm.service.customer;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.lang.Assert; 4 import cn.hutool.core.lang.Assert;
  5 +import cn.hutool.core.util.ObjUtil;
5 import cn.hutool.extra.spring.SpringUtil; 6 import cn.hutool.extra.spring.SpringUtil;
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 7 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.CollectionUtils;
  9 +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 10 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
9 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerLockReqVO; 11 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerLockReqVO;
10 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO; 12 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO;
@@ -13,6 +15,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerTrans @@ -13,6 +15,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerTrans
13 import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert; 15 import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert;
14 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 16 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
15 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO; 17 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO;
  18 +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;
16 import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper; 19 import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper;
17 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 20 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
18 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; 21 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
@@ -31,6 +34,7 @@ import com.mzt.logapi.context.LogRecordContext; @@ -31,6 +34,7 @@ import com.mzt.logapi.context.LogRecordContext;
31 import com.mzt.logapi.service.impl.DiffParseFunction; 34 import com.mzt.logapi.service.impl.DiffParseFunction;
32 import com.mzt.logapi.starter.annotation.LogRecord; 35 import com.mzt.logapi.starter.annotation.LogRecord;
33 import jakarta.annotation.Resource; 36 import jakarta.annotation.Resource;
  37 +import lombok.extern.slf4j.Slf4j;
34 import org.springframework.context.annotation.Lazy; 38 import org.springframework.context.annotation.Lazy;
35 import org.springframework.stereotype.Service; 39 import org.springframework.stereotype.Service;
36 import org.springframework.transaction.annotation.Transactional; 40 import org.springframework.transaction.annotation.Transactional;
@@ -55,6 +59,7 @@ import static java.util.Collections.singletonList; @@ -55,6 +59,7 @@ import static java.util.Collections.singletonList;
55 * @author Wanwan 59 * @author Wanwan
56 */ 60 */
57 @Service 61 @Service
  62 +@Slf4j
58 @Validated 63 @Validated
59 public class CrmCustomerServiceImpl implements CrmCustomerService { 64 public class CrmCustomerServiceImpl implements CrmCustomerService {
60 65
@@ -67,6 +72,9 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -67,6 +72,9 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
67 private CrmCustomerLimitConfigService customerLimitConfigService; 72 private CrmCustomerLimitConfigService customerLimitConfigService;
68 @Resource 73 @Resource
69 @Lazy 74 @Lazy
  75 + private CrmCustomerPoolConfigService customerPoolConfigService;
  76 + @Resource
  77 + @Lazy
70 private CrmContactService contactService; 78 private CrmContactService contactService;
71 @Resource 79 @Resource
72 @Lazy 80 @Lazy
@@ -245,17 +253,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -245,17 +253,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
245 // 1.3. 校验客户是否锁定 253 // 1.3. 校验客户是否锁定
246 validateCustomerIsLocked(customer, true); 254 validateCustomerIsLocked(customer, true);
247 255
248 - // 2.1 设置负责人为 NULL  
249 - int updateOwnerUserIncr = customerMapper.updateOwnerUserIdById(customer.getId(), null);  
250 - if (updateOwnerUserIncr == 0) {  
251 - throw exception(CUSTOMER_UPDATE_OWNER_USER_FAIL);  
252 - }  
253 - // 2.2 删除负责人数据权限  
254 - permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), customer.getId(),  
255 - CrmPermissionLevelEnum.OWNER.getLevel());  
256 -  
257 - // 3. 联系人的负责人,也要设置为 null。因为:因为领取后,负责人也要关联过来,这块和 receiveCustomer 是对应的  
258 - contactService.updateOwnerUserIdByCustomerId(customer.getId(), null); 256 + // 2. 客户放入公海
  257 + putCustomerPool(customer);
259 258
260 // 记录操作日志上下文 259 // 记录操作日志上下文
261 LogRecordContext.putVariable("customerName", customer.getName()); 260 LogRecordContext.putVariable("customerName", customer.getName());
@@ -313,6 +312,49 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -313,6 +312,49 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
313 } 312 }
314 } 313 }
315 314
  315 + @Override
  316 + public int customerAutoPutPoolBySystem() {
  317 + CrmCustomerPoolConfigDO poolConfig = customerPoolConfigService.getCustomerPoolConfig();
  318 + if (poolConfig == null || !poolConfig.getEnabled()) {
  319 + return 0;
  320 + }
  321 + // 获取没有锁定的不在公海的客户
  322 + List<CrmCustomerDO> customerList = customerMapper.selectListByLockStatusAndOwnerUserIdNotNull(Boolean.FALSE);
  323 + List<CrmCustomerDO> poolCustomerList = CollectionUtils.filterList(customerList, customer -> {
  324 + // 1.1 未成交放入公海
  325 + if (!customer.getDealStatus()) {
  326 + return (poolConfig.getDealExpireDays() - LocalDateTimeUtils.between(customer.getCreateTime())) <= 0;
  327 + }
  328 + // 1.2 未跟进放入公海
  329 + LocalDateTime lastTime = ObjUtil.defaultIfNull(customer.getContactLastTime(), customer.getCreateTime());
  330 + return (poolConfig.getContactExpireDays() - LocalDateTimeUtils.between(lastTime)) <= 0;
  331 + });
  332 + int count = 0;
  333 + for (CrmCustomerDO customer : poolCustomerList) {
  334 + try {
  335 + getSelf().putCustomerPool(customer);
  336 + count++;
  337 + } catch (Throwable e) {
  338 + log.error("[customerAutoPutPoolBySystem][Customer 客户({}) 放入公海异常]", customer.getId(), e);
  339 + }
  340 + }
  341 + return count;
  342 + }
  343 +
  344 + private void putCustomerPool(CrmCustomerDO customer) {
  345 + // 1. 设置负责人为 NULL
  346 + int updateOwnerUserIncr = customerMapper.updateOwnerUserIdById(customer.getId(), null);
  347 + if (updateOwnerUserIncr == 0) {
  348 + throw exception(CUSTOMER_UPDATE_OWNER_USER_FAIL);
  349 + }
  350 + // 2. 删除负责人数据权限
  351 + permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), customer.getId(),
  352 + CrmPermissionLevelEnum.OWNER.getLevel());
  353 +
  354 + // 3. 联系人的负责人,也要设置为 null。因为:因为领取后,负责人也要关联过来,这块和 receiveCustomer 是对应的
  355 + contactService.updateOwnerUserIdByCustomerId(customer.getId(), null);
  356 + }
  357 +
316 @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_RECEIVE_SUB_TYPE, bizNo = "{{#customer.id}}", 358 @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_RECEIVE_SUB_TYPE, bizNo = "{{#customer.id}}",
317 success = CRM_CUSTOMER_RECEIVE_SUCCESS) 359 success = CRM_CUSTOMER_RECEIVE_SUCCESS)
318 public void receiveCustomerLog(CrmCustomerDO customer, String ownerUserName) { 360 public void receiveCustomerLog(CrmCustomerDO customer, String ownerUserName) {