crm:code review 销售漏斗
This commit is contained in:
parent
0eff2ae602
commit
ed5f3a6bc2
@ -177,7 +177,7 @@ public class CrmBusinessController {
|
||||
buildBusinessDetailList(list));
|
||||
}
|
||||
|
||||
private List<CrmBusinessRespVO> buildBusinessDetailList(List<CrmBusinessDO> list) {
|
||||
public List<CrmBusinessRespVO> buildBusinessDetailList(List<CrmBusinessDO> list) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
@ -1,28 +1,16 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.business.CrmBusinessController;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessRespVO;
|
||||
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.CrmStatisticsBusinessSummaryByDateRespVO;
|
||||
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.dataobject.business.CrmBusinessStatusDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
|
||||
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
|
||||
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessStatusService;
|
||||
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
|
||||
import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsFunnelService;
|
||||
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 io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
@ -33,14 +21,9 @@ import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
|
||||
@Tag(name = "管理后台 - CRM 销售漏斗")
|
||||
@RestController
|
||||
@ -48,31 +31,20 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
|
||||
@Validated
|
||||
public class CrmStatisticsFunnelController {
|
||||
|
||||
// TODO @puhui999:crmStatisticsFunnelService 改成 funnelService 更好点哈
|
||||
@Resource
|
||||
private CrmStatisticsFunnelService crmStatisticsFunnelService;
|
||||
@Resource
|
||||
private CrmBusinessService businessService;
|
||||
@Resource
|
||||
private CrmCustomerService customerService;
|
||||
@Resource
|
||||
private CrmBusinessStatusService businessStatusTypeService;
|
||||
@Resource
|
||||
private CrmBusinessStatusService businessStatusService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
|
||||
@GetMapping("/get-funnel-summary")
|
||||
@Operation(summary = "获取销售漏斗统计数据", description = "用于【销售漏斗】页面")
|
||||
@Operation(summary = "获取销售漏斗统计数据", description = "用于【销售漏斗】页面的【销售漏斗分析】")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-funnel:query')")
|
||||
public CommonResult<CrmStatisticFunnelRespVO> getFunnelSummary(@Valid CrmStatisticsFunnelReqVO reqVO) {
|
||||
return success(crmStatisticsFunnelService.getFunnelSummary(reqVO));
|
||||
}
|
||||
|
||||
// TODO @puhui:这个接口,应该是 getBusinessSummaryByEndStatus?这样更统一哈;
|
||||
@GetMapping("/get-business-end-status-summary")
|
||||
@Operation(summary = "获取商机结束状态统计", description = "用于【销售漏斗】页面")
|
||||
@Operation(summary = "获取商机结束状态统计", description = "用于【销售漏斗】页面的【销售漏斗分析】")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-funnel:query')")
|
||||
public CommonResult<List<CrmStatisticBusinessEndStatusRespVO>> getBusinessEndStatusSummary(@Valid CrmStatisticsFunnelReqVO reqVO) {
|
||||
return success(crmStatisticsFunnelService.getBusinessEndStatusSummary(reqVO));
|
||||
@ -86,7 +58,7 @@ public class CrmStatisticsFunnelController {
|
||||
}
|
||||
|
||||
@GetMapping("/get-business-page-by-date")
|
||||
@Operation(summary = "获得商机分页(按日期)", description = "用于【销售漏斗】页面")
|
||||
@Operation(summary = "获得商机分页(按日期)", description = "用于【销售漏斗】页面的【新增商机分析】")
|
||||
@PreAuthorize("@ss.hasPermission('crm:business:query')")
|
||||
public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessPageByDate(@Valid CrmStatisticsFunnelReqVO pageVO) {
|
||||
PageResult<CrmBusinessDO> pageResult = crmStatisticsFunnelService.getBusinessPageByDate(pageVO);
|
||||
@ -94,37 +66,7 @@ public class CrmStatisticsFunnelController {
|
||||
}
|
||||
|
||||
private List<CrmBusinessRespVO> buildBusinessDetailList(List<CrmBusinessDO> list) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 1.1 获取客户列表
|
||||
Map<Long, CrmCustomerDO> customerMap = customerService.getCustomerMap(
|
||||
convertSet(list, CrmBusinessDO::getCustomerId));
|
||||
// 1.2 获取创建人、负责人列表
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(list,
|
||||
contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
|
||||
// 1.3 获得商机状态组
|
||||
Map<Long, CrmBusinessStatusTypeDO> statusTypeMap = businessStatusTypeService.getBusinessStatusTypeMap(
|
||||
convertSet(list, CrmBusinessDO::getStatusTypeId));
|
||||
Map<Long, CrmBusinessStatusDO> statusMap = businessStatusService.getBusinessStatusMap(
|
||||
convertSet(list, CrmBusinessDO::getStatusId));
|
||||
// 2. 拼接数据
|
||||
return BeanUtils.toBean(list, CrmBusinessRespVO.class, businessVO -> {
|
||||
// 2.1 设置客户名称
|
||||
MapUtils.findAndThen(customerMap, businessVO.getCustomerId(), customer -> businessVO.setCustomerName(customer.getName()));
|
||||
// 2.2 设置创建人、负责人名称
|
||||
MapUtils.findAndThen(userMap, NumberUtils.parseLong(businessVO.getCreator()),
|
||||
user -> businessVO.setCreatorName(user.getNickname()));
|
||||
MapUtils.findAndThen(userMap, businessVO.getOwnerUserId(), user -> {
|
||||
businessVO.setOwnerUserName(user.getNickname());
|
||||
MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> businessVO.setOwnerUserDeptName(dept.getName()));
|
||||
});
|
||||
// 2.3 设置商机状态
|
||||
MapUtils.findAndThen(statusTypeMap, businessVO.getStatusTypeId(), statusType -> businessVO.setStatusTypeName(statusType.getName()));
|
||||
MapUtils.findAndThen(statusMap, businessVO.getStatusId(), status -> businessVO.setStatusName(
|
||||
businessService.getBusinessStatusName(businessVO.getEndStatus(), status)));
|
||||
});
|
||||
return SpringUtil.getBean(CrmBusinessController.class).buildBusinessDetailList(list);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
// TODO @puhui999:改成 CrmStatisticsBusinessSummaryByEndStatusRespVO,按照结束状态
|
||||
@Schema(description = "管理后台 - CRM 商机结束状态统计 Response VO")
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
|
@ -5,6 +5,7 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
// TODO @puhui999:改成 CrmStatisticFunnelSummaryRespVO 更有统计的味道
|
||||
@Schema(description = "管理后台 - CRM 销售漏斗 Response VO")
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ -17,6 +18,7 @@ public class CrmStatisticFunnelRespVO {
|
||||
@Schema(description = "商机数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long businessCount;
|
||||
|
||||
// TODO @puhui999:这个改成 businessWinCount 可能会更合适点哈;
|
||||
@Schema(description = "赢单数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long winCount;
|
||||
|
||||
|
@ -3,14 +3,12 @@ package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel;
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
// TODO @puhui999:是不是可以删除哈?
|
||||
@Schema(description = "管理后台 - CRM 商机 Response VO")
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
|
@ -15,6 +15,7 @@ public class CrmStatisticsBusinessSummaryByDateRespVO {
|
||||
@Schema(description = "新增商机数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer businessCreateCount;
|
||||
|
||||
// TODO @puhui999:是不是金额哈,不是数量
|
||||
@Schema(description = "新增商机金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private BigDecimal businessDealCount;
|
||||
|
||||
|
@ -17,6 +17,7 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
|
||||
|
||||
@Schema(description = "管理后台 - CRM 销售漏斗 Request VO")
|
||||
@Data
|
||||
// TODO @puhui999:不用写 EqualsAndHashCode、ToString,已经全局 lombok 啦
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class CrmStatisticsFunnelReqVO extends PageParam {
|
||||
@ -42,6 +43,7 @@ public class CrmStatisticsFunnelReqVO extends PageParam {
|
||||
@InEnum(value = DateIntervalEnum.class, message = "时间间隔类型,必须是 {value}")
|
||||
private Integer interval;
|
||||
|
||||
// TODO @puhui999:这个全部前端传递哈;参考 CrmStatisticsCustomerReqVO
|
||||
/**
|
||||
* 前端如果选择自定义时间, 那么前端传递起始-终止时间, 如果选择其他时间间隔类型, 则由后台计算起始-终止时间
|
||||
* 并作为参数传递给Mapper
|
||||
|
@ -73,6 +73,7 @@ public interface CrmBusinessMapper extends BaseMapperX<CrmBusinessDO> {
|
||||
.betweenIfPresent(CrmBusinessDO::getCreateTime, times));
|
||||
}
|
||||
|
||||
// TODO @puhui999:这个可以优化下,通过统计 sql,不通过内存计算;
|
||||
default List<CrmBusinessDO> selectListByOwnerUserIdsAndEndStatusNotNull(Collection<Long> ownerUserIds, LocalDateTime[] times) {
|
||||
return selectList(new LambdaQueryWrapperX<CrmBusinessDO>()
|
||||
.in(CrmBusinessDO::getOwnerUserId, ownerUserIds)
|
||||
@ -80,6 +81,7 @@ public interface CrmBusinessMapper extends BaseMapperX<CrmBusinessDO> {
|
||||
.isNotNull(CrmBusinessDO::getEndStatus));
|
||||
}
|
||||
|
||||
// TODO @puhui999:这个可以优化下,通过统计 sql,不通过内存计算;
|
||||
default List<CrmBusinessDO> selectListByOwnerUserIdsAndDate(Collection<Long> ownerUserIds, LocalDateTime[] times) {
|
||||
return selectList(new LambdaQueryWrapperX<CrmBusinessDO>()
|
||||
.in(CrmBusinessDO::getOwnerUserId, ownerUserIds)
|
||||
|
@ -19,11 +19,9 @@ import org.apache.ibatis.annotations.Mapper;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import javax.management.ObjectName;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 客户 Mapper
|
||||
@ -188,6 +186,7 @@ public interface CrmCustomerMapper extends BaseMapperX<CrmCustomerDO> {
|
||||
return selectCount(query);
|
||||
}
|
||||
|
||||
// TODO @puhui999:这个可以优化下,通过统计 sql,不通过内存计算;
|
||||
default Long selectCountByOwnerUserIds(Collection<Long> ownerUserIds, LocalDateTime[] times){
|
||||
return selectCount(new LambdaQueryWrapperX<CrmCustomerDO>()
|
||||
.in(CrmCustomerDO::getOwnerUserId, ownerUserIds)
|
||||
|
@ -199,11 +199,12 @@ public interface CrmBusinessService {
|
||||
*
|
||||
* @param ownerUserIds 负责人编号
|
||||
* @param times 时间范围
|
||||
* @param endStatus 商机结束状态
|
||||
* @param endStatus 商机结束状态,允许为空
|
||||
* @return 商机数
|
||||
*/
|
||||
Long getBusinessCountByOwnerUserIdsAndEndStatus(List<Long> ownerUserIds, LocalDateTime[] times, Integer endStatus);
|
||||
|
||||
// TODO @puhui999:这个可以优化下,通过统计 sql,不通过内存计算;
|
||||
/**
|
||||
* 获得商机列表【数据统计】
|
||||
*
|
||||
@ -213,6 +214,7 @@ public interface CrmBusinessService {
|
||||
*/
|
||||
List<CrmBusinessDO> getBusinessListByOwnerUserIdsAndEndStatusNotNull(List<Long> ownerUserIds, LocalDateTime[] times);
|
||||
|
||||
// TODO @puhui999:这个可以优化下,通过统计 sql,不通过内存计算;
|
||||
/**
|
||||
* 获得商机列表【数据统计】
|
||||
*
|
||||
@ -223,10 +225,11 @@ public interface CrmBusinessService {
|
||||
List<CrmBusinessDO> getBusinessListByOwnerUserIdsAndDate(List<Long> ownerUserIds, LocalDateTime[] times);
|
||||
|
||||
/**
|
||||
* 商机分页【数据统计】
|
||||
* 获得商机分页,目前用于【数据统计】
|
||||
*
|
||||
* @param ownerUserIds 负责人编号
|
||||
* @param times 时间范围
|
||||
* @param pageNo 页码
|
||||
* @param pageNo 页码 TODO @puhui999:直接传递 CrmStatisticsFunnelReqVO 吧,虽然有点耦合,但是更清晰一点;
|
||||
* @param pageSize 数量
|
||||
* @return 商机分页
|
||||
*/
|
||||
|
@ -383,6 +383,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
|
||||
return businessMapper.selectCountByOwnerUserIdsAndEndStatus(convertSet(ownerUserIds), times, endStatus);
|
||||
}
|
||||
|
||||
// TODO @puhui999:这个可以优化下,通过统计 sql,不通过内存计算;
|
||||
@Override
|
||||
public List<CrmBusinessDO> getBusinessListByOwnerUserIdsAndEndStatusNotNull(List<Long> ownerUserIds, LocalDateTime[] times) {
|
||||
if (CollUtil.isEmpty(ownerUserIds)) {
|
||||
@ -391,6 +392,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
|
||||
return businessMapper.selectListByOwnerUserIdsAndEndStatusNotNull(convertSet(ownerUserIds), times);
|
||||
}
|
||||
|
||||
// TODO @puhui999:这个可以优化下,通过统计 sql,不通过内存计算;
|
||||
@Override
|
||||
public List<CrmBusinessDO> getBusinessListByOwnerUserIdsAndDate(List<Long> ownerUserIds, LocalDateTime[] times) {
|
||||
if (CollUtil.isEmpty(ownerUserIds)) {
|
||||
|
@ -51,6 +51,7 @@ public class CrmStatisticsFunnelServiceImpl implements CrmStatisticsFunnelServic
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
|
||||
// TODO @puhui999:貌似想了下,可能还是得按照;;;
|
||||
@Override
|
||||
public CrmStatisticFunnelRespVO getFunnelSummary(CrmStatisticsFunnelReqVO reqVO) {
|
||||
// 1. 获得用户编号数组
|
||||
@ -76,6 +77,7 @@ public class CrmStatisticsFunnelServiceImpl implements CrmStatisticsFunnelServic
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// TODO @puhui999:这个可以优化下,通过统计 sql,不通过内存计算;
|
||||
// 2.1 获得用户负责的商机
|
||||
List<CrmBusinessDO> businessList = businessService.getBusinessListByOwnerUserIdsAndEndStatusNotNull(reqVO.getUserIds(), reqVO.getTimes());
|
||||
// 2.2 统计各阶段数据
|
||||
@ -99,6 +101,7 @@ public class CrmStatisticsFunnelServiceImpl implements CrmStatisticsFunnelServic
|
||||
}
|
||||
|
||||
// 2. 按天统计,获取分项统计数据
|
||||
// TODO @puhui999:可以这个统计,返回的时候,就把数量、金额一起统计好;
|
||||
List<CrmStatisticsBusinessSummaryByDateRespVO> businessCreateCountList = funnelMapper.selectBusinessCreateCountGroupByDate(reqVO);
|
||||
List<CrmBusinessDO> businessList = businessService.getBusinessListByOwnerUserIdsAndDate(reqVO.getUserIds(), reqVO.getTimes());
|
||||
Map<String, BigDecimal> businessDealCountMap = businessList.stream().collect(Collectors.groupingBy(business ->
|
||||
@ -128,7 +131,7 @@ public class CrmStatisticsFunnelServiceImpl implements CrmStatisticsFunnelServic
|
||||
if (CollUtil.isEmpty(pageVO.getUserIds())) {
|
||||
return PageResult.empty();
|
||||
}
|
||||
|
||||
// 2. 执行查询
|
||||
return businessService.getBusinessPageByDate(pageVO.getUserIds(), pageVO.getTimes(), pageVO.getPageNo(), pageVO.getPageSize());
|
||||
}
|
||||
|
||||
|
@ -4,15 +4,16 @@
|
||||
|
||||
<select id="selectBusinessCreateCountGroupByDate"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByDateRespVO">
|
||||
SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS time, COUNT(*) AS businessCreateCount
|
||||
SELECT
|
||||
DATE_FORMAT(create_time, '%Y-%m-%d') AS time,
|
||||
COUNT(*) AS businessCreateCount
|
||||
FROM crm_business
|
||||
WHERE deleted = 0
|
||||
AND owner_user_id IN
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
|
||||
#{times[1],javaType=java.time.LocalDateTime}
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
|
||||
GROUP BY time
|
||||
</select>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user