Commit ed92bf45ce2b157fe309e59c24dfb12bd1cfb72e

Authored by puhui999
1 parent 6850c7c7

CRM: 客户自动掉入公海

yudao-module-crm/yudao-module-crm-biz/pom.xml
... ... @@ -70,5 +70,9 @@
70 70 <groupId>cn.iocoder.boot</groupId>
71 71 <artifactId>yudao-spring-boot-starter-test</artifactId>
72 72 </dependency>
  73 + <dependency>
  74 + <groupId>cn.iocoder.boot</groupId>
  75 + <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
  76 + </dependency>
73 77 </dependencies>
74 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 10 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
11 11 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
12 12 import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils;
  13 +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
13 14 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
14 15 import org.apache.ibatis.annotations.Mapper;
15 16 import org.springframework.lang.Nullable;
... ... @@ -99,4 +100,10 @@ public interface CrmCustomerMapper extends BaseMapperX&lt;CrmCustomerDO&gt; {
99 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 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 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 126 */
127 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 2  
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.lang.Assert;
  5 +import cn.hutool.core.util.ObjUtil;
5 6 import cn.hutool.extra.spring.SpringUtil;
6 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 8 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  9 +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
8 10 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
9 11 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerLockReqVO;
10 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 15 import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert;
14 16 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
15 17 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO;
  18 +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;
16 19 import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper;
17 20 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
18 21 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
... ... @@ -31,6 +34,7 @@ import com.mzt.logapi.context.LogRecordContext;
31 34 import com.mzt.logapi.service.impl.DiffParseFunction;
32 35 import com.mzt.logapi.starter.annotation.LogRecord;
33 36 import jakarta.annotation.Resource;
  37 +import lombok.extern.slf4j.Slf4j;
34 38 import org.springframework.context.annotation.Lazy;
35 39 import org.springframework.stereotype.Service;
36 40 import org.springframework.transaction.annotation.Transactional;
... ... @@ -55,6 +59,7 @@ import static java.util.Collections.singletonList;
55 59 * @author Wanwan
56 60 */
57 61 @Service
  62 +@Slf4j
58 63 @Validated
59 64 public class CrmCustomerServiceImpl implements CrmCustomerService {
60 65  
... ... @@ -67,6 +72,9 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
67 72 private CrmCustomerLimitConfigService customerLimitConfigService;
68 73 @Resource
69 74 @Lazy
  75 + private CrmCustomerPoolConfigService customerPoolConfigService;
  76 + @Resource
  77 + @Lazy
70 78 private CrmContactService contactService;
71 79 @Resource
72 80 @Lazy
... ... @@ -245,17 +253,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
245 253 // 1.3. 校验客户是否锁定
246 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 260 LogRecordContext.putVariable("customerName", customer.getName());
... ... @@ -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 358 @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_RECEIVE_SUB_TYPE, bizNo = "{{#customer.id}}",
317 359 success = CRM_CUSTOMER_RECEIVE_SUCCESS)
318 360 public void receiveCustomerLog(CrmCustomerDO customer, String ownerUserName) {
... ...