mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-10-31 10:18:42 +08:00 
			
		
		
		
	CRM: 客户自动掉入公海
This commit is contained in:
		| @@ -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> | ||||||
|   | |||||||
| @@ -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)); | ||||||
|  |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1 @@ | |||||||
|  | package cn.iocoder.yudao.module.crm.job; | ||||||
| @@ -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(); | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 puhui999
					puhui999