Commit a23ea45632640ec50c5472aa89796b8fcb160531

Authored by dhb52
1 parent 379bd958

fix: [CRM-客户统计]根据Code-Review 修改

Showing 16 changed files with 824 additions and 282 deletions
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http
1 -### 新建客户总量分析(按日)  
2 -GET {{baseUrl}}/crm/statistics-customer/get-total-customer-count?deptId=100&times[0]=2024-12-01 00:00:00&times[1]=2024-12-12 23:59:59 1 +# == 1. 客户总量分析 ==
  2 +### 1.1 客户总量分析(按日)
  3 +GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-date?deptId=100&times[0]=2024-01-01 00:00:00&times[1]=2024-01-29 23:59:59
3 Authorization: Bearer {{token}} 4 Authorization: Bearer {{token}}
4 tenant-id: {{adminTenentId}} 5 tenant-id: {{adminTenentId}}
5 6
6 -### 新建客户总量分析(按月)  
7 -GET {{baseUrl}}/crm/statistics-customer/get-total-customer-count?deptId=100&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59 7 +### 1.2 客户总量分析(按月)
  8 +GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-date?deptId=100&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59
8 Authorization: Bearer {{token}} 9 Authorization: Bearer {{token}}
9 tenant-id: {{adminTenentId}} 10 tenant-id: {{adminTenentId}}
10 11
11 -### 成交客户总量分析(按日)  
12 -GET {{baseUrl}}/crm/statistics-customer/get-deal-total-customer-count?deptId=100&times[0]=2024-12-01 00:00:00&times[1]=2024-12-12 23:59:59 12 +### 1.3 客户总量统计(按用户)
  13 +GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-user?deptId=100&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59
13 Authorization: Bearer {{token}} 14 Authorization: Bearer {{token}}
14 tenant-id: {{adminTenentId}} 15 tenant-id: {{adminTenentId}}
15 16
16 -### 成交客户总量分析(按月)  
17 -GET {{baseUrl}}/crm/statistics-customer/get-deal-total-customer-count?deptId=100&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59 17 +
  18 +# == 2. 客户跟进次数分析 ==
  19 +### 2.1 客户跟进次数分析(按日)
  20 +GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-date?deptId=100&times[0]=2024-01-01 00:00:00&times[1]=2024-01-29 23:59:59
  21 +Authorization: Bearer {{token}}
  22 +tenant-id: {{adminTenentId}}
  23 +
  24 +### 2.2 客户跟进次数分析(按月)
  25 +GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-date?deptId=100&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59
  26 +Authorization: Bearer {{token}}
  27 +tenant-id: {{adminTenentId}}
  28 +
  29 +### 2.3 客户总量统计(按用户)
  30 +GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-user?deptId=100&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59
  31 +Authorization: Bearer {{token}}
  32 +tenant-id: {{adminTenentId}}
  33 +
  34 +
  35 +# == 3. 客户跟进方式分析 ==
  36 +### 3.1 客户跟进方式分析
  37 +GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-type?deptId=100&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59
18 Authorization: Bearer {{token}} 38 Authorization: Bearer {{token}}
19 tenant-id: {{adminTenentId}} 39 tenant-id: {{adminTenentId}}
  40 +tenant-id: {{adminTenentId}}
20 41
21 -### 获取客户跟进次数(按日)  
22 -GET {{baseUrl}}/crm/statistics-customer/get-record-count?deptId=100&times[0]=2024-12-01 00:00:00&times[1]=2024-12-12 23:59:59 42 +
  43 +# == 4. 客户成交周期 ==
  44 +### 4.1 合同摘要信息(客户转化率页面)
  45 +GET {{baseUrl}}/crm/statistics-customer/get-contract-summary?deptId=100&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59
23 Authorization: Bearer {{token}} 46 Authorization: Bearer {{token}}
24 tenant-id: {{adminTenentId}} 47 tenant-id: {{adminTenentId}}
  48 +tenant-id: {{adminTenentId}}
25 49
26 -### 获取客户跟进次数(按月)  
27 -GET {{baseUrl}}/crm/statistics-customer/get-record-count?deptId=100&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59 50 +
  51 +# == 5. 客户成交周期 ==
  52 +### 5.1 客户成交周期(按日)
  53 +GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-date?deptId=100&times[0]=2024-01-01 00:00:00&times[1]=2024-01-29 23:59:59
28 Authorization: Bearer {{token}} 54 Authorization: Bearer {{token}}
29 tenant-id: {{adminTenentId}} 55 tenant-id: {{adminTenentId}}
  56 +tenant-id: {{adminTenentId}}
30 57
31 -### 获取已跟进客户数(按日)  
32 -GET {{baseUrl}}/crm/statistics-customer/get-distinct-record-count?deptId=100&times[0]=2024-12-01 00:00:00&times[1]=2024-12-12 23:59:59 58 +### 5.2 客户成交周期(按月)
  59 +GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-date?deptId=100&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59
33 Authorization: Bearer {{token}} 60 Authorization: Bearer {{token}}
34 tenant-id: {{adminTenentId}} 61 tenant-id: {{adminTenentId}}
  62 +tenant-id: {{adminTenentId}}
35 63
36 -### 获取已跟进客户数(按月)  
37 -GET {{baseUrl}}/crm/statistics-customer/get-distinct-record-count?deptId=100&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59 64 +### 5.3 获取客户成交周期(按用户)
  65 +GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-user?deptId=100&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59
38 Authorization: Bearer {{token}} 66 Authorization: Bearer {{token}}
  67 +tenant-id: {{adminTenentId}}
39 tenant-id: {{adminTenentId}} 68 tenant-id: {{adminTenentId}}
40 \ No newline at end of file 69 \ No newline at end of file
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java
1 package cn.iocoder.yudao.module.crm.controller.admin.statistics; 1 package cn.iocoder.yudao.module.crm.controller.admin.statistics;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO;  
5 -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO; 4 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*;
6 import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsCustomerService; 5 import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsCustomerService;
7 import io.swagger.v3.oas.annotations.Operation; 6 import io.swagger.v3.oas.annotations.Operation;
8 import io.swagger.v3.oas.annotations.tags.Tag; 7 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -18,8 +17,7 @@ import java.util.List; @@ -18,8 +17,7 @@ import java.util.List;
18 17
19 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 18 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
20 19
21 -// TODO @dhb52:数据统计 员工客户分析,改成“客户统计”  
22 -@Tag(name = "管理后台 - CRM 数据统计 员工客户分析") 20 +@Tag(name = "管理后台 - CRM 客户统计")
23 @RestController 21 @RestController
24 @RequestMapping("/crm/statistics-customer") 22 @RequestMapping("/crm/statistics-customer")
25 @Validated 23 @Validated
@@ -28,50 +26,60 @@ public class CrmStatisticsCustomerController { @@ -28,50 +26,60 @@ public class CrmStatisticsCustomerController {
28 @Resource 26 @Resource
29 private CrmStatisticsCustomerService customerService; 27 private CrmStatisticsCustomerService customerService;
30 28
31 - // TODO @dhb52:建议 getCustomerCount 和 getDealTotalCustomerCount 搞成一个接口;  
32 - // 1. 数量接口:【方法:getCustomerSummaryByDate】,VO:CrmStatisticsCustomerSummaryByDateRespVO,然后里面是 time、customerCreateCount customerDealCount  
33 - // 2. 按人统计:【方法:getCustomerSummaryByUser】,VO:CrmStatisticsCustomerSummaryByOwnerRespVO,然后里面是 ownerUserId、ownerUserName、customerCreateCount customerDealCount、contractPrice、receivablePrice;客户成交率、未回款金额、回款完成率,交给前端计算; 29 + @GetMapping("/get-customer-summary-by-date")
  30 + @Operation(summary = "获取客户总量分析(按日期)")
  31 + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")
  32 + public CommonResult<List<CrmStatisticsCustomerSummaryByDateRespVO>> getCustomerSummaryByDate(@Valid CrmStatisticsCustomerReqVO reqVO) {
  33 + return success(customerService.getCustomerSummaryByDate(reqVO));
  34 + }
  35 +
  36 + @GetMapping("/get-customer-summary-by-user")
  37 + @Operation(summary = "获取客户总量分析(按用户)")
  38 + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")
  39 + public CommonResult<List<CrmStatisticsCustomerSummaryByUserRespVO>> getCustomerSummaryByUser(@Valid CrmStatisticsCustomerReqVO reqVO) {
  40 + return success(customerService.getCustomerSummaryByUser(reqVO));
  41 + }
34 42
35 - @GetMapping("/get-total-customer-count")  
36 - @Operation(summary = "获得新建客户数量") 43 + @GetMapping("/get-followup-summary-by-date")
  44 + @Operation(summary = "获取客户跟进次数分析(按日期)")
37 @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") 45 @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")
38 - public CommonResult<List<CrmStatisticsCustomerCountVO>> getTotalCustomerCount(@Valid CrmStatisticsCustomerReqVO reqVO) {  
39 - return success(customerService.getTotalCustomerCount(reqVO)); 46 + public CommonResult<List<CrmStatisticsFollowupSummaryByDateRespVO>> getFollowupSummaryByDate(@Valid CrmStatisticsCustomerReqVO reqVO) {
  47 + return success(customerService.getFollowupSummaryByDate(reqVO));
40 } 48 }
41 49
42 - @GetMapping("/get-deal-total-customer-count")  
43 - @Operation(summary = "获得成交客户数量") 50 + @GetMapping("/get-followup-summary-by-user")
  51 + @Operation(summary = "获取客户跟进次数分析(按用户)")
44 @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") 52 @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")
45 - public CommonResult<List<CrmStatisticsCustomerCountVO>> getDealTotalCustomerCount(@Valid CrmStatisticsCustomerReqVO reqVO) {  
46 - return success(customerService.getDealTotalCustomerCount(reqVO)); 53 + public CommonResult<List<CrmStatisticsFollowupSummaryByUserRespVO>> getFollowupSummaryByUser(@Valid CrmStatisticsCustomerReqVO reqVO) {
  54 + return success(customerService.getFollowupSummaryByUser(reqVO));
47 } 55 }
48 56
49 - @GetMapping("/get-record-count")  
50 - @Operation(summary = "获取客户跟进次数") 57 + @GetMapping("/get-followup-summary-by-type")
  58 + @Operation(summary = "获取客户跟进次数分析(按类型)")
51 @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") 59 @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")
52 - public CommonResult<List<CrmStatisticsCustomerCountVO>> getRecordCount(@Valid CrmStatisticsCustomerReqVO reqVO) {  
53 - return success(customerService.getRecordCount(reqVO)); 60 + public CommonResult<List<CrmStatisticsFollowupSummaryByTypeRespVO>> getFollowupSummaryByType(@Valid CrmStatisticsCustomerReqVO reqVO) {
  61 + return success(customerService.getFollowupSummaryByType(reqVO));
54 } 62 }
55 63
56 - @GetMapping("/get-distinct-record-count")  
57 - @Operation(summary = "获取已跟进客户数") 64 + @GetMapping("/get-contract-summary")
  65 + @Operation(summary = "获取合同摘要信息(客户转化率页面)")
58 @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") 66 @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")
59 - public CommonResult<List<CrmStatisticsCustomerCountVO>> getDistinctRecordCount(@Valid CrmStatisticsCustomerReqVO reqVO) {  
60 - return success(customerService.getDistinctRecordCount(reqVO)); 67 + public CommonResult<List<CrmStatisticsCustomerContractSummaryRespVO>> getContractSummary(@Valid CrmStatisticsCustomerReqVO reqVO) {
  68 + return success(customerService.getContractSummary(reqVO));
61 } 69 }
62 70
63 - @GetMapping("/get-record-type-count")  
64 - @Operation(summary = "获取客户跟进方式统计数") 71 + @GetMapping("/get-customer-deal-cycle-by-date")
  72 + @Operation(summary = "获取客户成交周期(按日期)")
65 @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") 73 @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")
66 - public CommonResult<List<CrmStatisticsCustomerCountVO>> getRecordTypeCount(@Valid CrmStatisticsCustomerReqVO reqVO) {  
67 - return success(customerService.getRecordTypeCount(reqVO)); 74 + public CommonResult<List<CrmStatisticsCustomerDealCycleByDateRespVO>> getCustomerDealCycleByDate(@Valid CrmStatisticsCustomerReqVO reqVO) {
  75 + return success(customerService.getCustomerDealCycleByDate(reqVO));
68 } 76 }
69 77
70 - @GetMapping("/get-customer-cycle")  
71 - @Operation(summary = "获取客户成交周期") 78 + @GetMapping("/get-customer-deal-cycle-by-user")
  79 + @Operation(summary = "获取客户成交周期(按用户)")
72 @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") 80 @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")
73 - public CommonResult<List<CrmStatisticsCustomerCountVO>> getCustomerCycle(@Valid CrmStatisticsCustomerReqVO reqVO) {  
74 - return success(customerService.getCustomerCycle(reqVO)); 81 + public CommonResult<List<CrmStatisticsCustomerDealCycleByUserRespVO>> getCustomerDealCycleByUser(@Valid CrmStatisticsCustomerReqVO reqVO) {
  82 + return success(customerService.getCustomerDealCycleByUser(reqVO));
75 } 83 }
76 84
77 } 85 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerByUserBaseRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +/**
  7 + * 用户客户统计响应 Base VO
  8 + */
  9 +@Data
  10 +public class CrmStatisticsCustomerByUserBaseRespVO {
  11 +
  12 + @Schema(description = "负责人ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  13 + private Long ownerUserId;
  14 +
  15 + @Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
  16 + private String ownerUserName;
  17 +
  18 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerContractSummaryRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;
  2 +
  3 +
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +
  7 +import java.math.BigDecimal;
  8 +import java.time.LocalDate;
  9 +import java.time.LocalDateTime;
  10 +
  11 +@Schema(description = "管理后台 - CRM 客户转化率分析 VO")
  12 +@Data
  13 +public class CrmStatisticsCustomerContractSummaryRespVO {
  14 +
  15 + @Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
  16 + private String customerName;
  17 +
  18 + @Schema(description = "合同名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "演示合同")
  19 + private String contractName;
  20 +
  21 + @Schema(description = "合同总金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1200.00")
  22 + private BigDecimal totalPrice;
  23 +
  24 + @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1200.00")
  25 + private BigDecimal receivablePrice;
  26 +
  27 + @Schema(description = "客户行业", requiredMode = Schema.RequiredMode.REQUIRED, example = "金融")
  28 + private String customerType;
  29 +
  30 + @Schema(description = "客户来源", requiredMode = Schema.RequiredMode.REQUIRED, example = "外呼")
  31 + private String customerSource;
  32 +
  33 + @Schema(description = "负责人ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  34 + private Long ownerUserId;
  35 +
  36 + @Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
  37 + private String ownerUserName;
  38 +
  39 + @Schema(description = "创建人ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  40 + private Long creatorUserId;
  41 +
  42 + @Schema(description = "创建人", requiredMode = Schema.RequiredMode.REQUIRED, example = "源码")
  43 + private String creatorUserName;
  44 +
  45 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-01 13:24:26")
  46 + private LocalDateTime createTime;
  47 +
  48 + @Schema(description = "下单日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02 00:00:00")
  49 + private LocalDate orderDate;
  50 +
  51 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerCountVO.java deleted
1 -package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;  
2 -  
3 -import io.swagger.v3.oas.annotations.media.Schema;  
4 -import lombok.Data;  
5 -  
6 -  
7 -@Schema(description = "管理后台 - CRM 数据统计 员工客户分析 VO")  
8 -@Data  
9 -public class CrmStatisticsCustomerCountVO {  
10 -  
11 - /**  
12 - * 时间轴  
13 - * <p>  
14 - * group by DATE_FORMAT(create_date, '%Y%m')  
15 - */  
16 - @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401")  
17 - private String category;  
18 -  
19 - /**  
20 - * 数量是个特别“抽象”的概念,在不同排行下,代表不同含义  
21 - * <p>  
22 - * 1. 金额:合同金额排行、回款金额排行  
23 - * 2. 个数:签约合同排行、产品销量排行、产品销量排行、新增客户数排行、新增联系人排行、跟进次数排行、跟进客户数排行  
24 - */  
25 - @Schema(description = "数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")  
26 - private Integer count = 0;  
27 -  
28 - /**  
29 - * 成交周期(天)  
30 - */  
31 - @Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0")  
32 - private Double cycle = 0.0;  
33 -  
34 -}  
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByDateRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +@Schema(description = "管理后台 - CRM 客户成交周期分析(按日期) VO")
  7 +@Data
  8 +public class CrmStatisticsCustomerDealCycleByDateRespVO {
  9 +
  10 + @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401")
  11 + private String time;
  12 +
  13 + @Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0")
  14 + private Double customerDealCycle = 0.0;
  15 +
  16 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByUserRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +@Schema(description = "管理后台 - CRM 成交周期分析(按用户) VO")
  7 +@Data
  8 +public class CrmStatisticsCustomerDealCycleByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO {
  9 +
  10 + @Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0")
  11 + private Double customerDealCycle = 0.0;
  12 +
  13 + @Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  14 + private Integer customerDealCount = 0;
  15 +
  16 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByDateRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +@Schema(description = "管理后台 - CRM 客户总量分析(按日期) VO")
  7 +@Data
  8 +public class CrmStatisticsCustomerSummaryByDateRespVO {
  9 +
  10 + @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401")
  11 + private String time;
  12 +
  13 + @Schema(description = "新建客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  14 + private Integer customerCreateCount = 0;
  15 +
  16 + @Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  17 + private Integer customerDealCount = 0;
  18 +
  19 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByUserRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.math.BigDecimal;
  7 +
  8 +@Schema(description = "管理后台 - CRM 客户总量分析(按用户) VO")
  9 +@Data
  10 +public class CrmStatisticsCustomerSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO {
  11 +
  12 + @Schema(description = "新建客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  13 + private Integer customerCreateCount = 0;
  14 +
  15 + @Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  16 + private Integer customerDealCount = 0;
  17 +
  18 + @Schema(description = "合同总金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
  19 + private BigDecimal contractPrice = BigDecimal.valueOf(0);
  20 +
  21 + @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
  22 + private BigDecimal receivablePrice = BigDecimal.valueOf(0);
  23 +
  24 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByDateRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +@Schema(description = "管理后台 - CRM 跟进次数分析(按日期) VO")
  7 +@Data
  8 +public class CrmStatisticsFollowupSummaryByDateRespVO {
  9 +
  10 + @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401")
  11 + private String time;
  12 +
  13 + @Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  14 + private Integer followupRecordCount = 0;
  15 +
  16 + @Schema(description = "跟进客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  17 + private Integer followupCustomerCount = 0;
  18 +
  19 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByTypeRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;
  2 +
  3 +
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +
  7 +@Schema(description = "管理后台 - CRM 跟进次数分析(按类型) VO")
  8 +@Data
  9 +public class CrmStatisticsFollowupSummaryByTypeRespVO {
  10 +
  11 + @Schema(description = "跟进类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  12 + private String followupType;
  13 +
  14 + @Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  15 + private Integer followupRecordCount = 0;
  16 +
  17 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByUserRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +@Schema(description = "管理后台 - CRM 跟进次数分析(按用户) VO")
  7 +@Data
  8 +public class CrmStatisticsFollowupSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO {
  9 +
  10 + @Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  11 + private Integer followupRecordCount = 0;
  12 +
  13 + @Schema(description = "跟进客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  14 + private Integer followupCustomerCount = 0;
  15 +
  16 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java
1 package cn.iocoder.yudao.module.crm.dal.mysql.statistics; 1 package cn.iocoder.yudao.module.crm.dal.mysql.statistics;
2 2
3 -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO;  
4 -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO; 3 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*;
5 import org.apache.ibatis.annotations.Mapper; 4 import org.apache.ibatis.annotations.Mapper;
6 5
7 import java.util.List; 6 import java.util.List;
@@ -14,16 +13,32 @@ import java.util.List; @@ -14,16 +13,32 @@ import java.util.List;
14 @Mapper 13 @Mapper
15 public interface CrmStatisticsCustomerMapper { 14 public interface CrmStatisticsCustomerMapper {
16 15
17 - List<CrmStatisticsCustomerCountVO> selectCustomerCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); 16 + List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerCreateCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO);
18 17
19 - List<CrmStatisticsCustomerCountVO> selectDealCustomerCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); 18 + List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerDealCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO);
20 19
21 - List<CrmStatisticsCustomerCountVO> selectRecordCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); 20 + List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerCreateCountGroupbyUser(CrmStatisticsCustomerReqVO reqVO);
22 21
23 - List<CrmStatisticsCustomerCountVO> selectDistinctRecordCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); 22 + List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerDealCountGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO);
24 23
25 - List<CrmStatisticsCustomerCountVO> selectRecordCountGroupbyType(CrmStatisticsCustomerReqVO reqVO); 24 + List<CrmStatisticsCustomerSummaryByUserRespVO> selectContractPriceGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO);
26 25
27 - List<CrmStatisticsCustomerCountVO> selectCustomerCycleGroupbyDate(CrmStatisticsCustomerReqVO reqVO); 26 + List<CrmStatisticsCustomerSummaryByUserRespVO> selectReceivablePriceGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO);
  27 +
  28 + List<CrmStatisticsFollowupSummaryByDateRespVO> selectFollowupRecordCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO);
  29 +
  30 + List<CrmStatisticsFollowupSummaryByDateRespVO> selectFollowupCustomerCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO);
  31 +
  32 + List<CrmStatisticsFollowupSummaryByUserRespVO> selectFollowupRecordCountGroupbyUser(CrmStatisticsCustomerReqVO reqVO);
  33 +
  34 + List<CrmStatisticsFollowupSummaryByUserRespVO> selectFollowupCustomerCountGroupbyUser(CrmStatisticsCustomerReqVO reqVO);
  35 +
  36 + List<CrmStatisticsCustomerContractSummaryRespVO> selectContractSummary(CrmStatisticsCustomerReqVO reqVO);
  37 +
  38 + List<CrmStatisticsFollowupSummaryByTypeRespVO> selectFollowupRecordCountGroupbyType(CrmStatisticsCustomerReqVO reqVO);
  39 +
  40 + List<CrmStatisticsCustomerDealCycleByDateRespVO> selectCustomerDealCycleGroupbyDate(CrmStatisticsCustomerReqVO reqVO);
  41 +
  42 + List<CrmStatisticsCustomerDealCycleByUserRespVO> selectCustomerDealCycleGroupbyUser(CrmStatisticsCustomerReqVO reqVO);
28 43
29 } 44 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java
1 package cn.iocoder.yudao.module.crm.service.statistics; 1 package cn.iocoder.yudao.module.crm.service.statistics;
2 2
3 -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO;  
4 -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO; 3 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*;
5 4
6 import java.util.List; 5 import java.util.List;
7 6
8 /** 7 /**
9 - * CRM 数据统计 员工客户分析 Service 接口 8 + * CRM 客户分析 Service 接口
10 * 9 *
11 * @author dhb52 10 * @author dhb52
12 */ 11 */
13 public interface CrmStatisticsCustomerService { 12 public interface CrmStatisticsCustomerService {
14 13
15 /** 14 /**
16 - * 获取新建客户数量 15 + * 总量分析(按日期)
17 * 16 *
18 * @param reqVO 请求参数 17 * @param reqVO 请求参数
19 - * @return 新建客户数量统计 18 + * @return 统计数据
20 */ 19 */
21 - List<CrmStatisticsCustomerCountVO> getTotalCustomerCount(CrmStatisticsCustomerReqVO reqVO); 20 + List<CrmStatisticsCustomerSummaryByDateRespVO> getCustomerSummaryByDate(CrmStatisticsCustomerReqVO reqVO);
22 21
23 /** 22 /**
24 - * 获取成交客户数量 23 + * 总量分析(按用户)
25 * 24 *
26 * @param reqVO 请求参数 25 * @param reqVO 请求参数
27 - * @return 成交客户数量统计 26 + * @return 统计数据
28 */ 27 */
29 - List<CrmStatisticsCustomerCountVO> getDealTotalCustomerCount(CrmStatisticsCustomerReqVO reqVO); 28 + List<CrmStatisticsCustomerSummaryByUserRespVO> getCustomerSummaryByUser(CrmStatisticsCustomerReqVO reqVO);
30 29
31 30
32 /** 31 /**
33 - * 获取客户跟进次数 32 + * 跟进次数分析(按日期)
34 * 33 *
35 * @param reqVO 请求参数 34 * @param reqVO 请求参数
36 - * @return 客户跟进次数 35 + * @return 统计数据
37 */ 36 */
38 - List<CrmStatisticsCustomerCountVO> getRecordCount(CrmStatisticsCustomerReqVO reqVO); 37 + List<CrmStatisticsFollowupSummaryByDateRespVO> getFollowupSummaryByDate(CrmStatisticsCustomerReqVO reqVO);
39 38
40 /** 39 /**
41 - * 获取已跟进客户数 40 + * 跟进次数分析(按用户)
42 * 41 *
43 * @param reqVO 请求参数 42 * @param reqVO 请求参数
44 - * @return 已跟进客户数 43 + * @return 统计数据
45 */ 44 */
46 - List<CrmStatisticsCustomerCountVO> getDistinctRecordCount(CrmStatisticsCustomerReqVO reqVO); 45 + List<CrmStatisticsFollowupSummaryByUserRespVO> getFollowupSummaryByUser(CrmStatisticsCustomerReqVO reqVO);
47 46
48 /** 47 /**
49 - * 获取客户跟进方式统计数 48 + * 客户跟进次数分析(按类型)
50 * 49 *
51 * @param reqVO 请求参数 50 * @param reqVO 请求参数
52 - * @return 客户跟进方式统计数 51 + * @return 统计数据
53 */ 52 */
54 - List<CrmStatisticsCustomerCountVO> getRecordTypeCount(CrmStatisticsCustomerReqVO reqVO); 53 + List<CrmStatisticsFollowupSummaryByTypeRespVO> getFollowupSummaryByType(CrmStatisticsCustomerReqVO reqVO);
  54 +
  55 +
  56 + /**
  57 + * 获取合同摘要信息(客户转化率页面)
  58 + *
  59 + * @param reqVO 请求参数
  60 + * @return 合同摘要列表
  61 + */
  62 + List<CrmStatisticsCustomerContractSummaryRespVO> getContractSummary(CrmStatisticsCustomerReqVO reqVO);
  63 +
  64 + /**
  65 + * 客户成交周期(按日期)
  66 + *
  67 + * @param reqVO 请求参数
  68 + * @return 统计数据
  69 + */
  70 + List<CrmStatisticsCustomerDealCycleByDateRespVO> getCustomerDealCycleByDate(CrmStatisticsCustomerReqVO reqVO);
55 71
56 /** 72 /**
57 - * 获取客户成交周期 73 + * 客户成交周期(按用户)
58 * 74 *
59 * @param reqVO 请求参数 75 * @param reqVO 请求参数
60 - * @return 客户成交周期 76 + * @return 统计数据
61 */ 77 */
62 - List<CrmStatisticsCustomerCountVO> getCustomerCycle(CrmStatisticsCustomerReqVO reqVO); 78 + List<CrmStatisticsCustomerDealCycleByUserRespVO> getCustomerDealCycleByUser(CrmStatisticsCustomerReqVO reqVO);
63 79
64 } 80 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java
@@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.crm.service.statistics; @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.crm.service.statistics;
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.date.LocalDateTimeUtil; 4 import cn.hutool.core.date.LocalDateTimeUtil;
5 import cn.hutool.core.util.ObjUtil; 5 import cn.hutool.core.util.ObjUtil;
6 -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO;  
7 -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO; 6 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
  7 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*;
8 import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper; 8 import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper;
9 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 9 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
10 import cn.iocoder.yudao.module.system.api.dept.DeptApi; 10 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
@@ -17,17 +17,14 @@ import jakarta.annotation.Resource; @@ -17,17 +17,14 @@ import jakarta.annotation.Resource;
17 import org.springframework.stereotype.Service; 17 import org.springframework.stereotype.Service;
18 import org.springframework.validation.annotation.Validated; 18 import org.springframework.validation.annotation.Validated;
19 19
  20 +import java.math.BigDecimal;
20 import java.time.LocalDateTime; 21 import java.time.LocalDateTime;
21 -import java.util.Collections;  
22 -import java.util.List;  
23 -import java.util.Map;  
24 -import java.util.function.Function; 22 +import java.util.*;
25 23
26 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;  
27 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; 24 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
28 25
29 /** 26 /**
30 - * CRM 数据统计 员工客户分析 Service 实现类 27 + * CRM 客户分析 Service 实现类
31 * 28 *
32 * @author dhb52 29 * @author dhb52
33 */ 30 */
@@ -35,6 +32,13 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. @@ -35,6 +32,13 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
35 @Validated 32 @Validated
36 public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerService { 33 public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerService {
37 34
  35 + private static final String SQL_DATE_FORMAT_BY_MONTH = "%Y%m";
  36 + private static final String SQL_DATE_FORMAT_BY_DAY = "%Y%m%d";
  37 +
  38 + private static final String TIME_FORMAT_BY_MONTH = "yyyyMM";
  39 + private static final String TIME_FORMAT_BY_DAY = "yyyyMMdd";
  40 +
  41 +
38 @Resource 42 @Resource
39 private CrmStatisticsCustomerMapper customerMapper; 43 private CrmStatisticsCustomerMapper customerMapper;
40 44
@@ -45,130 +49,315 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe @@ -45,130 +49,315 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
45 @Resource 49 @Resource
46 private DictDataApi dictDataApi; 50 private DictDataApi dictDataApi;
47 51
  52 +
48 @Override 53 @Override
49 - public List<CrmStatisticsCustomerCountVO> getTotalCustomerCount(CrmStatisticsCustomerReqVO reqVO) {  
50 - return getStat(reqVO, customerMapper::selectCustomerCountGroupbyDate); 54 + public List<CrmStatisticsCustomerSummaryByDateRespVO> getCustomerSummaryByDate(CrmStatisticsCustomerReqVO reqVO) {
  55 + // 1. 获得用户编号数组
  56 + final List<Long> userIds = getUserIds(reqVO);
  57 + if (CollUtil.isEmpty(userIds)) {
  58 + return Collections.emptyList();
  59 + }
  60 + reqVO.setUserIds(userIds);
  61 +
  62 + // 2. 获取分项统计数据
  63 + reqVO.setSqlDateFormat(getSqlDateFormat(reqVO.getTimes()[0], reqVO.getTimes()[1]));
  64 + final List<CrmStatisticsCustomerSummaryByDateRespVO> customerCreateCount = customerMapper.selectCustomerCreateCountGroupbyDate(reqVO);
  65 + final List<CrmStatisticsCustomerSummaryByDateRespVO> customerDealCount = customerMapper.selectCustomerDealCountGroupbyDate(reqVO);
  66 +
  67 + // 3. 获取时间序列
  68 + final List<String> times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]);
  69 +
  70 + // 4. 合并统计数据
  71 + List<CrmStatisticsCustomerSummaryByDateRespVO> result = new ArrayList<>(times.size());
  72 + final Map<String, Integer> customerCreateCountMap = convertMap(customerCreateCount, CrmStatisticsCustomerSummaryByDateRespVO::getTime, CrmStatisticsCustomerSummaryByDateRespVO::getCustomerCreateCount);
  73 + final Map<String, Integer> customerDealCountMap = convertMap(customerDealCount, CrmStatisticsCustomerSummaryByDateRespVO::getTime, CrmStatisticsCustomerSummaryByDateRespVO::getCustomerDealCount);
  74 + times.forEach(time -> result.add(
  75 + new CrmStatisticsCustomerSummaryByDateRespVO().setTime(time)
  76 + .setCustomerCreateCount(customerCreateCountMap.getOrDefault(time, 0))
  77 + .setCustomerDealCount(customerDealCountMap.getOrDefault(time, 0))
  78 + ));
  79 +
  80 + return result;
51 } 81 }
52 82
53 @Override 83 @Override
54 - public List<CrmStatisticsCustomerCountVO> getDealTotalCustomerCount(CrmStatisticsCustomerReqVO reqVO) {  
55 - return getStat(reqVO, customerMapper::selectDealCustomerCountGroupbyDate); 84 + public List<CrmStatisticsCustomerSummaryByUserRespVO> getCustomerSummaryByUser(CrmStatisticsCustomerReqVO reqVO) {
  85 + // 1. 获得用户编号数组
  86 + final List<Long> userIds = getUserIds(reqVO);
  87 + if (CollUtil.isEmpty(userIds)) {
  88 + return Collections.emptyList();
  89 + }
  90 + reqVO.setUserIds(userIds);
  91 +
  92 + // 2. 获取分项统计数据
  93 + final List<CrmStatisticsCustomerSummaryByUserRespVO> customerCreateCount = customerMapper.selectCustomerCreateCountGroupbyUser(reqVO);
  94 + final List<CrmStatisticsCustomerSummaryByUserRespVO> customerDealCount = customerMapper.selectCustomerDealCountGroupbyUser(reqVO);
  95 + final List<CrmStatisticsCustomerSummaryByUserRespVO> contractPrice = customerMapper.selectContractPriceGroupbyUser(reqVO);
  96 + final List<CrmStatisticsCustomerSummaryByUserRespVO> receivablePrice = customerMapper.selectReceivablePriceGroupbyUser(reqVO);
  97 +
  98 + // 3. 合并统计数据
  99 + final Map<Long, Integer> customerCreateCountMap = convertMap(customerCreateCount, CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, CrmStatisticsCustomerSummaryByUserRespVO::getCustomerCreateCount);
  100 + final Map<Long, Integer> customerDealCountMap = convertMap(customerDealCount, CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount);
  101 + final Map<Long, BigDecimal> contractPriceMap = convertMap(contractPrice, CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, CrmStatisticsCustomerSummaryByUserRespVO::getContractPrice);
  102 + final Map<Long, BigDecimal> receivablePriceMap = convertMap(receivablePrice, CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, CrmStatisticsCustomerSummaryByUserRespVO::getReceivablePrice);
  103 + List<CrmStatisticsCustomerSummaryByUserRespVO> result = new ArrayList<>(userIds.size());
  104 + userIds.forEach(userId -> {
  105 + final CrmStatisticsCustomerSummaryByUserRespVO respVO = new CrmStatisticsCustomerSummaryByUserRespVO();
  106 + respVO.setOwnerUserId(userId);
  107 + respVO.setCustomerCreateCount(customerCreateCountMap.getOrDefault(userId, 0))
  108 + .setCustomerDealCount(customerDealCountMap.getOrDefault(userId, 0))
  109 + .setContractPrice(contractPriceMap.getOrDefault(userId, BigDecimal.valueOf(0)))
  110 + .setReceivablePrice(receivablePriceMap.getOrDefault(userId, BigDecimal.valueOf(0)));
  111 + result.add(respVO);
  112 + });
  113 +
  114 + // 4. 拼接用户信息
  115 + appendUserInfo(result);
  116 +
  117 + return result;
56 } 118 }
57 119
58 @Override 120 @Override
59 - public List<CrmStatisticsCustomerCountVO> getRecordCount(CrmStatisticsCustomerReqVO reqVO) { 121 + public List<CrmStatisticsFollowupSummaryByDateRespVO> getFollowupSummaryByDate(CrmStatisticsCustomerReqVO reqVO) {
  122 + // 1. 获得用户编号数组
  123 + final List<Long> userIds = getUserIds(reqVO);
  124 + if (CollUtil.isEmpty(userIds)) {
  125 + return Collections.emptyList();
  126 + }
  127 + reqVO.setUserIds(userIds);
  128 +
  129 + // 2. 获取分项统计数据
  130 + reqVO.setSqlDateFormat(getSqlDateFormat(reqVO.getTimes()[0], reqVO.getTimes()[1]));
60 reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); 131 reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType());
61 - return getStat(reqVO, customerMapper::selectRecordCountGroupbyDate); 132 + final List<CrmStatisticsFollowupSummaryByDateRespVO> followupRecordCount = customerMapper.selectFollowupRecordCountGroupbyDate(reqVO);
  133 + final List<CrmStatisticsFollowupSummaryByDateRespVO> followupCustomerCount = customerMapper.selectFollowupCustomerCountGroupbyDate(reqVO);
  134 +
  135 + // 3. 获取时间序列
  136 + final List<String> times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]);
  137 +
  138 + // 4. 合并统计数据
  139 + List<CrmStatisticsFollowupSummaryByDateRespVO> result = new ArrayList<>(times.size());
  140 + final Map<String, Integer> followupRecordCountMap = convertMap(followupRecordCount, CrmStatisticsFollowupSummaryByDateRespVO::getTime, CrmStatisticsFollowupSummaryByDateRespVO::getFollowupRecordCount);
  141 + final Map<String, Integer> followupCustomerCountMap = convertMap(followupCustomerCount, CrmStatisticsFollowupSummaryByDateRespVO::getTime, CrmStatisticsFollowupSummaryByDateRespVO::getFollowupCustomerCount);
  142 + times.forEach(time -> result.add(
  143 + new CrmStatisticsFollowupSummaryByDateRespVO().setTime(time)
  144 + .setFollowupRecordCount(followupRecordCountMap.getOrDefault(time, 0))
  145 + .setFollowupCustomerCount(followupCustomerCountMap.getOrDefault(time, 0))
  146 + ));
  147 +
  148 + return result;
62 } 149 }
63 150
64 @Override 151 @Override
65 - public List<CrmStatisticsCustomerCountVO> getDistinctRecordCount(CrmStatisticsCustomerReqVO reqVO) { 152 + public List<CrmStatisticsFollowupSummaryByUserRespVO> getFollowupSummaryByUser(CrmStatisticsCustomerReqVO reqVO) {
  153 + // 1. 获得用户编号数组
  154 + final List<Long> userIds = getUserIds(reqVO);
  155 + if (CollUtil.isEmpty(userIds)) {
  156 + return Collections.emptyList();
  157 + }
  158 + reqVO.setUserIds(userIds);
  159 +
  160 + // 2. 获取分项统计数据
66 reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); 161 reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType());
67 - return getStat(reqVO, customerMapper::selectDistinctRecordCountGroupbyDate); 162 + final List<CrmStatisticsFollowupSummaryByUserRespVO> followupRecordCount = customerMapper.selectFollowupRecordCountGroupbyUser(reqVO);
  163 + final List<CrmStatisticsFollowupSummaryByUserRespVO> followupCustomerCount = customerMapper.selectFollowupCustomerCountGroupbyUser(reqVO);
  164 +
  165 + // 3. 合并统计数据
  166 + final Map<Long, Integer> followupRecordCountMap = convertMap(followupRecordCount, CrmStatisticsFollowupSummaryByUserRespVO::getOwnerUserId, CrmStatisticsFollowupSummaryByUserRespVO::getFollowupRecordCount);
  167 + final Map<Long, Integer> followupCustomerCountMap = convertMap(followupCustomerCount, CrmStatisticsFollowupSummaryByUserRespVO::getOwnerUserId, CrmStatisticsFollowupSummaryByUserRespVO::getFollowupCustomerCount);
  168 + List<CrmStatisticsFollowupSummaryByUserRespVO> result = new ArrayList<>(userIds.size());
  169 + userIds.forEach(userId -> {
  170 + final CrmStatisticsFollowupSummaryByUserRespVO stat = new CrmStatisticsFollowupSummaryByUserRespVO()
  171 + .setFollowupRecordCount(followupRecordCountMap.getOrDefault(userId, 0))
  172 + .setFollowupCustomerCount(followupCustomerCountMap.getOrDefault(userId, 0));
  173 + stat.setOwnerUserId(userId);
  174 + result.add(stat);
  175 + });
  176 +
  177 + // 4. 拼接用户信息
  178 + appendUserInfo(result);
  179 +
  180 + return result;
68 } 181 }
69 182
70 @Override 183 @Override
71 - public List<CrmStatisticsCustomerCountVO> getRecordTypeCount(CrmStatisticsCustomerReqVO reqVO) {  
72 - // 1. 获得用户编号数组: 如果用户编号为空, 则获得部门下的用户编号数组  
73 - if (ObjUtil.isNotNull(reqVO.getUserId())) {  
74 - reqVO.setUserIds(List.of(reqVO.getUserId()));  
75 - } else {  
76 - reqVO.setUserIds(getUserIds(reqVO.getDeptId()));  
77 - }  
78 - if (CollUtil.isEmpty(reqVO.getUserIds())) { 184 + public List<CrmStatisticsFollowupSummaryByTypeRespVO> getFollowupSummaryByType(CrmStatisticsCustomerReqVO reqVO) {
  185 + // 1. 获得用户编号数组
  186 + final List<Long> userIds = getUserIds(reqVO);
  187 + if (CollUtil.isEmpty(userIds)) {
79 return Collections.emptyList(); 188 return Collections.emptyList();
80 } 189 }
  190 + reqVO.setUserIds(userIds);
81 191
82 // 2. 获得排行数据 192 // 2. 获得排行数据
83 reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); 193 reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType());
84 - List<CrmStatisticsCustomerCountVO> stats = customerMapper.selectRecordCountGroupbyType(reqVO); 194 + List<CrmStatisticsFollowupSummaryByTypeRespVO> stats = customerMapper.selectFollowupRecordCountGroupbyType(reqVO);
85 195
86 // 3. 获取字典数据 196 // 3. 获取字典数据
87 List<DictDataRespDTO> followUpTypes = dictDataApi.getDictDataList("crm_follow_up_type"); 197 List<DictDataRespDTO> followUpTypes = dictDataApi.getDictDataList("crm_follow_up_type");
88 final Map<String, String> followUpTypeMap = convertMap(followUpTypes, DictDataRespDTO::getValue, DictDataRespDTO::getLabel); 198 final Map<String, String> followUpTypeMap = convertMap(followUpTypes, DictDataRespDTO::getValue, DictDataRespDTO::getLabel);
89 stats.forEach(stat -> { 199 stats.forEach(stat -> {
90 - stat.setCategory(followUpTypeMap.get(stat.getCategory())); 200 + stat.setFollowupType(followUpTypeMap.get(stat.getFollowupType()));
91 }); 201 });
92 202
93 return stats; 203 return stats;
94 } 204 }
95 205
96 @Override 206 @Override
97 - public List<CrmStatisticsCustomerCountVO> getCustomerCycle(CrmStatisticsCustomerReqVO reqVO) {  
98 - return getStat(reqVO, customerMapper::selectCustomerCycleGroupbyDate); 207 + public List<CrmStatisticsCustomerContractSummaryRespVO> getContractSummary(CrmStatisticsCustomerReqVO reqVO) {
  208 + // 1. 获得用户编号数组
  209 + final List<Long> userIds = getUserIds(reqVO);
  210 + if (CollUtil.isEmpty(userIds)) {
  211 + return Collections.emptyList();
  212 + }
  213 + reqVO.setUserIds(userIds);
  214 +
  215 + List<CrmStatisticsCustomerContractSummaryRespVO> contractSummary = customerMapper.selectContractSummary(reqVO);
  216 +
  217 + // 2. 拼接用户信息
  218 + final Set<Long> userIdSet = new HashSet<>();
  219 + userIdSet.addAll(userIds);
  220 + userIdSet.addAll(convertSet(contractSummary, CrmStatisticsCustomerContractSummaryRespVO::getCreatorUserId));
  221 + final Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIdSet);
  222 + contractSummary.forEach(contract -> contract.setCreatorUserName(userMap.get(contract.getCreatorUserId()).getNickname())
  223 + .setOwnerUserName(userMap.get(contract.getOwnerUserId()).getNickname()));
  224 +
  225 + return contractSummary;
  226 + }
  227 +
  228 + @Override
  229 + public List<CrmStatisticsCustomerDealCycleByDateRespVO> getCustomerDealCycleByDate(CrmStatisticsCustomerReqVO reqVO) {
  230 + // 1. 获得用户编号数组
  231 + final List<Long> userIds = getUserIds(reqVO);
  232 + if (CollUtil.isEmpty(userIds)) {
  233 + return Collections.emptyList();
  234 + }
  235 + reqVO.setUserIds(userIds);
  236 +
  237 + // 2. 获取分项统计数据
  238 + reqVO.setSqlDateFormat(getSqlDateFormat(reqVO.getTimes()[0], reqVO.getTimes()[1]));
  239 + reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType());
  240 + final List<CrmStatisticsCustomerDealCycleByDateRespVO> customerDealCycle = customerMapper.selectCustomerDealCycleGroupbyDate(reqVO);
  241 +
  242 + // 3. 获取时间序列
  243 + final List<String> times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]);
  244 +
  245 + // 4. 合并统计数据
  246 + List<CrmStatisticsCustomerDealCycleByDateRespVO> result = new ArrayList<>(times.size());
  247 + final Map<String, Double> customerDealCycleMap = convertMap(customerDealCycle, CrmStatisticsCustomerDealCycleByDateRespVO::getTime, CrmStatisticsCustomerDealCycleByDateRespVO::getCustomerDealCycle);
  248 + times.forEach(time -> result.add(
  249 + new CrmStatisticsCustomerDealCycleByDateRespVO().setTime(time)
  250 + .setCustomerDealCycle(customerDealCycleMap.getOrDefault(time, Double.valueOf(0)))
  251 + ));
  252 +
  253 + return result;
  254 + }
  255 +
  256 + @Override
  257 + public List<CrmStatisticsCustomerDealCycleByUserRespVO> getCustomerDealCycleByUser(CrmStatisticsCustomerReqVO reqVO) {
  258 + // 1. 获得用户编号数组
  259 + final List<Long> userIds = getUserIds(reqVO);
  260 + if (CollUtil.isEmpty(userIds)) {
  261 + return Collections.emptyList();
  262 + }
  263 + reqVO.setUserIds(userIds);
  264 +
  265 + // 2. 获取分项统计数据
  266 + reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType());
  267 + final List<CrmStatisticsCustomerDealCycleByUserRespVO> customerDealCycle = customerMapper.selectCustomerDealCycleGroupbyUser(reqVO);
  268 + final List<CrmStatisticsCustomerSummaryByUserRespVO> customerDealCount = customerMapper.selectCustomerDealCountGroupbyUser(reqVO);
  269 +
  270 + // 3. 合并统计数据
  271 + final Map<Long, Double> customerDealCycleMap = convertMap(customerDealCycle, CrmStatisticsCustomerDealCycleByUserRespVO::getOwnerUserId, CrmStatisticsCustomerDealCycleByUserRespVO::getCustomerDealCycle);
  272 + final Map<Long, Integer> customerDealCountMap = convertMap(customerDealCount, CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount);
  273 + List<CrmStatisticsCustomerDealCycleByUserRespVO> result = new ArrayList<>(userIds.size());
  274 + userIds.forEach(userId -> {
  275 + final CrmStatisticsCustomerDealCycleByUserRespVO stat = new CrmStatisticsCustomerDealCycleByUserRespVO()
  276 + .setCustomerDealCycle(customerDealCycleMap.getOrDefault(userId, 0.0))
  277 + .setCustomerDealCount(customerDealCountMap.getOrDefault(userId, 0));
  278 + stat.setOwnerUserId(userId);
  279 + result.add(stat);
  280 + });
  281 +
  282 + // 4. 拼接用户信息
  283 + appendUserInfo(result);
  284 +
  285 + return result;
  286 + }
  287 +
  288 + /**
  289 + * 拼接用户信息(昵称)
  290 + *
  291 + * @param stats 统计数据
  292 + */
  293 + private <T extends CrmStatisticsCustomerByUserBaseRespVO> void appendUserInfo(List<T> stats) {
  294 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(stats, CrmStatisticsCustomerByUserBaseRespVO::getOwnerUserId));
  295 + stats.forEach(stat -> MapUtils.findAndThen(userMap, stat.getOwnerUserId(), user -> {
  296 + stat.setOwnerUserName(user.getNickname());
  297 + }));
99 } 298 }
100 299
101 /** 300 /**
102 - * 获得统计数据 301 + * 获取用户编号数组。如果用户编号为空, 则获得部门下的用户编号数组,包括子部门的所有用户编号
103 * 302 *
104 - * @param reqVO 参数  
105 - * @param statFunction 统计方法  
106 - * @return 统计数据 303 + * @param reqVO 请求参数
  304 + * @return 用户编号数组
107 */ 305 */
108 - private List<CrmStatisticsCustomerCountVO> getStat(CrmStatisticsCustomerReqVO reqVO, Function<CrmStatisticsCustomerReqVO, List<CrmStatisticsCustomerCountVO>> statFunction) {  
109 - // 1. 获得用户编号数组: 如果用户编号为空, 则获得部门下的用户编号数组 306 + private List<Long> getUserIds(CrmStatisticsCustomerReqVO reqVO) {
110 if (ObjUtil.isNotNull(reqVO.getUserId())) { 307 if (ObjUtil.isNotNull(reqVO.getUserId())) {
111 - reqVO.setUserIds(List.of(reqVO.getUserId())); 308 + return List.of(reqVO.getUserId());
112 } else { 309 } else {
113 - reqVO.setUserIds(getUserIds(reqVO.getDeptId()));  
114 - }  
115 - if (CollUtil.isEmpty(reqVO.getUserIds())) {  
116 - return Collections.emptyList(); 310 + // 1. 获得部门列表
  311 + final Long deptId = reqVO.getDeptId();
  312 + List<Long> deptIds = convertList(deptApi.getChildDeptList(deptId), DeptRespDTO::getId);
  313 + deptIds.add(deptId);
  314 + // 2. 获得用户编号
  315 + return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId);
117 } 316 }
  317 + }
118 318
119 - // 2. 生成日期格式  
120 - LocalDateTime startTime = reqVO.getTimes()[0];  
121 - final LocalDateTime endTime = reqVO.getTimes()[1];  
122 - final long days = LocalDateTimeUtil.between(startTime, endTime).toDays();  
123 - boolean byMonth = days > 31;  
124 - if (byMonth) {  
125 - // 按月  
126 - reqVO.setSqlDateFormat("%Y%m");  
127 - } else {  
128 - // 按日  
129 - reqVO.setSqlDateFormat("%Y%m%d");  
130 - }  
131 319
132 - // 3. 获得排行数据  
133 - List<CrmStatisticsCustomerCountVO> stats = statFunction.apply(reqVO); 320 + /**
  321 + * 判断是否按照 月粒度 统计
  322 + *
  323 + * @param startTime 开始时间
  324 + * @param endTime 结束时间
  325 + * @return 是, 按月粒度, 否则按天粒度统计。
  326 + */
  327 + private boolean queryByMonth(LocalDateTime startTime, LocalDateTime endTime) {
  328 + return LocalDateTimeUtil.between(startTime, endTime).toDays() > 31;
  329 + }
134 330
135 - // 4. 生成时间序列  
136 - List<CrmStatisticsCustomerCountVO> result = CollUtil.newArrayList(); 331 + /**
  332 + * 生成时间序列
  333 + *
  334 + * @param startTime 开始时间
  335 + * @param endTime 结束时间
  336 + * @return 时间序列
  337 + */
  338 + private List<String> generateTimeSeries(LocalDateTime startTime, LocalDateTime endTime) {
  339 + boolean byMonth = queryByMonth(startTime, endTime);
  340 + List<String> times = CollUtil.newArrayList();
137 while (!startTime.isAfter(endTime)) { 341 while (!startTime.isAfter(endTime)) {
138 - final String category = LocalDateTimeUtil.format(startTime, byMonth ? "yyyyMM" : "yyyyMMdd");  
139 - result.add(new CrmStatisticsCustomerCountVO().setCategory(category)); 342 + times.add(LocalDateTimeUtil.format(startTime, byMonth ? TIME_FORMAT_BY_MONTH : TIME_FORMAT_BY_DAY));
140 if (byMonth) 343 if (byMonth)
141 startTime = startTime.plusMonths(1); 344 startTime = startTime.plusMonths(1);
142 else 345 else
143 startTime = startTime.plusDays(1); 346 startTime = startTime.plusDays(1);
144 } 347 }
145 348
146 - // 5. 使用时间序列填充结果  
147 - final Map<String, CrmStatisticsCustomerCountVO> statMap = convertMap(stats,  
148 - CrmStatisticsCustomerCountVO::getCategory,  
149 - Function.identity());  
150 - result.forEach(r -> {  
151 - if (statMap.containsKey(r.getCategory())) {  
152 - r.setCount(statMap.get(r.getCategory()).getCount())  
153 - .setCycle(statMap.get(r.getCategory()).getCycle());  
154 - }  
155 - });  
156 -  
157 - return result; 349 + return times;
158 } 350 }
159 351
160 -  
161 /** 352 /**
162 - * 获得部门下的用户编号数组,包括子部门的 353 + * 获取 SQL 查询 GROUP BY 的时间格式
163 * 354 *
164 - * @param deptId 部门编号  
165 - * @return 用户编号数组 355 + * @param startTime 开始时间
  356 + * @param endTime 结束时间
  357 + * @return SQL 查询 GROUP BY 的时间格式
166 */ 358 */
167 - public List<Long> getUserIds(Long deptId) {  
168 - // 1. 获得部门列表  
169 - List<Long> deptIds = convertList(deptApi.getChildDeptList(deptId), DeptRespDTO::getId);  
170 - deptIds.add(deptId);  
171 - // 2. 获得用户编号  
172 - return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId); 359 + private String getSqlDateFormat(LocalDateTime startTime, LocalDateTime endTime) {
  360 + return queryByMonth(startTime, endTime) ? SQL_DATE_FORMAT_BY_MONTH : SQL_DATE_FORMAT_BY_DAY;
173 } 361 }
  362 +
174 } 363 }
yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml
@@ -2,115 +2,238 @@ @@ -2,115 +2,238 @@
2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3 <mapper namespace="cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper"> 3 <mapper namespace="cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper">
4 4
  5 + <select id="selectCustomerCreateCountGroupbyDate"
  6 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByDateRespVO">
  7 + SELECT
  8 + DATE_FORMAT( create_time, #{sqlDateFormat} ) AS time,
  9 + count(*) AS customerCreateCount
  10 + FROM crm_customer
  11 + WHERE deleted = 0
  12 + AND owner_user_id IN
  13 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  14 + #{userId}
  15 + </foreach>
  16 + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
  17 + #{times[1],javaType=java.time.LocalDateTime}
  18 + GROUP BY time
  19 + </select>
5 20
6 - <select id="selectCustomerCountGroupbyDate"  
7 - resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO"> 21 + <select id="selectCustomerDealCountGroupbyDate"
  22 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByDateRespVO">
8 SELECT 23 SELECT
9 - DATE_FORMAT( create_time, #{sqlDateFormat,javaType=java.lang.String} ) AS category,  
10 - count(*) AS count  
11 - FROM  
12 - crm_customer  
13 - WHERE  
14 - deleted = 0  
15 - AND owner_user_id IN  
16 - <foreach collection="userIds" item="userId" open="(" close=")" separator=",">  
17 - #{userId}  
18 - </foreach>  
19 - AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND  
20 - #{times[1],javaType=java.time.LocalDateTime}  
21 - GROUP BY category 24 + DATE_FORMAT( b.order_date, #{sqlDateFormat} ) AS time,
  25 + count( DISTINCT a.id ) AS customerDealCount
  26 + FROM crm_customer AS a
  27 + LEFT JOIN crm_contract AS b ON b.customer_id = a.id
  28 + WHERE a.deleted = 0 AND b.deleted = 0
  29 + AND b.audit_status = 20
  30 + AND a.owner_user_id IN
  31 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  32 + #{userId}
  33 + </foreach>
  34 + AND b.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
  35 + #{times[1],javaType=java.time.LocalDateTime}
  36 + GROUP BY time
  37 + </select>
  38 +
  39 + <select id="selectCustomerCreateCountGroupbyUser"
  40 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByUserRespVO">
  41 + SELECT owner_user_id, COUNT(1) AS customer_create_count
  42 + FROM crm_customer
  43 + WHERE deleted = 0
  44 + AND owner_user_id in
  45 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  46 + #{userId}
  47 + </foreach>
  48 + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
  49 + #{times[1],javaType=java.time.LocalDateTime}
  50 + GROUP BY owner_user_id
  51 + </select>
  52 +
  53 + <select id="selectCustomerDealCountGroupbyUser"
  54 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByUserRespVO">
  55 + SELECT a.owner_user_id, count( DISTINCT a.id ) AS customer_deal_count
  56 + FROM crm_customer AS a
  57 + LEFT JOIN crm_contract AS b ON b.customer_id = a.id
  58 + WHERE a.deleted = 0 AND b.deleted = 0
  59 + AND b.audit_status = 20
  60 + AND a.owner_user_id IN
  61 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  62 + #{userId}
  63 + </foreach>
  64 + AND b.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
  65 + #{times[1],javaType=java.time.LocalDateTime}
  66 + GROUP BY a.owner_user_id
  67 + </select>
  68 +
  69 + <select id="selectContractPriceGroupbyUser"
  70 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByUserRespVO">
  71 + SELECT owner_user_id, IFNULL(SUM(total_price), 0) AS contract_price
  72 + FROM crm_contract
  73 + WHERE deleted = 0
  74 + AND audit_status = 20
  75 + AND owner_user_id in
  76 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  77 + #{userId}
  78 + </foreach>
  79 + AND order_date BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
  80 + #{times[1],javaType=java.time.LocalDateTime}
  81 + GROUP BY owner_user_id
  82 + </select>
  83 +
  84 +
  85 + <select id="selectReceivablePriceGroupbyUser"
  86 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByUserRespVO">
  87 + SELECT owner_user_id,
  88 + IFNULL(SUM(price), 0) AS receivable_price
  89 + FROM crm_receivable
  90 + WHERE deleted = 0
  91 + AND audit_status = 20
  92 + AND owner_user_id IN
  93 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  94 + #{userId}
  95 + </foreach>
  96 + AND return_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
  97 + #{times[1],javaType=java.time.LocalDateTime}
  98 + GROUP BY owner_user_id
22 </select> 99 </select>
23 100
24 - <select id="selectDealCustomerCountGroupbyDate"  
25 - resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO"> 101 + <select id="selectFollowupRecordCountGroupbyDate"
  102 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowupSummaryByDateRespVO">
  103 + SELECT DATE_FORMAT( create_time, #{sqlDateFormat} ) AS time,
  104 + count(*) AS followup_record_count
  105 + FROM crm_follow_up_record
  106 + WHERE creator IN
  107 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  108 + #{userId}
  109 + </foreach>
  110 + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
  111 + #{times[1],javaType=java.time.LocalDateTime}
  112 + AND biz_type = #{bizType}
  113 + GROUP BY time
  114 + </select>
  115 +
  116 + <select id="selectFollowupCustomerCountGroupbyDate"
  117 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowupSummaryByDateRespVO">
26 SELECT 118 SELECT
27 - DATE_FORMAT( b.order_date, #{sqlDateFormat,javaType=java.lang.String} ) AS category,  
28 - count( DISTINCT a.id ) AS count  
29 - FROM  
30 - crm_customer AS a  
31 - LEFT JOIN crm_contract AS b ON b.customer_id = a.id  
32 - WHERE  
33 - a.deleted = 0 AND b.deleted = 0  
34 - AND b.audit_status = 20  
35 - AND a.owner_user_id IN  
36 - <foreach collection="userIds" item="userId" open="(" close=")" separator=",">  
37 - #{userId}  
38 - </foreach>  
39 - AND b.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND  
40 - #{times[1],javaType=java.time.LocalDateTime}  
41 - GROUP BY category 119 + DATE_FORMAT( create_time, #{sqlDateFormat} ) AS time,
  120 + count(DISTINCT biz_id) AS followup_customer_count
  121 + FROM crm_follow_up_record
  122 + WHERE creator IN
  123 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  124 + #{userId}
  125 + </foreach>
  126 + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
  127 + #{times[1],javaType=java.time.LocalDateTime}
  128 + AND biz_type = #{bizType}
  129 + GROUP BY time
42 </select> 130 </select>
43 131
44 - <select id="selectRecordCountGroupbyDate"  
45 - resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO"> 132 + <select id="selectFollowupRecordCountGroupbyUser"
  133 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowupSummaryByUserRespVO">
  134 + SELECT creator as owner_user_id,
  135 + count(*) AS followup_record_count
  136 + FROM crm_follow_up_record
  137 + WHERE creator IN
  138 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  139 + #{userId}
  140 + </foreach>
  141 + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
  142 + #{times[1],javaType=java.time.LocalDateTime}
  143 + AND biz_type = #{bizType}
  144 + GROUP BY creator
  145 + </select>
  146 +
  147 + <select id="selectFollowupCustomerCountGroupbyUser"
  148 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowupSummaryByUserRespVO">
  149 + SELECT creator as owner_user_id,
  150 + count(DISTINCT biz_id) AS followup_customer_count
  151 + FROM crm_follow_up_record
  152 + WHERE creator IN
  153 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  154 + #{userId}
  155 + </foreach>
  156 + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
  157 + #{times[1],javaType=java.time.LocalDateTime}
  158 + AND biz_type = #{bizType}
  159 + GROUP BY creator
  160 + </select>
  161 +
  162 + <select id="selectFollowupRecordCountGroupbyType"
  163 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowupSummaryByTypeRespVO">
46 SELECT 164 SELECT
47 - DATE_FORMAT( create_time, #{sqlDateFormat,javaType=java.lang.String} ) AS category,  
48 - count(*) AS count  
49 - FROM  
50 - crm_follow_up_record  
51 - WHERE  
52 - creator IN  
53 - <foreach collection="userIds" item="userId" open="(" close=")" separator=",">  
54 - #{userId}  
55 - </foreach>  
56 - AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND  
57 - #{times[1],javaType=java.time.LocalDateTime}  
58 - AND biz_type = #{bizType,javaType=java.lang.Integer}  
59 - GROUP BY category 165 + type AS followupType,
  166 + count(*) AS followup_record_count
  167 + FROM crm_follow_up_record
  168 + WHERE creator IN
  169 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  170 + #{userId}
  171 + </foreach>
  172 + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
  173 + #{times[1],javaType=java.time.LocalDateTime}
  174 + AND biz_type = #{bizType}
  175 + GROUP BY followupType
60 </select> 176 </select>
61 177
62 - <select id="selectDistinctRecordCountGroupbyDate"  
63 - resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO"> 178 + <select id="selectContractSummary"
  179 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerContractSummaryRespVO">
64 SELECT 180 SELECT
65 - DATE_FORMAT( create_time, #{sqlDateFormat,javaType=java.lang.String} ) AS category,  
66 - count(DISTINCT biz_id) AS count  
67 - FROM  
68 - crm_follow_up_record  
69 - WHERE  
70 - creator IN  
71 - <foreach collection="userIds" item="userId" open="(" close=")" separator=",">  
72 - #{userId}  
73 - </foreach>  
74 - AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND  
75 - #{times[1],javaType=java.time.LocalDateTime}  
76 - AND biz_type = #{bizType,javaType=java.lang.Integer}  
77 - GROUP BY category 181 + a.`name` AS customer_name,
  182 + b.`name` AS contract_name,
  183 + b.total_price AS contract_price,
  184 + IFNULL( c.price, 0 ) AS receivable_price,
  185 + a.owner_user_id,
  186 + a.creator AS creator_user_id,
  187 + a.create_time,
  188 + b.order_date
  189 + FROM
  190 + crm_customer AS a
  191 + INNER JOIN crm_contract AS b ON a.id = b.customer_id
  192 + LEFT JOIN crm_receivable AS c ON b.id = c.contract_id
  193 + WHERE a.deleted = 0 AND b.deleted = 0 AND c.deleted = 0
  194 + AND b.audit_status = 20
  195 + AND a.owner_user_id IN
  196 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  197 + #{userId}
  198 + </foreach>
  199 + AND b.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
  200 + #{times[1],javaType=java.time.LocalDateTime}
78 </select> 201 </select>
79 202
80 - <select id="selectRecordCountGroupbyType"  
81 - resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO"> 203 + <select id="selectCustomerDealCycleGroupbyDate"
  204 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerDealCycleByDateRespVO">
82 SELECT 205 SELECT
83 - type AS category,  
84 - count(*) AS count  
85 - FROM crm_follow_up_record  
86 - WHERE  
87 - creator IN  
88 - <foreach collection="userIds" item="userId" open="(" close=")" separator=",">  
89 - #{userId}  
90 - </foreach>  
91 - AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND  
92 - #{times[1],javaType=java.time.LocalDateTime}  
93 - AND biz_type = #{bizType,javaType=java.lang.Integer}  
94 - GROUP BY category 206 + DATE_FORMAT( b.order_date, #{sqlDateFormat} ) AS time,
  207 + IFNULL( TRUNCATE ( AVG( TIMESTAMPDIFF( DAY, a.create_time, b.order_date )), 1 ), 0 ) AS customer_deal_cycle
  208 + FROM crm_customer AS a
  209 + LEFT JOIN crm_contract AS b ON b.customer_id = a.id
  210 + WHERE a.deleted = 0 AND b.deleted = 0
  211 + AND b.audit_status = 20
  212 + AND a.owner_user_id IN
  213 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  214 + #{userId}
  215 + </foreach>
  216 + AND b.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
  217 + #{times[1],javaType=java.time.LocalDateTime}
  218 + GROUP BY time
95 </select> 219 </select>
96 220
97 - <select id="selectCustomerCycleGroupbyDate"  
98 - resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO"> 221 + <select id="selectCustomerDealCycleGroupbyUser"
  222 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerDealCycleByUserRespVO">
99 SELECT 223 SELECT
100 - DATE_FORMAT( b.order_date, #{sqlDateFormat,javaType=java.lang.String} ) AS category,  
101 - IFNULL( TRUNCATE ( AVG( TIMESTAMPDIFF( DAY, a.create_time, b.order_date )), 1 ), 0 ) AS cycle  
102 - FROM crm_customer AS a  
103 - LEFT JOIN crm_contract AS b ON b.customer_id = a.id  
104 - WHERE  
105 - a.deleted = 0 AND b.deleted = 0  
106 - AND b.audit_status = 20  
107 - AND a.owner_user_id IN  
108 - <foreach collection="userIds" item="userId" open="(" close=")" separator=",">  
109 - #{userId}  
110 - </foreach>  
111 - AND b.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND  
112 - #{times[1],javaType=java.time.LocalDateTime}  
113 - GROUP BY category 224 + a.owner_user_id,
  225 + IFNULL( TRUNCATE ( AVG( TIMESTAMPDIFF( DAY, a.create_time, b.order_date )), 1 ), 0 ) AS customer_deal_cycle
  226 + FROM crm_customer AS a
  227 + LEFT JOIN crm_contract AS b ON b.customer_id = a.id
  228 + WHERE a.deleted = 0 AND b.deleted = 0
  229 + AND b.audit_status = 20
  230 + AND a.owner_user_id IN
  231 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  232 + #{userId}
  233 + </foreach>
  234 + AND b.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
  235 + #{times[1],javaType=java.time.LocalDateTime}
  236 + GROUP BY a.owner_user_id
114 </select> 237 </select>
115 238
116 </mapper> 239 </mapper>