CRM: 完善销售漏斗分析
This commit is contained in:
parent
c4ce9068f9
commit
6c9a3b0e11
|
@ -97,6 +97,10 @@ public class CollectionUtils {
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> Set<T> convertSet(Collection<T> from) {
|
||||||
|
return convertSet(from, v -> v);
|
||||||
|
}
|
||||||
|
|
||||||
public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func) {
|
public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func) {
|
||||||
if (CollUtil.isEmpty(from)) {
|
if (CollUtil.isEmpty(from)) {
|
||||||
return new HashSet<>();
|
return new HashSet<>();
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.statistics;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
|
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticBusinessEndStatusRespVO;
|
||||||
|
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticFunnelRespVO;
|
||||||
|
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsFunnelReqVO;
|
||||||
|
import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsFunnelService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
|
|
||||||
|
@Tag(name = "管理后台 - CRM 销售漏斗")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/crm/statistics-funnel")
|
||||||
|
@Validated
|
||||||
|
public class CrmStatisticsFunnelController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private CrmStatisticsFunnelService crmStatisticsFunnelService;
|
||||||
|
|
||||||
|
@GetMapping("/get-funnel-summary")
|
||||||
|
@Operation(summary = "获取销售漏斗统计数据", description = "用于【销售漏斗】页面")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:statistics-funnel:query')")
|
||||||
|
public CommonResult<CrmStatisticFunnelRespVO> getFunnelSummary(@Valid CrmStatisticsFunnelReqVO reqVO) {
|
||||||
|
return success(crmStatisticsFunnelService.getFunnelSummary(reqVO));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@GetMapping("/get-business-end-status-summary")
|
||||||
|
@Operation(summary = "获取商机结束状态统计", description = "用于【销售漏斗】页面")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:statistics-funnel:query')")
|
||||||
|
public CommonResult<List<CrmStatisticBusinessEndStatusRespVO>> getBusinessEndStatusSummary(@Valid CrmStatisticsFunnelReqVO reqVO) {
|
||||||
|
return success(crmStatisticsFunnelService.getBusinessEndStatusSummary(reqVO));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - CRM 商机结束状态统计 Response VO")
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Data
|
||||||
|
public class CrmStatisticBusinessEndStatusRespVO {
|
||||||
|
|
||||||
|
@Schema(description = "结束状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
|
private Integer endStatus;
|
||||||
|
|
||||||
|
@Schema(description = "商机数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
|
private Long businessCount;
|
||||||
|
|
||||||
|
@Schema(description = "商机总金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
|
private BigDecimal totalPrice;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - CRM 销售漏斗 Response VO")
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Data
|
||||||
|
public class CrmStatisticFunnelRespVO {
|
||||||
|
|
||||||
|
@Schema(description = "客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
|
private Long customerCount;
|
||||||
|
|
||||||
|
@Schema(description = "商机数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
|
private Long businessCount;
|
||||||
|
|
||||||
|
@Schema(description = "赢单数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
|
private Long winCount;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - CRM 销售漏斗 Request VO")
|
||||||
|
@Data
|
||||||
|
public class CrmStatisticsFunnelReqVO {
|
||||||
|
|
||||||
|
@Schema(description = "部门 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
|
@NotNull(message = "部门 id 不能为空")
|
||||||
|
private Long deptId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 负责人用户 id, 当用户为空, 则计算部门下用户
|
||||||
|
*/
|
||||||
|
@Schema(description = "负责人用户 id", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1")
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* userIds 目前不用前端传递,目前是方便后端通过 deptId 读取编号后,设置回来
|
||||||
|
* 后续,可能会支持选择部分用户进行查询
|
||||||
|
*/
|
||||||
|
@Schema(description = "负责人用户 id 集合", hidden = true, example = "2")
|
||||||
|
private List<Long> userIds;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 前端如果选择自定义时间, 那么前端传递起始-终止时间, 如果选择其他时间间隔类型, 则由后台计算起始-终止时间
|
||||||
|
* 并作为参数传递给Mapper
|
||||||
|
*/
|
||||||
|
@Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime[] times;
|
||||||
|
|
||||||
|
}
|
|
@ -6,14 +6,15 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
|
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
|
||||||
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO;
|
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO;
|
||||||
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
|
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
|
||||||
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
|
|
||||||
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.CrmPermissionUtils;
|
import cn.iocoder.yudao.module.crm.util.CrmPermissionUtils;
|
||||||
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 java.time.LocalDateTime;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 商机 Mapper
|
* 商机 Mapper
|
||||||
|
@ -59,10 +60,24 @@ public interface CrmBusinessMapper extends BaseMapperX<CrmBusinessDO> {
|
||||||
return selectCount(CrmBusinessDO::getStatusTypeId, statusTypeId);
|
return selectCount(CrmBusinessDO::getStatusTypeId, statusTypeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
default List<CrmBusinessDO> selectListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId){
|
default List<CrmBusinessDO> selectListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) {
|
||||||
return selectList(new LambdaQueryWrapperX<CrmBusinessDO>()
|
return selectList(new LambdaQueryWrapperX<CrmBusinessDO>()
|
||||||
.eq(CrmBusinessDO::getCustomerId, customerId)
|
.eq(CrmBusinessDO::getCustomerId, customerId)
|
||||||
.eq(CrmBusinessDO::getOwnerUserId, ownerUserId));
|
.eq(CrmBusinessDO::getOwnerUserId, ownerUserId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default Long selectCountByOwnerUserIdsAndEndStatus(Collection<Long> ownerUserIds, LocalDateTime[] times, Integer endStatus) {
|
||||||
|
return selectCount(new LambdaQueryWrapperX<CrmBusinessDO>()
|
||||||
|
.in(CrmBusinessDO::getOwnerUserId, ownerUserIds)
|
||||||
|
.eqIfPresent(CrmBusinessDO::getEndStatus, endStatus)
|
||||||
|
.betweenIfPresent(CrmBusinessDO::getCreateTime, times));
|
||||||
|
}
|
||||||
|
|
||||||
|
default List<CrmBusinessDO> selectListByOwnerUserIdsAndEndStatusNotNull(Collection<Long> ownerUserIds, LocalDateTime[] times){
|
||||||
|
return selectList(new LambdaQueryWrapperX<CrmBusinessDO>()
|
||||||
|
.in(CrmBusinessDO::getOwnerUserId, ownerUserIds)
|
||||||
|
.betweenIfPresent(CrmBusinessDO::getCreateTime, times)
|
||||||
|
.isNotNull(CrmBusinessDO::getEndStatus));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,11 @@ import org.apache.ibatis.annotations.Mapper;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
import javax.management.ObjectName;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 客户 Mapper
|
* 客户 Mapper
|
||||||
|
@ -186,4 +188,10 @@ public interface CrmCustomerMapper extends BaseMapperX<CrmCustomerDO> {
|
||||||
return selectCount(query);
|
return selectCount(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default Long selectCountByOwnerUserIds(Collection<Long> ownerUserIds, LocalDateTime[] times){
|
||||||
|
return selectCount(new LambdaQueryWrapperX<CrmCustomerDO>()
|
||||||
|
.in(CrmCustomerDO::getOwnerUserId, ownerUserIds)
|
||||||
|
.betweenIfPresent(CrmCustomerDO::getCreateTime, times));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
package cn.iocoder.yudao.module.crm.dal.mysql.statistics;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CRM 销售漏斗 Mapper
|
||||||
|
*
|
||||||
|
* @author HUIHUI
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface CrmStatisticsFunnelMapper {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -194,4 +194,23 @@ public interface CrmBusinessService {
|
||||||
*/
|
*/
|
||||||
List<CrmBusinessDO> getBusinessListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId);
|
List<CrmBusinessDO> getBusinessListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得商机数
|
||||||
|
*
|
||||||
|
* @param ownerUserIds 负责人编号
|
||||||
|
* @param times 时间范围
|
||||||
|
* @param endStatus 商机结束状态
|
||||||
|
* @return 商机数
|
||||||
|
*/
|
||||||
|
Long getBusinessCountByOwnerUserIdsAndEndStatus(List<Long> ownerUserIds, LocalDateTime[] times, Integer endStatus);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得商机列表【数据统计】
|
||||||
|
*
|
||||||
|
* @param ownerUserIds 负责人编号
|
||||||
|
* @param times 时间范围
|
||||||
|
* @return 商机列表
|
||||||
|
*/
|
||||||
|
List<CrmBusinessDO> getBusinessListByOwnerUserIdsAndEndStatusNotNull(List<Long> ownerUserIds, LocalDateTime[] times);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -375,4 +375,20 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
|
||||||
return businessMapper.selectListByCustomerIdOwnerUserId(customerId, ownerUserId);
|
return businessMapper.selectListByCustomerIdOwnerUserId(customerId, ownerUserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getBusinessCountByOwnerUserIdsAndEndStatus(List<Long> ownerUserIds, LocalDateTime[] times, Integer endStatus) {
|
||||||
|
if (CollUtil.isEmpty(ownerUserIds)) {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
return businessMapper.selectCountByOwnerUserIdsAndEndStatus(convertSet(ownerUserIds), times, endStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CrmBusinessDO> getBusinessListByOwnerUserIdsAndEndStatusNotNull(List<Long> ownerUserIds, LocalDateTime[] times) {
|
||||||
|
if (CollUtil.isEmpty(ownerUserIds)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return businessMapper.selectListByOwnerUserIdsAndEndStatusNotNull(convertSet(ownerUserIds), times);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ public interface CrmCustomerService {
|
||||||
/**
|
/**
|
||||||
* 更新客户的跟进状态
|
* 更新客户的跟进状态
|
||||||
*
|
*
|
||||||
* @param id 编号
|
* @param id 编号
|
||||||
* @param dealStatus 跟进状态
|
* @param dealStatus 跟进状态
|
||||||
*/
|
*/
|
||||||
void updateCustomerDealStatus(Long id, Boolean dealStatus);
|
void updateCustomerDealStatus(Long id, Boolean dealStatus);
|
||||||
|
@ -47,8 +47,8 @@ public interface CrmCustomerService {
|
||||||
/**
|
/**
|
||||||
* 更新客户相关的跟进信息
|
* 更新客户相关的跟进信息
|
||||||
*
|
*
|
||||||
* @param id 编号
|
* @param id 编号
|
||||||
* @param contactNextTime 下次联系时间
|
* @param contactNextTime 下次联系时间
|
||||||
* @param contactLastContent 最后联系内容
|
* @param contactLastContent 最后联系内容
|
||||||
*/
|
*/
|
||||||
void updateCustomerFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent);
|
void updateCustomerFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent);
|
||||||
|
@ -99,8 +99,8 @@ public interface CrmCustomerService {
|
||||||
/**
|
/**
|
||||||
* 获得放入公海提醒的客户分页
|
* 获得放入公海提醒的客户分页
|
||||||
*
|
*
|
||||||
* @param pageVO 分页查询
|
* @param pageVO 分页查询
|
||||||
* @param userId 用户编号
|
* @param userId 用户编号
|
||||||
* @return 客户分页
|
* @return 客户分页
|
||||||
*/
|
*/
|
||||||
PageResult<CrmCustomerDO> getPutPoolRemindCustomerPage(CrmCustomerPageReqVO pageVO, Long userId);
|
PageResult<CrmCustomerDO> getPutPoolRemindCustomerPage(CrmCustomerPageReqVO pageVO, Long userId);
|
||||||
|
@ -108,7 +108,7 @@ public interface CrmCustomerService {
|
||||||
/**
|
/**
|
||||||
* 获得待进入公海的客户数量
|
* 获得待进入公海的客户数量
|
||||||
*
|
*
|
||||||
* @param userId 用户编号
|
* @param userId 用户编号
|
||||||
* @return 提醒数量
|
* @return 提醒数量
|
||||||
*/
|
*/
|
||||||
Long getPutPoolRemindCustomerCount(Long userId);
|
Long getPutPoolRemindCustomerCount(Long userId);
|
||||||
|
@ -195,4 +195,13 @@ public interface CrmCustomerService {
|
||||||
*/
|
*/
|
||||||
int autoPutCustomerPool();
|
int autoPutCustomerPool();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得客户数
|
||||||
|
*
|
||||||
|
* @param ownerUserIds 负责人编号
|
||||||
|
* @param times 时间范围
|
||||||
|
* @return 客户数
|
||||||
|
*/
|
||||||
|
Long getCustomerCountByOwnerUserIds(List<Long> ownerUserIds, LocalDateTime[] times);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,7 @@ import java.time.LocalDateTime;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
|
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.ErrorCodeConstants.*;
|
||||||
import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
|
import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
|
||||||
|
@ -650,6 +651,14 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getCustomerCountByOwnerUserIds(List<Long> ownerUserIds, LocalDateTime[] times) {
|
||||||
|
if (CollUtil.isEmpty(ownerUserIds)) {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
return customerMapper.selectCountByOwnerUserIds(convertSet(ownerUserIds), times);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得自身的代理对象,解决 AOP 生效问题
|
* 获得自身的代理对象,解决 AOP 生效问题
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
package cn.iocoder.yudao.module.crm.service.statistics;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticBusinessEndStatusRespVO;
|
||||||
|
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticFunnelRespVO;
|
||||||
|
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsFunnelReqVO;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CRM 销售漏斗分析 Service
|
||||||
|
*
|
||||||
|
* @author HUIHUI
|
||||||
|
*/
|
||||||
|
public interface CrmStatisticsFunnelService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得销售漏斗数据
|
||||||
|
*
|
||||||
|
* @param reqVO 请求
|
||||||
|
* @return 销售漏斗数据
|
||||||
|
*/
|
||||||
|
CrmStatisticFunnelRespVO getFunnelSummary(CrmStatisticsFunnelReqVO reqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得商机结束状态统计
|
||||||
|
*
|
||||||
|
* @param reqVO 请求
|
||||||
|
* @return 商机结束状态统计
|
||||||
|
*/
|
||||||
|
List<CrmStatisticBusinessEndStatusRespVO> getBusinessEndStatusSummary(CrmStatisticsFunnelReqVO reqVO);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
package cn.iocoder.yudao.module.crm.service.statistics;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.util.ObjUtil;
|
||||||
|
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticBusinessEndStatusRespVO;
|
||||||
|
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticFunnelRespVO;
|
||||||
|
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsFunnelReqVO;
|
||||||
|
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
|
||||||
|
import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsFunnelMapper;
|
||||||
|
import cn.iocoder.yudao.module.crm.enums.business.CrmBusinessEndStatusEnum;
|
||||||
|
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
|
||||||
|
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
|
||||||
|
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||||
|
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||||
|
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||||
|
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CRM 销售漏斗分析 Service 实现类
|
||||||
|
*
|
||||||
|
* @author HUIHUI
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class CrmStatisticsFunnelServiceImpl implements CrmStatisticsFunnelService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private CrmStatisticsFunnelMapper funnelMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private AdminUserApi adminUserApi;
|
||||||
|
@Resource
|
||||||
|
private CrmCustomerService customerService;
|
||||||
|
@Resource
|
||||||
|
private CrmBusinessService businessService;
|
||||||
|
@Resource
|
||||||
|
private DeptApi deptApi;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CrmStatisticFunnelRespVO getFunnelSummary(CrmStatisticsFunnelReqVO reqVO) {
|
||||||
|
// 1. 获得用户编号数组
|
||||||
|
List<Long> userIds = getUserIds(reqVO);
|
||||||
|
if (CollUtil.isEmpty(userIds)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
reqVO.setUserIds(userIds);
|
||||||
|
|
||||||
|
// 2. 获得漏斗数据
|
||||||
|
return new CrmStatisticFunnelRespVO(
|
||||||
|
customerService.getCustomerCountByOwnerUserIds(userIds, reqVO.getTimes()),
|
||||||
|
businessService.getBusinessCountByOwnerUserIdsAndEndStatus(userIds, reqVO.getTimes(), null),
|
||||||
|
businessService.getBusinessCountByOwnerUserIdsAndEndStatus(userIds, reqVO.getTimes(), CrmBusinessEndStatusEnum.WIN.getStatus())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CrmStatisticBusinessEndStatusRespVO> getBusinessEndStatusSummary(CrmStatisticsFunnelReqVO reqVO) {
|
||||||
|
// 1. 获得用户编号数组
|
||||||
|
List<Long> userIds = getUserIds(reqVO);
|
||||||
|
if (CollUtil.isEmpty(userIds)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
reqVO.setUserIds(userIds);
|
||||||
|
|
||||||
|
// 2.1 获得用户负责的商机
|
||||||
|
List<CrmBusinessDO> businessList = businessService.getBusinessListByOwnerUserIdsAndEndStatusNotNull(userIds, reqVO.getTimes());
|
||||||
|
// 2.2 统计各阶段数据
|
||||||
|
Map<Integer, List<CrmBusinessDO>> businessMap = convertMultiMap(businessList, CrmBusinessDO::getEndStatus);
|
||||||
|
return convertList(CrmBusinessEndStatusEnum.values(), endStatusEnum -> {
|
||||||
|
List<CrmBusinessDO> list = businessMap.get(endStatusEnum.getStatus());
|
||||||
|
if (CollUtil.isEmpty(list)) {
|
||||||
|
return new CrmStatisticBusinessEndStatusRespVO(endStatusEnum.getStatus(), 0L, BigDecimal.ZERO);
|
||||||
|
}
|
||||||
|
return new CrmStatisticBusinessEndStatusRespVO(endStatusEnum.getStatus(), (long) list.size(),
|
||||||
|
getSumValue(list, CrmBusinessDO::getTotalPrice, BigDecimal::add));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户编号数组。如果用户编号为空, 则获得部门下的用户编号数组,包括子部门的所有用户编号
|
||||||
|
*
|
||||||
|
* @param reqVO 请求参数
|
||||||
|
* @return 用户编号数组
|
||||||
|
*/
|
||||||
|
private List<Long> getUserIds(CrmStatisticsFunnelReqVO reqVO) {
|
||||||
|
// 情况一:选中某个用户
|
||||||
|
if (ObjUtil.isNotNull(reqVO.getUserId())) {
|
||||||
|
return List.of(reqVO.getUserId());
|
||||||
|
}
|
||||||
|
// 情况二:选中某个部门
|
||||||
|
// 2.1 获得部门列表
|
||||||
|
List<Long> deptIds = convertList(deptApi.getChildDeptList(reqVO.getDeptId()), DeptRespDTO::getId);
|
||||||
|
deptIds.add(reqVO.getDeptId());
|
||||||
|
// 2.2 获得用户编号
|
||||||
|
return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -20,7 +20,6 @@ import java.util.Map;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CRM 客户画像 Service 实现类
|
* CRM 客户画像 Service 实现类
|
||||||
|
@ -55,15 +54,18 @@ public class CrmStatisticsPortraitServiceImpl implements CrmStatisticsPortraitSe
|
||||||
|
|
||||||
// 3. 拼接数据
|
// 3. 拼接数据
|
||||||
List<Area> areaList = AreaUtils.getByType(AreaTypeEnum.PROVINCE, area -> area);
|
List<Area> areaList = AreaUtils.getByType(AreaTypeEnum.PROVINCE, area -> area);
|
||||||
areaList.add(new Area().setId(null).setName("未知")); // TODO @puhui999:是不是 65 find 的逻辑改下;不用 findAndThen,直接从 areaMap 拿;拿到就设置,不拿到就设置 null 和 未知;这样,58 本行可以删除掉完事了;这样代码更简单和一致
|
|
||||||
Map<Integer, Area> areaMap = convertMap(areaList, Area::getId);
|
Map<Integer, Area> areaMap = convertMap(areaList, Area::getId);
|
||||||
return convertList(list, item -> {
|
return convertList(list, item -> {
|
||||||
Integer parentId = AreaUtils.getParentIdByType(item.getAreaId(), AreaTypeEnum.PROVINCE);
|
Integer parentId = AreaUtils.getParentIdByType(item.getAreaId(), AreaTypeEnum.PROVINCE);
|
||||||
if (parentId == null) { // 找不到,归到未知
|
if (parentId != null) {
|
||||||
return item.setAreaId(null).setAreaName("未知");
|
Area area = areaMap.get(parentId);
|
||||||
|
if (area != null) {
|
||||||
|
item.setAreaId(parentId).setAreaName(area.getName());
|
||||||
|
return item;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
findAndThen(areaMap, parentId, area -> item.setAreaId(parentId).setAreaName(area.getName()));
|
// 找不到,归到未知
|
||||||
return item;
|
return item.setAreaId(null).setAreaName("未知");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsFunnelMapper">
|
||||||
|
|
||||||
|
</mapper>
|
Loading…
Reference in New Issue