CRM: 客户自动掉入公海

This commit is contained in:
puhui999 2024-01-23 23:30:13 +08:00
parent 6850c7c71d
commit ed92bf45ce
6 changed files with 99 additions and 11 deletions

View File

@ -70,5 +70,9 @@
<groupId>cn.iocoder.boot</groupId> <groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-test</artifactId> <artifactId>yudao-spring-boot-starter-test</artifactId>
</dependency> </dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageR
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils; import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
@ -99,4 +100,10 @@ public interface CrmCustomerMapper extends BaseMapperX<CrmCustomerDO> {
return selectJoinPage(pageReqVO, CrmCustomerDO.class, query); return selectJoinPage(pageReqVO, CrmCustomerDO.class, query);
} }
default List<CrmCustomerDO> selectListByLockStatusAndOwnerUserIdNotNull(Boolean lockStatus) {
return selectList(new LambdaQueryWrapper<CrmCustomerDO>()
.eq(CrmCustomerDO::getLockStatus, lockStatus)
.isNotNull(CrmCustomerDO::getOwnerUserId));
}
} }

View File

@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.crm.job.customer;
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Component;
/**
* 客户自动掉入公海 Job
*
* @author 芋道源码
*/
@Component
public class CrmCustomerAutoPutPoolJob implements JobHandler {
@Resource
private CrmCustomerService customerService;
@Override
@TenantJob
public String execute(String param) {
int count = customerService.customerAutoPutPoolBySystem();
return String.format("掉入公海客户 %s 个", count);
}
}

View File

@ -0,0 +1 @@
package cn.iocoder.yudao.module.crm.job;

View File

@ -126,4 +126,11 @@ public interface CrmCustomerService {
*/ */
void receiveCustomer(List<Long> ids, Long ownerUserId, Boolean isReceive); void receiveCustomer(List<Long> ids, Long ownerUserId, Boolean isReceive);
/**
* 系统客户自动掉入公海
*
* @return 掉入公海数量
*/
int customerAutoPutPoolBySystem();
} }

View File

@ -2,9 +2,11 @@ package cn.iocoder.yudao.module.crm.service.customer;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.extra.spring.SpringUtil; import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerLockReqVO; import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerLockReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO; 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
import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert; import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;
import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper; import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper;
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
@ -31,6 +34,7 @@ import com.mzt.logapi.context.LogRecordContext;
import com.mzt.logapi.service.impl.DiffParseFunction; import com.mzt.logapi.service.impl.DiffParseFunction;
import com.mzt.logapi.starter.annotation.LogRecord; import com.mzt.logapi.starter.annotation.LogRecord;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -55,6 +59,7 @@ import static java.util.Collections.singletonList;
* @author Wanwan * @author Wanwan
*/ */
@Service @Service
@Slf4j
@Validated @Validated
public class CrmCustomerServiceImpl implements CrmCustomerService { public class CrmCustomerServiceImpl implements CrmCustomerService {
@ -67,6 +72,9 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
private CrmCustomerLimitConfigService customerLimitConfigService; private CrmCustomerLimitConfigService customerLimitConfigService;
@Resource @Resource
@Lazy @Lazy
private CrmCustomerPoolConfigService customerPoolConfigService;
@Resource
@Lazy
private CrmContactService contactService; private CrmContactService contactService;
@Resource @Resource
@Lazy @Lazy
@ -245,17 +253,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
// 1.3. 校验客户是否锁定 // 1.3. 校验客户是否锁定
validateCustomerIsLocked(customer, true); validateCustomerIsLocked(customer, true);
// 2.1 设置负责人为 NULL // 2. 客户放入公海
int updateOwnerUserIncr = customerMapper.updateOwnerUserIdById(customer.getId(), null); putCustomerPool(customer);
if (updateOwnerUserIncr == 0) {
throw exception(CUSTOMER_UPDATE_OWNER_USER_FAIL);
}
// 2.2 删除负责人数据权限
permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), customer.getId(),
CrmPermissionLevelEnum.OWNER.getLevel());
// 3. 联系人的负责人也要设置为 null因为因为领取后负责人也要关联过来这块和 receiveCustomer 是对应的
contactService.updateOwnerUserIdByCustomerId(customer.getId(), null);
// 记录操作日志上下文 // 记录操作日志上下文
LogRecordContext.putVariable("customerName", customer.getName()); LogRecordContext.putVariable("customerName", customer.getName());
@ -313,6 +312,49 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
} }
} }
@Override
public int customerAutoPutPoolBySystem() {
CrmCustomerPoolConfigDO poolConfig = customerPoolConfigService.getCustomerPoolConfig();
if (poolConfig == null || !poolConfig.getEnabled()) {
return 0;
}
// 获取没有锁定的不在公海的客户
List<CrmCustomerDO> customerList = customerMapper.selectListByLockStatusAndOwnerUserIdNotNull(Boolean.FALSE);
List<CrmCustomerDO> poolCustomerList = CollectionUtils.filterList(customerList, customer -> {
// 1.1 未成交放入公海
if (!customer.getDealStatus()) {
return (poolConfig.getDealExpireDays() - LocalDateTimeUtils.between(customer.getCreateTime())) <= 0;
}
// 1.2 未跟进放入公海
LocalDateTime lastTime = ObjUtil.defaultIfNull(customer.getContactLastTime(), customer.getCreateTime());
return (poolConfig.getContactExpireDays() - LocalDateTimeUtils.between(lastTime)) <= 0;
});
int count = 0;
for (CrmCustomerDO customer : poolCustomerList) {
try {
getSelf().putCustomerPool(customer);
count++;
} catch (Throwable e) {
log.error("[customerAutoPutPoolBySystem][Customer 客户({}) 放入公海异常]", customer.getId(), e);
}
}
return count;
}
private void putCustomerPool(CrmCustomerDO customer) {
// 1. 设置负责人为 NULL
int updateOwnerUserIncr = customerMapper.updateOwnerUserIdById(customer.getId(), null);
if (updateOwnerUserIncr == 0) {
throw exception(CUSTOMER_UPDATE_OWNER_USER_FAIL);
}
// 2. 删除负责人数据权限
permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), customer.getId(),
CrmPermissionLevelEnum.OWNER.getLevel());
// 3. 联系人的负责人也要设置为 null因为因为领取后负责人也要关联过来这块和 receiveCustomer 是对应的
contactService.updateOwnerUserIdByCustomerId(customer.getId(), null);
}
@LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_RECEIVE_SUB_TYPE, bizNo = "{{#customer.id}}", @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_RECEIVE_SUB_TYPE, bizNo = "{{#customer.id}}",
success = CRM_CUSTOMER_RECEIVE_SUCCESS) success = CRM_CUSTOMER_RECEIVE_SUCCESS)
public void receiveCustomerLog(CrmCustomerDO customer, String ownerUserName) { public void receiveCustomerLog(CrmCustomerDO customer, String ownerUserName) {