mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2025-01-18 19:20:05 +08:00
✨ CRM:优化客户的过期到公海的逻辑
This commit is contained in:
parent
3e6524932b
commit
b444312ea8
@ -45,6 +45,8 @@ public interface LogRecordConstants {
|
||||
String CRM_CUSTOMER_IMPORT_SUCCESS = "{{#isUpdate ? '导入并更新了客户【'+ #customer.name +'】' : '导入了客户【'+ #customer.name +'】'}}";
|
||||
String CRM_CUSTOMER_UPDATE_DEAL_STATUS_SUB_TYPE = "更新客户成交状态";
|
||||
String CRM_CUSTOMER_UPDATE_DEAL_STATUS_SUCCESS = "更新了客户【{{#customerName}}】的成交状态为【{{#dealStatus ? '已成交' : '未成交'}}】";
|
||||
String CRM_CUSTOMER_FOLLOW_UP_SUB_TYPE = "客户跟进";
|
||||
String CRM_CUSTOMER_FOLLOW_UP_SUCCESS = "客户跟进【{{#customerName}}】";
|
||||
|
||||
// ======================= CRM_CUSTOMER_LIMIT_CONFIG 客户限制配置 =======================
|
||||
|
||||
|
@ -43,6 +43,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
@Tag(name = "管理后台 - 线索")
|
||||
@RestController
|
||||
@ -93,11 +94,11 @@ public class CrmClueController {
|
||||
return success(buildClueDetail(clue));
|
||||
}
|
||||
|
||||
public CrmClueRespVO buildClueDetail(CrmClueDO clue) {
|
||||
private CrmClueRespVO buildClueDetail(CrmClueDO clue) {
|
||||
if (clue == null) {
|
||||
return null;
|
||||
}
|
||||
return buildClueDetailList(Collections.singletonList(clue)).get(0);
|
||||
return buildClueDetailList(singletonList(clue)).get(0);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
|
@ -2,9 +2,9 @@ package cn.iocoder.yudao.module.crm.controller.admin.customer;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
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.MapUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||
@ -15,7 +15,6 @@ import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer.*;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;
|
||||
import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;
|
||||
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerPoolConfigService;
|
||||
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
|
||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||
@ -40,13 +39,11 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
||||
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CUSTOMER_POOL_CONFIG_NOT_EXISTS_OR_DISABLED;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
@Tag(name = "管理后台 - CRM 客户")
|
||||
@ -123,6 +120,7 @@ public class CrmCustomerController {
|
||||
@Operation(summary = "获得客户分页")
|
||||
@PreAuthorize("@ss.hasPermission('crm:customer:query')")
|
||||
public CommonResult<PageResult<CrmCustomerRespVO>> getCustomerPage(@Valid CrmCustomerPageReqVO pageVO) {
|
||||
customerService.autoPutCustomerPool();
|
||||
// 1. 查询客户分页
|
||||
PageResult<CrmCustomerDO> pageResult = customerService.getCustomerPage(pageVO, getLoginUserId());
|
||||
if (CollUtil.isEmpty(pageResult.getList())) {
|
||||
@ -159,45 +157,21 @@ public class CrmCustomerController {
|
||||
});
|
||||
}
|
||||
|
||||
// TODO @芋艿:需要 review 下
|
||||
@GetMapping("/put-in-pool-remind-page")
|
||||
@GetMapping("/put-pool-remind-page")
|
||||
@Operation(summary = "获得待进入公海客户分页")
|
||||
@PreAuthorize("@ss.hasPermission('crm:customer:query')")
|
||||
public CommonResult<PageResult<CrmCustomerRespVO>> getPutInPoolRemindCustomerPage(@Valid CrmCustomerPageReqVO pageVO) {
|
||||
// 获取公海配置 TODO @dbh52:合并到 getPutInPoolRemindCustomerPage 会更合适哈;
|
||||
CrmCustomerPoolConfigDO poolConfigDO = customerPoolConfigService.getCustomerPoolConfig();
|
||||
if (ObjUtil.isNull(poolConfigDO)
|
||||
|| Boolean.FALSE.equals(poolConfigDO.getEnabled())
|
||||
|| Boolean.FALSE.equals(poolConfigDO.getNotifyEnabled())) {
|
||||
throw exception(CUSTOMER_POOL_CONFIG_NOT_EXISTS_OR_DISABLED);
|
||||
}
|
||||
|
||||
public CommonResult<PageResult<CrmCustomerRespVO>> getPutPoolRemindCustomerPage(@Valid CrmCustomerPageReqVO pageVO) {
|
||||
// 1. 查询客户分页
|
||||
PageResult<CrmCustomerDO> pageResult = customerService.getPutInPoolRemindCustomerPage(pageVO, poolConfigDO, getLoginUserId());
|
||||
if (CollUtil.isEmpty(pageResult.getList())) {
|
||||
return success(PageResult.empty(pageResult.getTotal()));
|
||||
}
|
||||
|
||||
PageResult<CrmCustomerDO> pageResult = customerService.getPutPoolRemindCustomerPage(pageVO, getLoginUserId());
|
||||
// 2. 拼接数据
|
||||
return success(new PageResult<>(buildCustomerDetailList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@GetMapping("/put-in-pool-remind-count")
|
||||
@GetMapping("/put-pool-remind-count")
|
||||
@Operation(summary = "获得待进入公海客户数量")
|
||||
@PreAuthorize("@ss.hasPermission('crm:customer:query')")
|
||||
public CommonResult<Long> getPutInPoolRemindCustomerCount() {
|
||||
// 获取公海配置
|
||||
CrmCustomerPoolConfigDO poolConfigDO = customerPoolConfigService.getCustomerPoolConfig();
|
||||
if (ObjUtil.isNull(poolConfigDO)
|
||||
|| Boolean.FALSE.equals(poolConfigDO.getEnabled())
|
||||
|| Boolean.FALSE.equals(poolConfigDO.getNotifyEnabled())) {
|
||||
throw exception(CUSTOMER_POOL_CONFIG_NOT_EXISTS_OR_DISABLED);
|
||||
}
|
||||
CrmCustomerPageReqVO pageVO = new CrmCustomerPageReqVO()
|
||||
.setPool(null)
|
||||
.setContactStatus(CrmCustomerPageReqVO.CONTACT_TODAY)
|
||||
.setSceneType(CrmSceneTypeEnum.OWNER.getType());
|
||||
return success(customerService.getPutInPoolRemindCustomerCount(pageVO, poolConfigDO, getLoginUserId()));
|
||||
public CommonResult<Long> getPutPoolRemindCustomerCount() {
|
||||
return success(customerService.getPutPoolRemindCustomerCount(getLoginUserId()));
|
||||
}
|
||||
|
||||
@GetMapping("/today-customer-count")
|
||||
@ -225,24 +199,26 @@ public class CrmCustomerController {
|
||||
if (poolConfig == null || !poolConfig.getEnabled()) {
|
||||
return MapUtil.empty();
|
||||
}
|
||||
list = CollectionUtils.filterList(list, customer -> {
|
||||
// 特殊:如果没负责人,则说明已经在公海,不用计算
|
||||
if (customer.getOwnerUserId() == null) {
|
||||
return false;
|
||||
}
|
||||
// 已成交 or 已锁定,不进入公海
|
||||
return !customer.getDealStatus() && !customer.getLockStatus();
|
||||
});
|
||||
return convertMap(list, CrmCustomerDO::getId, customer -> {
|
||||
// TODO 芋艿:这样计算,貌似有点问题
|
||||
// 1.1 未成交放入公海天数
|
||||
long dealExpireDay = 0;
|
||||
if (!customer.getDealStatus()) {
|
||||
dealExpireDay = poolConfig.getDealExpireDays() - LocalDateTimeUtils.between(customer.getCreateTime());
|
||||
}
|
||||
if (dealExpireDay < 0) {
|
||||
dealExpireDay = 0;
|
||||
}
|
||||
long dealExpireDay = poolConfig.getDealExpireDays() - LocalDateTimeUtils.between(customer.getOwnerTime());
|
||||
// 1.2 未跟进放入公海天数
|
||||
LocalDateTime lastTime = ObjUtil.defaultIfNull(customer.getContactLastTime(), customer.getCreateTime());
|
||||
long contactExpireDay = poolConfig.getContactExpireDays() - LocalDateTimeUtils.between(lastTime);
|
||||
if (contactExpireDay < 0) {
|
||||
contactExpireDay = 0;
|
||||
LocalDateTime lastTime = customer.getOwnerTime();
|
||||
if (customer.getContactLastTime() != null && customer.getContactLastTime().isAfter(lastTime)) {
|
||||
lastTime = customer.getContactLastTime();
|
||||
}
|
||||
long contactExpireDay = poolConfig.getContactExpireDays() - LocalDateTimeUtils.between(lastTime);
|
||||
// 2. 返回最小的天数
|
||||
return Math.min(dealExpireDay, contactExpireDay);
|
||||
long poolDay = Math.min(dealExpireDay, contactExpireDay);
|
||||
return poolDay > 0 ? poolDay : 0;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -57,6 +57,10 @@ public class CrmCustomerDO extends BaseDO {
|
||||
* 关联 AdminUserDO 的 id 字段
|
||||
*/
|
||||
private Long ownerUserId;
|
||||
/**
|
||||
* 成为负责人的时间
|
||||
*/
|
||||
private LocalDateTime ownerTime;
|
||||
|
||||
/**
|
||||
* 锁定状态
|
||||
|
@ -31,40 +31,6 @@ import java.util.List;
|
||||
@Mapper
|
||||
public interface CrmCustomerMapper extends BaseMapperX<CrmCustomerDO> {
|
||||
|
||||
private static MPJLambdaWrapperX<CrmCustomerDO> buildPutInPoolRemindCustomerWrapper(CrmCustomerPageReqVO pageReqVO, CrmCustomerPoolConfigDO poolConfigDO, Long userId) {
|
||||
MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>();
|
||||
// 拼接数据权限的查询条件
|
||||
CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(),
|
||||
CrmCustomerDO::getId, userId, pageReqVO.getSceneType(), null);
|
||||
|
||||
// 锁定状态不需要提醒
|
||||
query.ne(CrmCustomerDO::getLockStatus, true);
|
||||
|
||||
// 情况一:未成交提醒日期区间
|
||||
Integer dealExpireDays = poolConfigDO.getDealExpireDays();
|
||||
LocalDateTime startDealRemindDate = LocalDateTimeUtil.beginOfDay(LocalDateTime.now())
|
||||
.minusDays(dealExpireDays);
|
||||
LocalDateTime endDealRemindDate = LocalDateTimeUtil.endOfDay(LocalDateTime.now())
|
||||
.minusDays(Math.max(dealExpireDays - poolConfigDO.getNotifyDays(), 0));
|
||||
// 情况二:未跟进提醒日期区间
|
||||
Integer contactExpireDays = poolConfigDO.getContactExpireDays();
|
||||
LocalDateTime startContactRemindDate = LocalDateTimeUtil.beginOfDay(LocalDateTime.now())
|
||||
.minusDays(contactExpireDays);
|
||||
LocalDateTime endContactRemindDate = LocalDateTimeUtil.endOfDay(LocalDateTime.now())
|
||||
.minusDays(Math.max(contactExpireDays - poolConfigDO.getNotifyDays(), 0));
|
||||
query
|
||||
// 情况一:1. 未成交放入公海提醒
|
||||
.eq(CrmCustomerDO::getDealStatus, false)
|
||||
.between(CrmCustomerDO::getCreateTime, startDealRemindDate, endDealRemindDate)
|
||||
// 情况二:未跟进放入公海提醒
|
||||
.or() // 2.1 contactLastTime 为空 TODO 芋艿:这个要不要搞个默认值;
|
||||
.isNull(CrmCustomerDO::getContactLastTime)
|
||||
.between(CrmCustomerDO::getCreateTime, startContactRemindDate, endContactRemindDate)
|
||||
.or() // 2.2 ContactLastTime 不为空
|
||||
.between(CrmCustomerDO::getContactLastTime, startContactRemindDate, endContactRemindDate);
|
||||
return query;
|
||||
}
|
||||
|
||||
default Long selectCountByLockStatusAndOwnerUserId(Boolean lockStatus, Long ownerUserId) {
|
||||
return selectCount(new LambdaUpdateWrapper<CrmCustomerDO>()
|
||||
.eq(CrmCustomerDO::getLockStatus, lockStatus)
|
||||
@ -124,30 +90,80 @@ public interface CrmCustomerMapper extends BaseMapperX<CrmCustomerDO> {
|
||||
return selectJoinList(CrmCustomerDO.class, query);
|
||||
}
|
||||
|
||||
default List<CrmCustomerDO> selectListByLockAndNotPool(Boolean lockStatus) {
|
||||
return selectList(new LambdaQueryWrapper<CrmCustomerDO>()
|
||||
.eq(CrmCustomerDO::getLockStatus, lockStatus)
|
||||
.gt(CrmCustomerDO::getOwnerUserId, 0));
|
||||
}
|
||||
|
||||
default CrmCustomerDO selectByCustomerName(String name) {
|
||||
return selectOne(CrmCustomerDO::getName, name);
|
||||
}
|
||||
|
||||
default PageResult<CrmCustomerDO> selectPutInPoolRemindCustomerPage(CrmCustomerPageReqVO pageReqVO,
|
||||
CrmCustomerPoolConfigDO poolConfigDO,
|
||||
Long userId) {
|
||||
final MPJLambdaWrapperX<CrmCustomerDO> query = buildPutInPoolRemindCustomerWrapper(pageReqVO, poolConfigDO, userId);
|
||||
default PageResult<CrmCustomerDO> selectPutPoolRemindCustomerPage(CrmCustomerPageReqVO pageReqVO,
|
||||
CrmCustomerPoolConfigDO poolConfig,
|
||||
Long userId) {
|
||||
final MPJLambdaWrapperX<CrmCustomerDO> query = buildPutPoolRemindCustomerQuery(pageReqVO, poolConfig, userId);
|
||||
return selectJoinPage(pageReqVO, CrmCustomerDO.class, query.selectAll(CrmCustomerDO.class));
|
||||
}
|
||||
|
||||
default Long selectPutInPoolRemindCustomerCount(CrmCustomerPageReqVO pageReqVO,
|
||||
CrmCustomerPoolConfigDO poolConfigDO,
|
||||
Long userId) {
|
||||
final MPJLambdaWrapperX<CrmCustomerDO> query = buildPutInPoolRemindCustomerWrapper(pageReqVO, poolConfigDO, userId);
|
||||
default Long selectPutPoolRemindCustomerCount(CrmCustomerPageReqVO pageReqVO,
|
||||
CrmCustomerPoolConfigDO poolConfigDO,
|
||||
Long userId) {
|
||||
final MPJLambdaWrapperX<CrmCustomerDO> query = buildPutPoolRemindCustomerQuery(pageReqVO, poolConfigDO, userId);
|
||||
return selectCount(query);
|
||||
}
|
||||
|
||||
private static MPJLambdaWrapperX<CrmCustomerDO> buildPutPoolRemindCustomerQuery(CrmCustomerPageReqVO pageReqVO,
|
||||
CrmCustomerPoolConfigDO poolConfig,
|
||||
Long userId) {
|
||||
MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>();
|
||||
// 拼接数据权限的查询条件
|
||||
CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(),
|
||||
CrmCustomerDO::getId, userId, pageReqVO.getSceneType(), null);
|
||||
|
||||
// 未锁定 + 未成交
|
||||
query.eq(CrmCustomerDO::getLockStatus, false).eq(CrmCustomerDO::getDealStatus, false);
|
||||
|
||||
// 情况一:未成交提醒日期区间
|
||||
Integer dealExpireDays = poolConfig.getDealExpireDays();
|
||||
LocalDateTime startDealRemindTime = LocalDateTime.now().minusDays(dealExpireDays);
|
||||
LocalDateTime endDealRemindTime = LocalDateTime.now()
|
||||
.minusDays(Math.max(dealExpireDays - poolConfig.getNotifyDays(), 0));
|
||||
// 情况二:未跟进提醒日期区间
|
||||
Integer contactExpireDays = poolConfig.getContactExpireDays();
|
||||
LocalDateTime startContactRemindTime = LocalDateTime.now().minusDays(contactExpireDays);
|
||||
LocalDateTime endContactRemindTime = LocalDateTime.now()
|
||||
.minusDays(Math.max(contactExpireDays - poolConfig.getNotifyDays(), 0));
|
||||
query.and(q -> {
|
||||
// 情况一:成交超时提醒
|
||||
q.between(CrmCustomerDO::getOwnerTime, startDealRemindTime, endDealRemindTime)
|
||||
// 情况二:跟进超时提醒
|
||||
.or(w -> w.between(CrmCustomerDO::getOwnerTime, startContactRemindTime, endContactRemindTime)
|
||||
.and(p -> p.between(CrmCustomerDO::getContactLastTime, startContactRemindTime, endContactRemindTime)
|
||||
.or().isNull(CrmCustomerDO::getContactLastTime)));
|
||||
});
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得需要过期到公海的客户列表
|
||||
*
|
||||
* @return 客户列表
|
||||
*/
|
||||
default List<CrmCustomerDO> selectListByAutoPool(CrmCustomerPoolConfigDO poolConfig) {
|
||||
LambdaQueryWrapper<CrmCustomerDO> query = new LambdaQueryWrapper<>();
|
||||
query.gt(CrmCustomerDO::getOwnerUserId, 0);
|
||||
// 未锁定 + 未成交
|
||||
query.eq(CrmCustomerDO::getLockStatus, false).eq(CrmCustomerDO::getDealStatus, false);
|
||||
// 已经超时
|
||||
LocalDateTime dealExpireTime = LocalDateTime.now().minusDays(poolConfig.getDealExpireDays());
|
||||
LocalDateTime contactExpireTime = LocalDateTime.now().minusDays(poolConfig.getContactExpireDays());
|
||||
query.and(q -> {
|
||||
// 情况一:成交超时
|
||||
q.lt(CrmCustomerDO::getOwnerTime, dealExpireTime)
|
||||
// 情况二:跟进超时
|
||||
.or(w -> w.lt(CrmCustomerDO::getOwnerTime, contactExpireTime)
|
||||
.and(p -> p.lt(CrmCustomerDO::getContactLastTime, contactExpireTime)
|
||||
.or().isNull(CrmCustomerDO::getContactLastTime)));
|
||||
});
|
||||
return selectList(query);
|
||||
}
|
||||
|
||||
default Long selectTodayCustomerCount(Long userId) {
|
||||
MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>();
|
||||
// 我负责的 + 非公海
|
||||
|
@ -78,8 +78,7 @@ public class CrmClueServiceImpl implements CrmClueService {
|
||||
adminUserApi.validateUser(createReqVO.getOwnerUserId());
|
||||
|
||||
// 2. 插入线索
|
||||
CrmClueDO clue = BeanUtils.toBean(createReqVO, CrmClueDO.class)
|
||||
.setContactLastTime(LocalDateTime.now());
|
||||
CrmClueDO clue = BeanUtils.toBean(createReqVO, CrmClueDO.class);
|
||||
clueMapper.insert(clue);
|
||||
|
||||
// 3. 创建数据权限
|
||||
@ -129,7 +128,7 @@ public class CrmClueServiceImpl implements CrmClueService {
|
||||
// 校验线索是否存在
|
||||
CrmClueDO oldClue = validateClueExists(id);
|
||||
|
||||
// 更新
|
||||
// 更新线索
|
||||
clueMapper.updateById(new CrmClueDO().setId(id).setFollowUpStatus(true).setContactNextTime(contactNextTime)
|
||||
.setContactLastTime(LocalDateTime.now()).setContactLastContent(contactLastContent));
|
||||
|
||||
|
@ -3,11 +3,10 @@ package cn.iocoder.yudao.module.crm.service.customer;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer.*;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;
|
||||
import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO;
|
||||
import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -45,6 +44,15 @@ public interface CrmCustomerService {
|
||||
*/
|
||||
void updateCustomerDealStatus(Long id, Boolean dealStatus);
|
||||
|
||||
/**
|
||||
* 更新客户相关的跟进信息
|
||||
*
|
||||
* @param id 编号
|
||||
* @param contactNextTime 下次联系时间
|
||||
* @param contactLastContent 最后联系内容
|
||||
*/
|
||||
void updateCustomerFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent);
|
||||
|
||||
/**
|
||||
* 删除客户
|
||||
*
|
||||
@ -88,6 +96,23 @@ public interface CrmCustomerService {
|
||||
*/
|
||||
PageResult<CrmCustomerDO> getCustomerPage(CrmCustomerPageReqVO pageReqVO, Long userId);
|
||||
|
||||
/**
|
||||
* 获得放入公海提醒的客户分页
|
||||
*
|
||||
* @param pageVO 分页查询
|
||||
* @param userId 用户编号
|
||||
* @return 客户分页
|
||||
*/
|
||||
PageResult<CrmCustomerDO> getPutPoolRemindCustomerPage(CrmCustomerPageReqVO pageVO, Long userId);
|
||||
|
||||
/**
|
||||
* 获得待进入公海的客户数量
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @return 提醒数量
|
||||
*/
|
||||
Long getPutPoolRemindCustomerCount(Long userId);
|
||||
|
||||
/**
|
||||
* 校验客户是否存在
|
||||
*
|
||||
@ -111,13 +136,6 @@ public interface CrmCustomerService {
|
||||
*/
|
||||
void lockCustomer(@Valid CrmCustomerLockReqVO lockReqVO, Long userId);
|
||||
|
||||
/**
|
||||
* 更新客户相关更进信息
|
||||
*
|
||||
* @param customerUpdateFollowUpReqBO 请求
|
||||
*/
|
||||
void updateCustomerFollowUp(CrmUpdateFollowUpReqBO customerUpdateFollowUpReqBO);
|
||||
|
||||
/**
|
||||
* 创建客户
|
||||
*
|
||||
@ -161,18 +179,6 @@ public interface CrmCustomerService {
|
||||
*/
|
||||
int autoPutCustomerPool();
|
||||
|
||||
/**
|
||||
* 获得放入公海提醒的客户分页数据
|
||||
*
|
||||
* @param pageVO 分页查询
|
||||
* @param poolConfigDO 公海配置
|
||||
* @param userId 用户编号
|
||||
* @return 客户分页
|
||||
*/
|
||||
PageResult<CrmCustomerDO> getPutInPoolRemindCustomerPage(CrmCustomerPageReqVO pageVO,
|
||||
CrmCustomerPoolConfigDO poolConfigDO,
|
||||
Long userId);
|
||||
|
||||
/**
|
||||
* 获得今日需联系客户数量
|
||||
*
|
||||
@ -181,18 +187,6 @@ public interface CrmCustomerService {
|
||||
*/
|
||||
Long getTodayCustomerCount(Long userId);
|
||||
|
||||
/**
|
||||
* 获得待进入公海的客户数量
|
||||
*
|
||||
* @param pageVO 分页查询
|
||||
* @param poolConfigDO 公海配置
|
||||
* @param userId 用户编号
|
||||
* @return 提醒数量
|
||||
*/
|
||||
Long getPutInPoolRemindCustomerCount(CrmCustomerPageReqVO pageVO,
|
||||
CrmCustomerPoolConfigDO poolConfigDO,
|
||||
Long userId);
|
||||
|
||||
/**
|
||||
* 获得分配给我的客户数量
|
||||
*
|
||||
|
@ -8,7 +8,6 @@ import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
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.module.crm.controller.admin.customer.vo.customer.*;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
|
||||
@ -16,6 +15,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfi
|
||||
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.enums.common.CrmBizTypeEnum;
|
||||
import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;
|
||||
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
|
||||
import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
|
||||
import cn.iocoder.yudao.module.crm.framework.permission.core.util.CrmPermissionUtils;
|
||||
@ -23,7 +23,6 @@ import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
|
||||
import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
|
||||
import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
|
||||
import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO;
|
||||
import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
|
||||
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
|
||||
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
|
||||
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
|
||||
@ -43,7 +42,6 @@ import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
|
||||
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
|
||||
import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
|
||||
import static cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum.CUSTOMER_LOCK_LIMIT;
|
||||
@ -114,7 +112,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
|
||||
*/
|
||||
private static CrmCustomerDO initCustomer(Object customer, Long ownerUserId) {
|
||||
return BeanUtils.toBean(customer, CrmCustomerDO.class).setOwnerUserId(ownerUserId)
|
||||
.setLockStatus(false).setDealStatus(false).setContactLastTime(LocalDateTime.now());
|
||||
.setOwnerTime(LocalDateTime.now());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -157,6 +155,22 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
|
||||
LogRecordContext.putVariable("dealStatus", dealStatus);
|
||||
}
|
||||
|
||||
@Override
|
||||
@LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_FOLLOW_UP_SUB_TYPE, bizNo = "{{#id}",
|
||||
success = CRM_CUSTOMER_FOLLOW_UP_SUCCESS)
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.WRITE)
|
||||
public void updateCustomerFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent) {
|
||||
// 1.1 校验存在
|
||||
CrmCustomerDO customer = validateCustomerExists(id);
|
||||
|
||||
// 2. 更新客户的跟进信息
|
||||
customerMapper.updateById(new CrmCustomerDO().setId(id).setFollowUpStatus(true).setContactNextTime(contactNextTime)
|
||||
.setContactLastTime(LocalDateTime.now()));
|
||||
|
||||
// 3. 记录操作日志上下文
|
||||
LogRecordContext.putVariable("customerName", customer.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_DELETE_SUB_TYPE, bizNo = "{{#id}}",
|
||||
@ -168,7 +182,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
|
||||
// 1.2 检查引用
|
||||
validateCustomerReference(id);
|
||||
|
||||
// 2. 删除
|
||||
// 2. 删除客户
|
||||
customerMapper.deleteById(id);
|
||||
// 3. 删除数据权限
|
||||
permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), id);
|
||||
@ -209,7 +223,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
|
||||
permissionService.transferPermission(new CrmPermissionTransferReqBO(userId, CrmBizTypeEnum.CRM_CUSTOMER.getType(),
|
||||
reqVO.getId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel()));
|
||||
// 2.2 转移后重新设置负责人
|
||||
customerMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
|
||||
customerMapper.updateById(new CrmCustomerDO().setId(reqVO.getId())
|
||||
.setOwnerUserId(reqVO.getNewOwnerUserId()).setOwnerTime(LocalDateTime.now()));
|
||||
|
||||
// 3. 记录转移日志
|
||||
LogRecordContext.putVariable("customer", customer);
|
||||
@ -226,7 +241,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
|
||||
if (customer.getLockStatus().equals(lockReqVO.getLockStatus())) {
|
||||
throw exception(customer.getLockStatus() ? CUSTOMER_LOCK_FAIL_IS_LOCK : CUSTOMER_UNLOCK_FAIL_IS_UNLOCK);
|
||||
}
|
||||
// 1.3 校验锁定上限。
|
||||
// 1.3 校验锁定上限
|
||||
if (lockReqVO.getLockStatus()) {
|
||||
validateCustomerExceedLockLimit(userId);
|
||||
}
|
||||
@ -239,11 +254,6 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
|
||||
LogRecordContext.putVariable("customer", customer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCustomerFollowUp(CrmUpdateFollowUpReqBO customerUpdateFollowUpReqBO) {
|
||||
customerMapper.updateById(BeanUtils.toBean(customerUpdateFollowUpReqBO, CrmCustomerDO.class).setId(customerUpdateFollowUpReqBO.getBizId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_CREATE_SUB_TYPE, bizNo = "{{#customer.id}}",
|
||||
@ -263,7 +273,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CrmCustomerImportRespVO importCustomerList(List<CrmCustomerImportExcelVO> importCustomers, CrmCustomerImportReqVO importReqVO) {
|
||||
public CrmCustomerImportRespVO importCustomerList(List<CrmCustomerImportExcelVO> importCustomers,
|
||||
CrmCustomerImportReqVO importReqVO) {
|
||||
if (CollUtil.isEmpty(importCustomers)) {
|
||||
throw exception(CUSTOMER_IMPORT_LIST_IS_EMPTY);
|
||||
}
|
||||
@ -383,12 +394,13 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
|
||||
// 1.4 校验负责人是否到达上限
|
||||
validateCustomerExceedOwnerLimit(ownerUserId, customers.size());
|
||||
|
||||
// 2.1 领取公海数据
|
||||
// 2. 领取公海数据
|
||||
List<CrmCustomerDO> updateCustomers = new ArrayList<>();
|
||||
List<CrmPermissionCreateReqBO> createPermissions = new ArrayList<>();
|
||||
customers.forEach(customer -> {
|
||||
// 2.1. 设置负责人
|
||||
updateCustomers.add(new CrmCustomerDO().setId(customer.getId()).setOwnerUserId(ownerUserId));
|
||||
updateCustomers.add(new CrmCustomerDO().setId(customer.getId())
|
||||
.setOwnerUserId(ownerUserId).setOwnerTime(LocalDateTime.now()));
|
||||
// 2.2. 创建负责人数据权限
|
||||
createPermissions.add(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())
|
||||
.setBizId(customer.getId()).setUserId(ownerUserId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
|
||||
@ -415,34 +427,23 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
|
||||
if (poolConfig == null || !poolConfig.getEnabled()) {
|
||||
return 0;
|
||||
}
|
||||
// 1.1 获取没有锁定的不在公海的客户
|
||||
List<CrmCustomerDO> customerList = customerMapper.selectListByLockAndNotPool(Boolean.FALSE);
|
||||
// TODO @puhui999:下面也搞到 sql 里去哈;写 or 查询,问题不大的;低 393 到 402;原因是,避免无用的太多数据查询到 java 进程里;
|
||||
List<CrmCustomerDO> poolCustomerList = new ArrayList<>();
|
||||
poolCustomerList.addAll(filterList(customerList, customer ->
|
||||
!customer.getDealStatus() && (poolConfig.getDealExpireDays() - LocalDateTimeUtils.between(customer.getCreateTime())) <= 0));
|
||||
poolCustomerList.addAll(filterList(customerList, customer -> {
|
||||
if (!customer.getDealStatus()) { // 这里只处理成交的
|
||||
return false;
|
||||
}
|
||||
LocalDateTime lastTime = ObjUtil.defaultIfNull(customer.getContactLastTime(), customer.getCreateTime());
|
||||
return (poolConfig.getContactExpireDays() - LocalDateTimeUtils.between(lastTime)) <= 0;
|
||||
}));
|
||||
|
||||
// 1. 获得需要放到的客户列表
|
||||
List<CrmCustomerDO> customerList = customerMapper.selectListByAutoPool(poolConfig);
|
||||
// 2. 逐个放入公海
|
||||
int count = 0;
|
||||
for (CrmCustomerDO customer : poolCustomerList) {
|
||||
for (CrmCustomerDO customer : customerList) {
|
||||
try {
|
||||
getSelf().putCustomerPool(customer);
|
||||
count++;
|
||||
} catch (Throwable e) {
|
||||
log.error("[autoPutCustomerPool][Customer 客户({}) 放入公海异常]", customer.getId(), e);
|
||||
log.error("[autoPutCustomerPool][客户({}) 放入公海异常]", customer.getId(), e);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private void putCustomerPool(CrmCustomerDO customer) {
|
||||
@Transactional // 需要 protected 修饰,因为需要在事务中调用
|
||||
protected void putCustomerPool(CrmCustomerDO customer) {
|
||||
// 1. 设置负责人为 NULL
|
||||
int updateOwnerUserIncr = customerMapper.updateOwnerUserIdById(customer.getId(), null);
|
||||
if (updateOwnerUserIncr == 0) {
|
||||
@ -486,17 +487,29 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<CrmCustomerDO> getPutInPoolRemindCustomerPage(CrmCustomerPageReqVO pageReqVO,
|
||||
CrmCustomerPoolConfigDO poolConfigDO,
|
||||
Long userId) {
|
||||
return customerMapper.selectPutInPoolRemindCustomerPage(pageReqVO, poolConfigDO, userId);
|
||||
public PageResult<CrmCustomerDO> getPutPoolRemindCustomerPage(CrmCustomerPageReqVO pageVO, Long userId) {
|
||||
CrmCustomerPoolConfigDO poolConfig = customerPoolConfigService.getCustomerPoolConfig();
|
||||
if (ObjUtil.isNull(poolConfig)
|
||||
|| Boolean.FALSE.equals(poolConfig.getEnabled())
|
||||
|| Boolean.FALSE.equals(poolConfig.getNotifyEnabled())) {
|
||||
return PageResult.empty();
|
||||
}
|
||||
return customerMapper.selectPutPoolRemindCustomerPage(pageVO, poolConfig, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getPutInPoolRemindCustomerCount(CrmCustomerPageReqVO pageReqVO,
|
||||
CrmCustomerPoolConfigDO poolConfigDO,
|
||||
Long userId) {
|
||||
return customerMapper.selectPutInPoolRemindCustomerCount(pageReqVO, poolConfigDO, userId);
|
||||
public Long getPutPoolRemindCustomerCount(Long userId) {
|
||||
CrmCustomerPoolConfigDO poolConfig = customerPoolConfigService.getCustomerPoolConfig();
|
||||
if (ObjUtil.isNull(poolConfig)
|
||||
|| Boolean.FALSE.equals(poolConfig.getEnabled())
|
||||
|| Boolean.FALSE.equals(poolConfig.getNotifyEnabled())) {
|
||||
return 0L;
|
||||
}
|
||||
CrmCustomerPageReqVO pageVO = new CrmCustomerPageReqVO()
|
||||
.setPool(null)
|
||||
.setContactStatus(CrmCustomerPageReqVO.CONTACT_TODAY)
|
||||
.setSceneType(CrmSceneTypeEnum.OWNER.getType());
|
||||
return customerMapper.selectPutPoolRemindCustomerCount(pageVO, poolConfig, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -596,7 +609,6 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获得自身的代理对象,解决 AOP 生效问题
|
||||
*
|
||||
|
@ -72,24 +72,24 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService {
|
||||
CrmFollowUpRecordDO followUpRecord = BeanUtils.toBean(createReqVO, CrmFollowUpRecordDO.class);
|
||||
crmFollowUpRecordMapper.insert(followUpRecord);
|
||||
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
CrmUpdateFollowUpReqBO updateFollowUpReqBO = new CrmUpdateFollowUpReqBO().setBizId(followUpRecord.getBizId())
|
||||
.setContactLastTime(now).setContactNextTime(followUpRecord.getNextTime()).setContactLastContent(followUpRecord.getContent());
|
||||
// 2. 更新 bizId 对应的记录;
|
||||
if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_BUSINESS.getType(), followUpRecord.getBizType())) { // 更新商机跟进信息
|
||||
CrmUpdateFollowUpReqBO updateFollowUpReqBO = new CrmUpdateFollowUpReqBO().setBizId(followUpRecord.getBizId())
|
||||
.setContactLastTime(LocalDateTime.now())
|
||||
.setContactNextTime(followUpRecord.getNextTime()).setContactLastContent(followUpRecord.getContent());
|
||||
if (ObjUtil.equal(CrmBizTypeEnum.CRM_BUSINESS.getType(), followUpRecord.getBizType())) { // 更新商机跟进信息
|
||||
businessService.updateBusinessFollowUpBatch(Collections.singletonList(updateFollowUpReqBO));
|
||||
}
|
||||
if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_CLUE.getType(), followUpRecord.getBizType())) { // 更新线索跟进信息
|
||||
if (ObjUtil.equal(CrmBizTypeEnum.CRM_CLUE.getType(), followUpRecord.getBizType())) { // 更新线索跟进信息
|
||||
clueService.updateClueFollowUp(followUpRecord.getId(), followUpRecord.getNextTime(), followUpRecord.getContent());
|
||||
}
|
||||
if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_CONTACT.getType(), followUpRecord.getBizType())) { // 更新联系人跟进信息
|
||||
if (ObjUtil.equal(CrmBizTypeEnum.CRM_CONTACT.getType(), followUpRecord.getBizType())) { // 更新联系人跟进信息
|
||||
contactService.updateContactFollowUpBatch(Collections.singletonList(updateFollowUpReqBO));
|
||||
}
|
||||
if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_CONTRACT.getType(), followUpRecord.getBizType())) { // 更新合同跟进信息
|
||||
if (ObjUtil.equal(CrmBizTypeEnum.CRM_CONTRACT.getType(), followUpRecord.getBizType())) { // 更新合同跟进信息
|
||||
contractService.updateContractFollowUp(updateFollowUpReqBO);
|
||||
}
|
||||
if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_CUSTOMER.getType(), followUpRecord.getBizType())) { // 更新客户跟进信息
|
||||
customerService.updateCustomerFollowUp(updateFollowUpReqBO);
|
||||
if (ObjUtil.equal(CrmBizTypeEnum.CRM_CUSTOMER.getType(), followUpRecord.getBizType())) { // 更新客户跟进信息
|
||||
customerService.updateCustomerFollowUp(followUpRecord.getBizId(), followUpRecord.getNextTime(), followUpRecord.getContent());
|
||||
}
|
||||
|
||||
// 3.1 更新 contactIds 对应的记录,不更新 lastTime 和 lastContent
|
||||
|
Loading…
Reference in New Issue
Block a user