Browse Source

!949 CRM: 新增商机赢单转化率分析
Merge pull request !949 from puhui999/develop

芋道源码 1 year ago
parent
commit
cc82e471ea
18 changed files with 190 additions and 322 deletions
  1. 16 14
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsFunnelController.java
  2. 2 4
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/funnel/CrmStatisticFunnelSummaryRespVO.java
  3. 21 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/funnel/CrmStatisticsBusinessInversionRateSummaryByDateRespVO.java
  4. 0 107
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/funnel/CrmStatisticsBusinessRespVO.java
  5. 2 3
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/funnel/CrmStatisticsBusinessSummaryByDateRespVO.java
  6. 1 2
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/funnel/CrmStatisticsBusinessSummaryByEndStatusRespVO.java
  7. 2 10
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/funnel/CrmStatisticsFunnelReqVO.java
  8. 2 4
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticsPortraitReqVO.java
  9. 5 28
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java
  10. 0 7
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java
  11. 12 2
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsFunnelMapper.java
  12. 3 35
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessService.java
  13. 3 29
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java
  14. 0 9
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java
  15. 0 8
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
  16. 11 6
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsFunnelService.java
  17. 43 47
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsFunnelServiceImpl.java
  18. 67 7
      yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsFunnelMapper.xml

+ 16 - 14
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsFunnelController.java

@@ -5,10 +5,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.crm.controller.admin.business.CrmBusinessController;
 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.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.controller.admin.statistics.vo.funnel.*;
 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.service.statistics.CrmStatisticsFunnelService;
 import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsFunnelService;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Operation;
@@ -31,37 +28,42 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 @Validated
 @Validated
 public class CrmStatisticsFunnelController {
 public class CrmStatisticsFunnelController {
 
 
-    // TODO @puhui999:crmStatisticsFunnelService 改成 funnelService 更好点哈
     @Resource
     @Resource
-    private CrmStatisticsFunnelService crmStatisticsFunnelService;
+    private CrmStatisticsFunnelService funnelService;
 
 
     @GetMapping("/get-funnel-summary")
     @GetMapping("/get-funnel-summary")
     @Operation(summary = "获取销售漏斗统计数据", description = "用于【销售漏斗】页面的【销售漏斗分析】")
     @Operation(summary = "获取销售漏斗统计数据", description = "用于【销售漏斗】页面的【销售漏斗分析】")
     @PreAuthorize("@ss.hasPermission('crm:statistics-funnel:query')")
     @PreAuthorize("@ss.hasPermission('crm:statistics-funnel:query')")
-    public CommonResult<CrmStatisticFunnelRespVO> getFunnelSummary(@Valid CrmStatisticsFunnelReqVO reqVO) {
-        return success(crmStatisticsFunnelService.getFunnelSummary(reqVO));
+    public CommonResult<CrmStatisticFunnelSummaryRespVO> getFunnelSummary(@Valid CrmStatisticsFunnelReqVO reqVO) {
+        return success(funnelService.getFunnelSummary(reqVO));
     }
     }
 
 
-    // TODO @puhui:这个接口,应该是 getBusinessSummaryByEndStatus?这样更统一哈;
-    @GetMapping("/get-business-end-status-summary")
+    @GetMapping("/get-business-summary-by-end-status")
     @Operation(summary = "获取商机结束状态统计", description = "用于【销售漏斗】页面的【销售漏斗分析】")
     @Operation(summary = "获取商机结束状态统计", description = "用于【销售漏斗】页面的【销售漏斗分析】")
     @PreAuthorize("@ss.hasPermission('crm:statistics-funnel:query')")
     @PreAuthorize("@ss.hasPermission('crm:statistics-funnel:query')")
-    public CommonResult<List<CrmStatisticBusinessEndStatusRespVO>> getBusinessEndStatusSummary(@Valid CrmStatisticsFunnelReqVO reqVO) {
-        return success(crmStatisticsFunnelService.getBusinessEndStatusSummary(reqVO));
+    public CommonResult<List<CrmStatisticsBusinessSummaryByEndStatusRespVO>> getBusinessSummaryByEndStatus(@Valid CrmStatisticsFunnelReqVO reqVO) {
+        return success(funnelService.getBusinessSummaryByEndStatus(reqVO));
     }
     }
 
 
     @GetMapping("/get-business-summary-by-date")
     @GetMapping("/get-business-summary-by-date")
     @Operation(summary = "获取新增商机分析(按日期)", description = "用于【销售漏斗】页面")
     @Operation(summary = "获取新增商机分析(按日期)", description = "用于【销售漏斗】页面")
     @PreAuthorize("@ss.hasPermission('crm:statistics-funnel:query')")
     @PreAuthorize("@ss.hasPermission('crm:statistics-funnel:query')")
     public CommonResult<List<CrmStatisticsBusinessSummaryByDateRespVO>> getBusinessSummaryByDate(@Valid CrmStatisticsFunnelReqVO reqVO) {
     public CommonResult<List<CrmStatisticsBusinessSummaryByDateRespVO>> getBusinessSummaryByDate(@Valid CrmStatisticsFunnelReqVO reqVO) {
-        return success(crmStatisticsFunnelService.getBusinessSummaryByDate(reqVO));
+        return success(funnelService.getBusinessSummaryByDate(reqVO));
+    }
+
+    @GetMapping("/get-business-inversion-rate-summary-by-date")
+    @Operation(summary = "获取商机转化率分析(按日期)", description = "用于【销售漏斗】页面")
+    @PreAuthorize("@ss.hasPermission('crm:statistics-funnel:query')")
+    public CommonResult<List<CrmStatisticsBusinessInversionRateSummaryByDateRespVO>> getBusinessInversionRateSummaryByDate(@Valid CrmStatisticsFunnelReqVO reqVO) {
+        return success(funnelService.getBusinessInversionRateSummaryByDate(reqVO));
     }
     }
 
 
     @GetMapping("/get-business-page-by-date")
     @GetMapping("/get-business-page-by-date")
     @Operation(summary = "获得商机分页(按日期)", description = "用于【销售漏斗】页面的【新增商机分析】")
     @Operation(summary = "获得商机分页(按日期)", description = "用于【销售漏斗】页面的【新增商机分析】")
     @PreAuthorize("@ss.hasPermission('crm:business:query')")
     @PreAuthorize("@ss.hasPermission('crm:business:query')")
     public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessPageByDate(@Valid CrmStatisticsFunnelReqVO pageVO) {
     public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessPageByDate(@Valid CrmStatisticsFunnelReqVO pageVO) {
-        PageResult<CrmBusinessDO> pageResult = crmStatisticsFunnelService.getBusinessPageByDate(pageVO);
+        PageResult<CrmBusinessDO> pageResult = funnelService.getBusinessPageByDate(pageVO);
         return success(new PageResult<>(buildBusinessDetailList(pageResult.getList()), pageResult.getTotal()));
         return success(new PageResult<>(buildBusinessDetailList(pageResult.getList()), pageResult.getTotal()));
     }
     }
 
 

+ 2 - 4
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/funnel/CrmStatisticFunnelRespVO.java → yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/funnel/CrmStatisticFunnelSummaryRespVO.java

@@ -5,12 +5,11 @@ import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 import lombok.NoArgsConstructor;
 
 
-// TODO @puhui999:改成 CrmStatisticFunnelSummaryRespVO 更有统计的味道
 @Schema(description = "管理后台 - CRM 销售漏斗 Response VO")
 @Schema(description = "管理后台 - CRM 销售漏斗 Response VO")
 @NoArgsConstructor
 @NoArgsConstructor
 @AllArgsConstructor
 @AllArgsConstructor
 @Data
 @Data
-public class CrmStatisticFunnelRespVO {
+public class CrmStatisticFunnelSummaryRespVO {
 
 
     @Schema(description = "客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
     @Schema(description = "客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
     private Long customerCount;
     private Long customerCount;
@@ -18,8 +17,7 @@ public class CrmStatisticFunnelRespVO {
     @Schema(description = "商机数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
     @Schema(description = "商机数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
     private Long businessCount;
     private Long businessCount;
 
 
-    // TODO @puhui999:这个改成 businessWinCount 可能会更合适点哈;
     @Schema(description = "赢单数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
     @Schema(description = "赢单数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
-    private Long winCount;
+    private Long businessWinCount;
 
 
 }
 }

+ 21 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/funnel/CrmStatisticsBusinessInversionRateSummaryByDateRespVO.java

@@ -0,0 +1,21 @@
+package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Schema(description = "管理后台 - CRM 商机转化率分析(按日期) VO")
+@Data
+public class CrmStatisticsBusinessInversionRateSummaryByDateRespVO {
+
+    @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401")
+    private String time;
+
+    @Schema(description = "商机数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private Long businessCount;
+
+    @Schema(description = "赢单商机数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private Long businessWinCount;
+
+}

+ 0 - 107
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/funnel/CrmStatisticsBusinessRespVO.java

@@ -1,107 +0,0 @@
-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.Data;
-
-import java.math.BigDecimal;
-import java.time.LocalDateTime;
-
-// TODO @puhui999:是不是可以删除哈?
-@Schema(description = "管理后台 - CRM 商机 Response VO")
-@Data
-@ExcelIgnoreUnannotated
-public class CrmStatisticsBusinessRespVO {
-
-    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "32129")
-    @ExcelProperty("编号")
-    private Long id;
-
-    @Schema(description = "商机名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
-    @ExcelProperty("商机名称")
-    private String name;
-
-    @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10299")
-    private Long customerId;
-    @Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
-    @ExcelProperty("客户名称")
-    private String customerName;
-
-    @Schema(description = "跟进状态", requiredMode = Schema.RequiredMode.REQUIRED, example ="true")
-    @ExcelProperty("跟进状态")
-    private Boolean followUpStatus;
-
-    @Schema(description = "最后跟进时间")
-    @ExcelProperty("最后跟进时间")
-    private LocalDateTime contactLastTime;
-
-    @Schema(description = "下次联系时间")
-    @ExcelProperty("下次联系时间")
-    private LocalDateTime contactNextTime;
-
-    @Schema(description = "负责人的用户编号", example = "25682")
-    @ExcelProperty("负责人的用户编号")
-    private Long ownerUserId;
-    @Schema(description = "负责人名字", example = "25682")
-    @ExcelProperty("负责人名字")
-    private String ownerUserName;
-    @Schema(description = "负责人部门")
-    @ExcelProperty("负责人部门")
-    private String ownerUserDeptName;
-
-    @Schema(description = "商机状态组编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "25714")
-    private Long statusTypeId;
-    @Schema(description = "商机状组名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "进行中")
-    @ExcelProperty("商机状态组")
-    private String statusTypeName;
-
-    @Schema(description = "商机状态编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320")
-    private Long statusId;
-    @Schema(description = "状态名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "跟进中")
-    @ExcelProperty("商机状态")
-    private String statusName;
-
-    @Schema
-    @ExcelProperty("结束状态")
-    private Integer endStatus;
-
-    @ExcelProperty("结束时的备注")
-    private String endRemark;
-
-    @Schema(description = "预计成交日期")
-    @ExcelProperty("预计成交日期")
-    private LocalDateTime dealTime;
-
-    @Schema(description = "产品总金额", example = "12025")
-    @ExcelProperty("产品总金额")
-    private BigDecimal totalProductPrice;
-
-    @Schema(description = "整单折扣")
-    @ExcelProperty("整单折扣")
-    private BigDecimal discountPercent;
-
-    @Schema(description = "商机总金额", example = "12371")
-    @ExcelProperty("商机总金额")
-    private BigDecimal totalPrice;
-
-    @Schema(description = "备注", example = "随便")
-    @ExcelProperty("备注")
-    private String remark;
-
-    @Schema(description = "创建人", example = "1024")
-    @ExcelProperty("创建人")
-    private String creator;
-    @Schema(description = "创建人名字", example = "芋道源码")
-    @ExcelProperty("创建人名字")
-    private String creatorName;
-
-    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
-    @ExcelProperty("创建时间")
-    private LocalDateTime createTime;
-
-    @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
-    @ExcelProperty("更新时间")
-    private LocalDateTime updateTime;
-
-}

+ 2 - 3
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/funnel/CrmStatisticsBusinessSummaryByDateRespVO.java

@@ -13,10 +13,9 @@ public class CrmStatisticsBusinessSummaryByDateRespVO {
     private String time;
     private String time;
 
 
     @Schema(description = "新增商机数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
     @Schema(description = "新增商机数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
-    private Integer businessCreateCount;
+    private Long businessCreateCount;
 
 
-    // TODO @puhui999:是不是金额哈,不是数量
     @Schema(description = "新增商机金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
     @Schema(description = "新增商机金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
-    private BigDecimal businessDealCount;
+    private BigDecimal totalPrice;
 
 
 }
 }

+ 1 - 2
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/funnel/CrmStatisticBusinessEndStatusRespVO.java → yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/funnel/CrmStatisticsBusinessSummaryByEndStatusRespVO.java

@@ -7,12 +7,11 @@ import lombok.NoArgsConstructor;
 
 
 import java.math.BigDecimal;
 import java.math.BigDecimal;
 
 
-// TODO @puhui999:改成 CrmStatisticsBusinessSummaryByEndStatusRespVO,按照结束状态
 @Schema(description = "管理后台 - CRM 商机结束状态统计 Response VO")
 @Schema(description = "管理后台 - CRM 商机结束状态统计 Response VO")
 @NoArgsConstructor
 @NoArgsConstructor
 @AllArgsConstructor
 @AllArgsConstructor
 @Data
 @Data
-public class CrmStatisticBusinessEndStatusRespVO {
+public class CrmStatisticsBusinessSummaryByEndStatusRespVO {
 
 
     @Schema(description = "结束状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
     @Schema(description = "结束状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
     private Integer endStatus;
     private Integer endStatus;

+ 2 - 10
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/funnel/CrmStatisticsFunnelReqVO.java

@@ -5,9 +5,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import cn.iocoder.yudao.framework.common.validation.InEnum;
 import cn.iocoder.yudao.framework.common.validation.InEnum;
 import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
 import lombok.Data;
 import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.ToString;
 import org.springframework.format.annotation.DateTimeFormat;
 import org.springframework.format.annotation.DateTimeFormat;
 
 
 import java.time.LocalDateTime;
 import java.time.LocalDateTime;
@@ -17,9 +16,6 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
 
 
 @Schema(description = "管理后台 - CRM 销售漏斗 Request VO")
 @Schema(description = "管理后台 - CRM 销售漏斗 Request VO")
 @Data
 @Data
-// TODO @puhui999:不用写 EqualsAndHashCode、ToString,已经全局 lombok 啦
-@EqualsAndHashCode(callSuper = true)
-@ToString(callSuper = true)
 public class CrmStatisticsFunnelReqVO extends PageParam {
 public class CrmStatisticsFunnelReqVO extends PageParam {
 
 
     @Schema(description = "部门 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
     @Schema(description = "部门 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@@ -43,13 +39,9 @@ public class CrmStatisticsFunnelReqVO extends PageParam {
     @InEnum(value = DateIntervalEnum.class, message = "时间间隔类型,必须是 {value}")
     @InEnum(value = DateIntervalEnum.class, message = "时间间隔类型,必须是 {value}")
     private Integer interval;
     private Integer interval;
 
 
-    // TODO @puhui999:这个全部前端传递哈;参考 CrmStatisticsCustomerReqVO
-    /**
-     * 前端如果选择自定义时间, 那么前端传递起始-终止时间, 如果选择其他时间间隔类型, 则由后台计算起始-终止时间
-     * 并作为参数传递给Mapper
-     */
     @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
     @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
     @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
     @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    @Size(min = 2, max = 2, message = "请选择时间范围")
     private LocalDateTime[] times;
     private LocalDateTime[] times;
 
 
 }
 }

+ 2 - 4
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticsPortraitReqVO.java

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait;
 
 
 import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
 import lombok.Data;
 import lombok.Data;
 import org.springframework.format.annotation.DateTimeFormat;
 import org.springframework.format.annotation.DateTimeFormat;
 
 
@@ -31,12 +32,9 @@ public class CrmStatisticsPortraitReqVO {
     @Schema(description = "负责人用户 id 集合", hidden = true, example = "2")
     @Schema(description = "负责人用户 id 集合", hidden = true, example = "2")
     private List<Long> userIds;
     private List<Long> userIds;
 
 
-    /**
-     * 前端如果选择自定义时间, 那么前端传递起始-终止时间, 如果选择其他时间间隔类型, 则由后台计算起始-终止时间
-     * 并作为参数传递给Mapper
-     */
     @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
     @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
     @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
     @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    @Size(min = 2, max = 2, message = "请选择时间范围")
     private LocalDateTime[] times;
     private LocalDateTime[] times;
 
 
 }
 }

+ 5 - 28
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java

@@ -1,18 +1,17 @@
 package cn.iocoder.yudao.module.crm.dal.mysql.business;
 package cn.iocoder.yudao.module.crm.dal.mysql.business;
 
 
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
 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.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.CrmBusinessDO;
 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;
 
 
@@ -66,32 +65,10 @@ public interface CrmBusinessMapper extends BaseMapperX<CrmBusinessDO> {
                 .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));
-    }
-
-    // TODO @puhui999:这个可以优化下,通过统计 sql,不通过内存计算;
-    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));
-    }
-
-    // TODO @puhui999:这个可以优化下,通过统计 sql,不通过内存计算;
-    default List<CrmBusinessDO> selectListByOwnerUserIdsAndDate(Collection<Long> ownerUserIds, LocalDateTime[] times) {
-        return selectList(new LambdaQueryWrapperX<CrmBusinessDO>()
-                .in(CrmBusinessDO::getOwnerUserId, ownerUserIds)
-                .betweenIfPresent(CrmBusinessDO::getCreateTime, times));
-    }
-
-    default PageResult<CrmBusinessDO> selectPage(Collection<Long> ownerUserIds, LocalDateTime[] times, Integer pageNo, Integer pageSize) {
-        return selectPage(new PageParam().setPageNo(pageNo).setPageSize(pageSize), new LambdaQueryWrapperX<CrmBusinessDO>()
-                .in(CrmBusinessDO::getOwnerUserId, ownerUserIds)
-                .betweenIfPresent(CrmBusinessDO::getCreateTime, times));
+    default PageResult<CrmBusinessDO> selectPage(CrmStatisticsFunnelReqVO pageVO) {
+        return selectPage(pageVO, new LambdaQueryWrapperX<CrmBusinessDO>()
+                .in(CrmBusinessDO::getOwnerUserId, pageVO.getUserIds())
+                .betweenIfPresent(CrmBusinessDO::getCreateTime, pageVO.getTimes()));
     }
     }
 
 
 }
 }

+ 0 - 7
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java

@@ -186,11 +186,4 @@ public interface CrmCustomerMapper extends BaseMapperX<CrmCustomerDO> {
         return selectCount(query);
         return selectCount(query);
     }
     }
 
 
-    // TODO @puhui999:这个可以优化下,通过统计 sql,不通过内存计算;
-    default Long selectCountByOwnerUserIds(Collection<Long> ownerUserIds, LocalDateTime[] times){
-        return selectCount(new LambdaQueryWrapperX<CrmCustomerDO>()
-                .in(CrmCustomerDO::getOwnerUserId, ownerUserIds)
-                .betweenIfPresent(CrmCustomerDO::getCreateTime, times));
-    }
-
 }
 }

+ 12 - 2
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsFunnelMapper.java

@@ -1,9 +1,11 @@
 package cn.iocoder.yudao.module.crm.dal.mysql.statistics;
 package cn.iocoder.yudao.module.crm.dal.mysql.statistics;
 
 
-import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByDateRespVO;
+import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessInversionRateSummaryByDateRespVO;
 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByDateRespVO;
 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByDateRespVO;
+import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByEndStatusRespVO;
 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsFunnelReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsFunnelReqVO;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
 
 
 import java.util.List;
 import java.util.List;
 
 
@@ -15,6 +17,14 @@ import java.util.List;
 @Mapper
 @Mapper
 public interface CrmStatisticsFunnelMapper {
 public interface CrmStatisticsFunnelMapper {
 
 
-    List<CrmStatisticsBusinessSummaryByDateRespVO> selectBusinessCreateCountGroupByDate(CrmStatisticsFunnelReqVO reqVO);
+    Long selectCustomerCountByDate(CrmStatisticsFunnelReqVO reqVO);
+
+    Long selectBusinessCountByDateAndEndStatus(@Param("reqVO") CrmStatisticsFunnelReqVO reqVO, @Param("status") Integer status);
+
+    List<CrmStatisticsBusinessSummaryByEndStatusRespVO> selectBusinessSummaryListGroupByEndStatus(CrmStatisticsFunnelReqVO reqVO);
+
+    List<CrmStatisticsBusinessSummaryByDateRespVO> selectBusinessSummaryGroupByDate(CrmStatisticsFunnelReqVO reqVO);
+
+    List<CrmStatisticsBusinessInversionRateSummaryByDateRespVO> selectBusinessInversionRateSummaryByDate(CrmStatisticsFunnelReqVO reqVO);
 
 
 }
 }

+ 3 - 35
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessService.java

@@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusi
 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessSaveReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessSaveReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessUpdateStatusReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessUpdateStatusReqVO;
+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.CrmBusinessDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
@@ -194,45 +195,12 @@ 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);
-
-    // TODO @puhui999:这个可以优化下,通过统计 sql,不通过内存计算;
-    /**
-     * 获得商机列表【数据统计】
-     *
-     * @param ownerUserIds 负责人编号
-     * @param times        时间范围
-     * @return 商机列表
-     */
-    List<CrmBusinessDO> getBusinessListByOwnerUserIdsAndEndStatusNotNull(List<Long> ownerUserIds, LocalDateTime[] times);
-
-    // TODO @puhui999:这个可以优化下,通过统计 sql,不通过内存计算;
-    /**
-     * 获得商机列表【数据统计】
-     *
-     * @param ownerUserIds 负责人编号
-     * @param times        时间范围
-     * @return 商机列表
-     */
-    List<CrmBusinessDO> getBusinessListByOwnerUserIdsAndDate(List<Long> ownerUserIds, LocalDateTime[] times);
-
     /**
     /**
      * 获得商机分页,目前用于【数据统计】
      * 获得商机分页,目前用于【数据统计】
      *
      *
-     * @param ownerUserIds 负责人编号
-     * @param times        时间范围
-     * @param pageNo       页码 TODO @puhui999:直接传递 CrmStatisticsFunnelReqVO 吧,虽然有点耦合,但是更清晰一点;
-     * @param pageSize     数量
+     * @param pageVO 请求
      * @return 商机分页
      * @return 商机分页
      */
      */
-    PageResult<CrmBusinessDO> getBusinessPageByDate(List<Long> ownerUserIds, LocalDateTime[] times, Integer pageNo, Integer pageSize);
+    PageResult<CrmBusinessDO> getBusinessPageByDate(CrmStatisticsFunnelReqVO pageVO);
 
 
 }
 }

+ 3 - 29
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java

@@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusi
 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessUpdateStatusReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessUpdateStatusReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactBusinessReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactBusinessReqVO;
+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.CrmBusinessDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
@@ -376,35 +377,8 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
     }
     }
 
 
     @Override
     @Override
-    public Long getBusinessCountByOwnerUserIdsAndEndStatus(List<Long> ownerUserIds, LocalDateTime[] times, Integer endStatus) {
-        if (CollUtil.isEmpty(ownerUserIds)) {
-            return 0L;
-        }
-        return businessMapper.selectCountByOwnerUserIdsAndEndStatus(convertSet(ownerUserIds), times, endStatus);
-    }
-
-    // TODO @puhui999:这个可以优化下,通过统计 sql,不通过内存计算;
-    @Override
-    public List<CrmBusinessDO> getBusinessListByOwnerUserIdsAndEndStatusNotNull(List<Long> ownerUserIds, LocalDateTime[] times) {
-        if (CollUtil.isEmpty(ownerUserIds)) {
-            return Collections.emptyList();
-        }
-        return businessMapper.selectListByOwnerUserIdsAndEndStatusNotNull(convertSet(ownerUserIds), times);
-    }
-
-    // TODO @puhui999:这个可以优化下,通过统计 sql,不通过内存计算;
-    @Override
-    public List<CrmBusinessDO> getBusinessListByOwnerUserIdsAndDate(List<Long> ownerUserIds, LocalDateTime[] times) {
-        if (CollUtil.isEmpty(ownerUserIds)) {
-            return Collections.emptyList();
-        }
-
-        return businessMapper.selectListByOwnerUserIdsAndDate(convertSet(ownerUserIds), times);
-    }
-
-    @Override
-    public PageResult<CrmBusinessDO> getBusinessPageByDate(List<Long> ownerUserIds, LocalDateTime[] times, Integer pageNo, Integer pageSize) {
-        return businessMapper.selectPage(ownerUserIds, times, pageNo, pageSize);
+    public PageResult<CrmBusinessDO> getBusinessPageByDate(CrmStatisticsFunnelReqVO pageVO) {
+        return businessMapper.selectPage(pageVO);
     }
     }
 
 
 }
 }

+ 0 - 9
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java

@@ -195,13 +195,4 @@ public interface CrmCustomerService {
      */
      */
     int autoPutCustomerPool();
     int autoPutCustomerPool();
 
 
-    /**
-     * 获得客户数
-     *
-     * @param ownerUserIds 负责人编号
-     * @param times        时间范围
-     * @return 客户数
-     */
-    Long getCustomerCountByOwnerUserIds(List<Long> ownerUserIds, LocalDateTime[] times);
-
 }
 }

+ 0 - 8
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java

@@ -651,14 +651,6 @@ 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 生效问题
      *
      *

+ 11 - 6
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsFunnelService.java

@@ -1,10 +1,7 @@
 package cn.iocoder.yudao.module.crm.service.statistics;
 package cn.iocoder.yudao.module.crm.service.statistics;
 
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
-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.controller.admin.statistics.vo.funnel.*;
 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
 
 
 import java.util.List;
 import java.util.List;
@@ -22,7 +19,7 @@ public interface CrmStatisticsFunnelService {
      * @param reqVO 请求
      * @param reqVO 请求
      * @return 销售漏斗数据
      * @return 销售漏斗数据
      */
      */
-    CrmStatisticFunnelRespVO getFunnelSummary(CrmStatisticsFunnelReqVO reqVO);
+    CrmStatisticFunnelSummaryRespVO getFunnelSummary(CrmStatisticsFunnelReqVO reqVO);
 
 
     /**
     /**
      * 获得商机结束状态统计
      * 获得商机结束状态统计
@@ -30,7 +27,7 @@ public interface CrmStatisticsFunnelService {
      * @param reqVO 请求
      * @param reqVO 请求
      * @return 商机结束状态统计
      * @return 商机结束状态统计
      */
      */
-    List<CrmStatisticBusinessEndStatusRespVO> getBusinessEndStatusSummary(CrmStatisticsFunnelReqVO reqVO);
+    List<CrmStatisticsBusinessSummaryByEndStatusRespVO> getBusinessSummaryByEndStatus(CrmStatisticsFunnelReqVO reqVO);
 
 
     /**
     /**
      * 获取新增商机分析(按日期)
      * 获取新增商机分析(按日期)
@@ -40,6 +37,14 @@ public interface CrmStatisticsFunnelService {
      */
      */
     List<CrmStatisticsBusinessSummaryByDateRespVO> getBusinessSummaryByDate(CrmStatisticsFunnelReqVO reqVO);
     List<CrmStatisticsBusinessSummaryByDateRespVO> getBusinessSummaryByDate(CrmStatisticsFunnelReqVO reqVO);
 
 
+    /**
+     * 获得商机转化率分析(按日期)
+     *
+     * @param reqVO 请求
+     * @return 商机转化率分析
+     */
+    List<CrmStatisticsBusinessInversionRateSummaryByDateRespVO> getBusinessInversionRateSummaryByDate(CrmStatisticsFunnelReqVO reqVO);
+
     /**
     /**
      * 获得商机分页(按日期)
      * 获得商机分页(按日期)
      *
      *

+ 43 - 47
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsFunnelServiceImpl.java

@@ -4,15 +4,11 @@ import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjUtil;
 import cn.hutool.core.util.ObjUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
-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.controller.admin.statistics.vo.funnel.*;
 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.mysql.statistics.CrmStatisticsFunnelMapper;
 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.enums.business.CrmBusinessEndStatusEnum;
 import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
 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.DeptApi;
 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
 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.AdminUserApi;
@@ -22,14 +18,10 @@ import org.springframework.stereotype.Service;
 
 
 import java.math.BigDecimal;
 import java.math.BigDecimal;
 import java.time.LocalDateTime;
 import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.List;
 import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
 
 
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
-import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
 
 
 /**
 /**
  * CRM 销售漏斗分析 Service 实现类
  * CRM 销售漏斗分析 Service 实现类
@@ -45,15 +37,12 @@ public class CrmStatisticsFunnelServiceImpl implements CrmStatisticsFunnelServic
     @Resource
     @Resource
     private AdminUserApi adminUserApi;
     private AdminUserApi adminUserApi;
     @Resource
     @Resource
-    private CrmCustomerService customerService;
-    @Resource
     private CrmBusinessService businessService;
     private CrmBusinessService businessService;
     @Resource
     @Resource
     private DeptApi deptApi;
     private DeptApi deptApi;
 
 
-    // TODO @puhui999:貌似想了下,可能还是得按照;;;
     @Override
     @Override
-    public CrmStatisticFunnelRespVO getFunnelSummary(CrmStatisticsFunnelReqVO reqVO) {
+    public CrmStatisticFunnelSummaryRespVO getFunnelSummary(CrmStatisticsFunnelReqVO reqVO) {
         // 1. 获得用户编号数组
         // 1. 获得用户编号数组
         List<Long> userIds = getUserIds(reqVO);
         List<Long> userIds = getUserIds(reqVO);
         if (CollUtil.isEmpty(userIds)) {
         if (CollUtil.isEmpty(userIds)) {
@@ -62,34 +51,22 @@ public class CrmStatisticsFunnelServiceImpl implements CrmStatisticsFunnelServic
         reqVO.setUserIds(userIds);
         reqVO.setUserIds(userIds);
 
 
         // 2. 获得漏斗数据
         // 2. 获得漏斗数据
-        return new CrmStatisticFunnelRespVO(
-                customerService.getCustomerCountByOwnerUserIds(userIds, reqVO.getTimes()),
-                businessService.getBusinessCountByOwnerUserIdsAndEndStatus(userIds, reqVO.getTimes(), null),
-                businessService.getBusinessCountByOwnerUserIdsAndEndStatus(userIds, reqVO.getTimes(), CrmBusinessEndStatusEnum.WIN.getStatus())
-        );
+        Long customerCount = funnelMapper.selectCustomerCountByDate(reqVO);
+        Long businessCount = funnelMapper.selectBusinessCountByDateAndEndStatus(reqVO, null);
+        Long businessWinCount = funnelMapper.selectBusinessCountByDateAndEndStatus(reqVO, CrmBusinessEndStatusEnum.WIN.getStatus());
+        return new CrmStatisticFunnelSummaryRespVO(customerCount, businessCount, businessWinCount);
     }
     }
 
 
     @Override
     @Override
-    public List<CrmStatisticBusinessEndStatusRespVO> getBusinessEndStatusSummary(CrmStatisticsFunnelReqVO reqVO) {
+    public List<CrmStatisticsBusinessSummaryByEndStatusRespVO> getBusinessSummaryByEndStatus(CrmStatisticsFunnelReqVO reqVO) {
         // 1. 获得用户编号数组
         // 1. 获得用户编号数组
         reqVO.setUserIds(getUserIds(reqVO));
         reqVO.setUserIds(getUserIds(reqVO));
         if (CollUtil.isEmpty(reqVO.getUserIds())) {
         if (CollUtil.isEmpty(reqVO.getUserIds())) {
             return Collections.emptyList();
             return Collections.emptyList();
         }
         }
 
 
-        // TODO @puhui999:这个可以优化下,通过统计 sql,不通过内存计算;
-        // 2.1 获得用户负责的商机
-        List<CrmBusinessDO> businessList = businessService.getBusinessListByOwnerUserIdsAndEndStatusNotNull(reqVO.getUserIds(), 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));
-        });
+        // 2. 获得统计数据
+        return funnelMapper.selectBusinessSummaryListGroupByEndStatus(reqVO);
     }
     }
 
 
     @Override
     @Override
@@ -101,26 +78,45 @@ public class CrmStatisticsFunnelServiceImpl implements CrmStatisticsFunnelServic
         }
         }
 
 
         // 2. 按天统计,获取分项统计数据
         // 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 ->
-                        business.getCreateTime().format(DateTimeFormatter.ofPattern(FORMAT_YEAR_MONTH_DAY)),
-                Collectors.reducing(BigDecimal.ZERO, CrmBusinessDO::getTotalPrice, BigDecimal::add)));
-
+        List<CrmStatisticsBusinessSummaryByDateRespVO> businessSummaryList = funnelMapper.selectBusinessSummaryGroupByDate(reqVO);
         // 3. 按照日期间隔,合并数据
         // 3. 按照日期间隔,合并数据
         List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval());
         List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval());
         return convertList(timeRanges, times -> {
         return convertList(timeRanges, times -> {
-            Integer businessCreateCount = businessCreateCountList.stream()
+            Long businessCreateCount = businessSummaryList.stream()
                     .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))
                     .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))
-                    .mapToInt(CrmStatisticsBusinessSummaryByDateRespVO::getBusinessCreateCount).sum();
-            BigDecimal businessDealCount = businessDealCountMap.entrySet().stream()
-                    .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getKey()))
-                    .map(Map.Entry::getValue)
+                    .mapToLong(CrmStatisticsBusinessSummaryByDateRespVO::getBusinessCreateCount).sum();
+            BigDecimal businessDealCount = businessSummaryList.stream()
+                    .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))
+                    .map(CrmStatisticsBusinessSummaryByDateRespVO::getTotalPrice)
                     .reduce(BigDecimal.ZERO, BigDecimal::add);
                     .reduce(BigDecimal.ZERO, BigDecimal::add);
             return new CrmStatisticsBusinessSummaryByDateRespVO()
             return new CrmStatisticsBusinessSummaryByDateRespVO()
                     .setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval()))
                     .setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval()))
-                    .setBusinessCreateCount(businessCreateCount).setBusinessDealCount(businessDealCount);
+                    .setBusinessCreateCount(businessCreateCount).setTotalPrice(businessDealCount);
+        });
+    }
+
+    @Override
+    public List<CrmStatisticsBusinessInversionRateSummaryByDateRespVO> getBusinessInversionRateSummaryByDate(CrmStatisticsFunnelReqVO reqVO) {
+        // 1. 获得用户编号数组
+        reqVO.setUserIds(getUserIds(reqVO));
+        if (CollUtil.isEmpty(reqVO.getUserIds())) {
+            return Collections.emptyList();
+        }
+
+        // 2. 按天统计,获取分项统计数据
+        List<CrmStatisticsBusinessInversionRateSummaryByDateRespVO> businessSummaryList = funnelMapper.selectBusinessInversionRateSummaryByDate(reqVO);
+        // 3. 按照日期间隔,合并数据
+        List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval());
+        return convertList(timeRanges, times -> {
+            Long businessCount = businessSummaryList.stream()
+                    .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))
+                    .mapToLong(CrmStatisticsBusinessInversionRateSummaryByDateRespVO::getBusinessCount).sum();
+            Long businessWinCount = businessSummaryList.stream()
+                    .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))
+                    .mapToLong(CrmStatisticsBusinessInversionRateSummaryByDateRespVO::getBusinessWinCount).sum();
+            return new CrmStatisticsBusinessInversionRateSummaryByDateRespVO()
+                    .setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval()))
+                    .setBusinessCount(businessCount).setBusinessWinCount(businessWinCount);
         });
         });
     }
     }
 
 
@@ -132,7 +128,7 @@ public class CrmStatisticsFunnelServiceImpl implements CrmStatisticsFunnelServic
             return PageResult.empty();
             return PageResult.empty();
         }
         }
         // 2. 执行查询
         // 2. 执行查询
-        return businessService.getBusinessPageByDate(pageVO.getUserIds(), pageVO.getTimes(), pageVO.getPageNo(), pageVO.getPageSize());
+        return businessService.getBusinessPageByDate(pageVO);
     }
     }
 
 
     /**
     /**

+ 67 - 7
yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsFunnelMapper.xml

@@ -2,18 +2,78 @@
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <!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 namespace="cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsFunnelMapper">
 
 
-    <select id="selectBusinessCreateCountGroupByDate"
+    <select id="selectCustomerCountByDate" resultType="java.lang.Long">
+        SELECT COUNT(*)
+        FROM crm_customer
+        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}
+    </select>
+
+    <select id="selectBusinessCountByDateAndEndStatus" resultType="java.lang.Long">
+        SELECT COUNT(*)
+        FROM crm_business
+        WHERE deleted = 0
+        <if test="status != null">
+            AND end_status = #{status}
+        </if>
+        AND owner_user_id IN
+        <foreach collection="reqVO.userIds" item="userId" open="(" close=")" separator=",">
+            #{userId}
+        </foreach>
+        AND create_time BETWEEN #{reqVO.times[0],javaType=java.time.LocalDateTime} AND
+        #{reqVO.times[1],javaType=java.time.LocalDateTime}
+    </select>
+
+    <select id="selectBusinessSummaryListGroupByEndStatus"
+            resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByEndStatusRespVO">
+        SELECT end_status AS endStatus,COUNT(*) AS businessCount, SUM(total_price) AS totalPrice
+        FROM crm_business
+        WHERE deleted = 0 AND end_status IS NOT NULL
+        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}
+        GROUP BY end_status
+    </select>
+
+    <select id="selectBusinessSummaryGroupByDate"
             resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByDateRespVO">
             resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByDateRespVO">
         SELECT
         SELECT
-            DATE_FORMAT(create_time, '%Y-%m-%d') AS time,
-            COUNT(*) AS businessCreateCount
+        DATE_FORMAT(create_time, '%Y-%m-%d') AS time,
+        COUNT(*) AS businessCreateCount,
+        SUM(total_price) AS totalPrice
+        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}
+        GROUP BY time
+    </select>
+
+    <select id="selectBusinessInversionRateSummaryByDate"
+            resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessInversionRateSummaryByDateRespVO">
+        SELECT
+        DATE_FORMAT(create_time, '%Y-%m-%d') AS time,
+        COUNT(*) AS businessCount,
+        SUM(IF(end_status = 1, 1, 0)) AS businessWinCount
         FROM crm_business
         FROM crm_business
         WHERE deleted = 0
         WHERE deleted = 0
         AND owner_user_id IN
         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
         GROUP BY time
     </select>
     </select>