Commit 85817a2426de151d1f1c33353533ee963ce53d09

Authored by 芋道源码
Committed by Gitee
2 parents 89fc83c4 0386820a

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

Merge pull request !902 from dhb52/develop
Showing 16 changed files with 890 additions and 283 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 com.fasterxml.jackson.annotation.JsonIgnore;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +
  7 +/**
  8 + * 用户客户统计响应 Base VO
  9 + */
  10 +@Data
  11 +public class CrmStatisticsCustomerByUserBaseRespVO {
  12 +
  13 + @Schema(description = "负责人ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  14 + @JsonIgnore
  15 + private Long ownerUserId;
  16 +
  17 + @Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
  18 + private String ownerUserName;
  19 +
  20 +}
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 com.fasterxml.jackson.annotation.JsonFormat;
  5 +import com.fasterxml.jackson.annotation.JsonIgnore;
  6 +import io.swagger.v3.oas.annotations.media.Schema;
  7 +import lombok.Data;
  8 +import org.springframework.format.annotation.DateTimeFormat;
  9 +
  10 +import java.math.BigDecimal;
  11 +import java.time.LocalDate;
  12 +import java.time.LocalDateTime;
  13 +
  14 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
  15 +
  16 +@Schema(description = "管理后台 - CRM 客户转化率分析 VO")
  17 +@Data
  18 +public class CrmStatisticsCustomerContractSummaryRespVO {
  19 +
  20 + @Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
  21 + private String customerName;
  22 +
  23 + @Schema(description = "合同名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "演示合同")
  24 + private String contractName;
  25 +
  26 + @Schema(description = "合同总金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1200.00")
  27 + private BigDecimal totalPrice;
  28 +
  29 + @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1200.00")
  30 + private BigDecimal receivablePrice;
  31 +
  32 + @Schema(description = "客户行业ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  33 + @JsonIgnore
  34 + private String industryId;
  35 +
  36 + @Schema(description = "客户行业", requiredMode = Schema.RequiredMode.REQUIRED, example = "金融")
  37 + private String industryName;
  38 +
  39 + @Schema(description = "客户来源ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  40 + @JsonIgnore
  41 + private String source;
  42 +
  43 + @Schema(description = "客户来源", requiredMode = Schema.RequiredMode.REQUIRED, example = "外呼")
  44 + private String sourceName;
  45 +
  46 + @Schema(description = "负责人ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  47 + @JsonIgnore
  48 + private Long ownerUserId;
  49 +
  50 + @Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
  51 + private String ownerUserName;
  52 +
  53 + @Schema(description = "创建人ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  54 + @JsonIgnore
  55 + private String creatorUserId;
  56 +
  57 + @Schema(description = "创建人", requiredMode = Schema.RequiredMode.REQUIRED, example = "源码")
  58 + private String creatorUserName;
  59 +
  60 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-01 13:24:26")
  61 + private LocalDateTime createTime;
  62 +
  63 + @Schema(description = "下单日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02 00:00:00")
  64 + @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY, timezone = TIME_ZONE_DEFAULT)
  65 + private LocalDate orderDate;
  66 +
  67 +}
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.ZERO;
  20 +
  21 + @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
  22 + private BigDecimal receivablePrice = BigDecimal.ZERO;
  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,9 @@ package cn.iocoder.yudao.module.crm.service.statistics; @@ -3,8 +3,9 @@ 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.framework.common.util.number.NumberUtils;
  8 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*;
8 import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper; 9 import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper;
9 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 10 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
10 import cn.iocoder.yudao.module.system.api.dept.DeptApi; 11 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
@@ -17,17 +18,19 @@ import jakarta.annotation.Resource; @@ -17,17 +18,19 @@ import jakarta.annotation.Resource;
17 import org.springframework.stereotype.Service; 18 import org.springframework.stereotype.Service;
18 import org.springframework.validation.annotation.Validated; 19 import org.springframework.validation.annotation.Validated;
19 20
  21 +import java.math.BigDecimal;
20 import java.time.LocalDateTime; 22 import java.time.LocalDateTime;
  23 +import java.util.ArrayList;
21 import java.util.Collections; 24 import java.util.Collections;
22 import java.util.List; 25 import java.util.List;
23 import java.util.Map; 26 import java.util.Map;
24 -import java.util.function.Function; 27 +import java.util.stream.Stream;
25 28
26 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;  
27 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; 29 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
  30 +import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*;
28 31
29 /** 32 /**
30 - * CRM 数据统计 员工客户分析 Service 实现类 33 + * CRM 客户分析 Service 实现类
31 * 34 *
32 * @author dhb52 35 * @author dhb52
33 */ 36 */
@@ -35,6 +38,13 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. @@ -35,6 +38,13 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
35 @Validated 38 @Validated
36 public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerService { 39 public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerService {
37 40
  41 + private static final String SQL_DATE_FORMAT_BY_MONTH = "%Y%m";
  42 + private static final String SQL_DATE_FORMAT_BY_DAY = "%Y%m%d";
  43 +
  44 + private static final String TIME_FORMAT_BY_MONTH = "yyyyMM";
  45 + private static final String TIME_FORMAT_BY_DAY = "yyyyMMdd";
  46 +
  47 +
38 @Resource 48 @Resource
39 private CrmStatisticsCustomerMapper customerMapper; 49 private CrmStatisticsCustomerMapper customerMapper;
40 50
@@ -45,130 +55,354 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe @@ -45,130 +55,354 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
45 @Resource 55 @Resource
46 private DictDataApi dictDataApi; 56 private DictDataApi dictDataApi;
47 57
  58 +
48 @Override 59 @Override
49 - public List<CrmStatisticsCustomerCountVO> getTotalCustomerCount(CrmStatisticsCustomerReqVO reqVO) {  
50 - return getStat(reqVO, customerMapper::selectCustomerCountGroupbyDate); 60 + public List<CrmStatisticsCustomerSummaryByDateRespVO> getCustomerSummaryByDate(CrmStatisticsCustomerReqVO reqVO) {
  61 + // 1. 获得用户编号数组
  62 + final List<Long> userIds = getUserIds(reqVO);
  63 + if (CollUtil.isEmpty(userIds)) {
  64 + return Collections.emptyList();
  65 + }
  66 + reqVO.setUserIds(userIds);
  67 +
  68 + // 2. 获取分项统计数据
  69 + reqVO.setSqlDateFormat(getSqlDateFormat(reqVO.getTimes()[0], reqVO.getTimes()[1]));
  70 + final List<CrmStatisticsCustomerSummaryByDateRespVO> customerCreateCount = customerMapper.selectCustomerCreateCountGroupbyDate(reqVO);
  71 + final List<CrmStatisticsCustomerSummaryByDateRespVO> customerDealCount = customerMapper.selectCustomerDealCountGroupbyDate(reqVO);
  72 +
  73 + // 3. 获取时间序列
  74 + final List<String> times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]);
  75 +
  76 + // 4. 合并统计数据
  77 + List<CrmStatisticsCustomerSummaryByDateRespVO> respVoList = new ArrayList<>(times.size());
  78 + final Map<String, Integer> customerCreateCountMap = convertMap(customerCreateCount,
  79 + CrmStatisticsCustomerSummaryByDateRespVO::getTime,
  80 + CrmStatisticsCustomerSummaryByDateRespVO::getCustomerCreateCount);
  81 + final Map<String, Integer> customerDealCountMap = convertMap(customerDealCount,
  82 + CrmStatisticsCustomerSummaryByDateRespVO::getTime,
  83 + CrmStatisticsCustomerSummaryByDateRespVO::getCustomerDealCount);
  84 + times.forEach(time -> respVoList.add(
  85 + new CrmStatisticsCustomerSummaryByDateRespVO().setTime(time)
  86 + .setCustomerCreateCount(customerCreateCountMap.getOrDefault(time, 0))
  87 + .setCustomerDealCount(customerDealCountMap.getOrDefault(time, 0))
  88 + ));
  89 +
  90 + return respVoList;
51 } 91 }
52 92
53 @Override 93 @Override
54 - public List<CrmStatisticsCustomerCountVO> getDealTotalCustomerCount(CrmStatisticsCustomerReqVO reqVO) {  
55 - return getStat(reqVO, customerMapper::selectDealCustomerCountGroupbyDate); 94 + public List<CrmStatisticsCustomerSummaryByUserRespVO> getCustomerSummaryByUser(CrmStatisticsCustomerReqVO reqVO) {
  95 + // 1. 获得用户编号数组
  96 + final List<Long> userIds = getUserIds(reqVO);
  97 + if (CollUtil.isEmpty(userIds)) {
  98 + return Collections.emptyList();
  99 + }
  100 + reqVO.setUserIds(userIds);
  101 +
  102 + // 2. 获取分项统计数据
  103 + final List<CrmStatisticsCustomerSummaryByUserRespVO> customerCreateCount = customerMapper.selectCustomerCreateCountGroupbyUser(reqVO);
  104 + final List<CrmStatisticsCustomerSummaryByUserRespVO> customerDealCount = customerMapper.selectCustomerDealCountGroupbyUser(reqVO);
  105 + final List<CrmStatisticsCustomerSummaryByUserRespVO> contractPrice = customerMapper.selectContractPriceGroupbyUser(reqVO);
  106 + final List<CrmStatisticsCustomerSummaryByUserRespVO> receivablePrice = customerMapper.selectReceivablePriceGroupbyUser(reqVO);
  107 +
  108 + // 3. 合并统计数据
  109 + final Map<Long, Integer> customerCreateCountMap = convertMap(customerCreateCount,
  110 + CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId,
  111 + CrmStatisticsCustomerSummaryByUserRespVO::getCustomerCreateCount);
  112 + final Map<Long, Integer> customerDealCountMap = convertMap(customerDealCount,
  113 + CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId,
  114 + CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount);
  115 + final Map<Long, BigDecimal> contractPriceMap = convertMap(contractPrice,
  116 + CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId,
  117 + CrmStatisticsCustomerSummaryByUserRespVO::getContractPrice);
  118 + final Map<Long, BigDecimal> receivablePriceMap = convertMap(receivablePrice,
  119 + CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId,
  120 + CrmStatisticsCustomerSummaryByUserRespVO::getReceivablePrice);
  121 + List<CrmStatisticsCustomerSummaryByUserRespVO> respVoList = new ArrayList<>(userIds.size());
  122 + userIds.forEach(userId -> {
  123 + final CrmStatisticsCustomerSummaryByUserRespVO vo = new CrmStatisticsCustomerSummaryByUserRespVO();
  124 + vo.setOwnerUserId(userId);
  125 + vo.setCustomerCreateCount(customerCreateCountMap.getOrDefault(userId, 0))
  126 + .setCustomerDealCount(customerDealCountMap.getOrDefault(userId, 0))
  127 + .setContractPrice(contractPriceMap.getOrDefault(userId, BigDecimal.ZERO))
  128 + .setReceivablePrice(receivablePriceMap.getOrDefault(userId, BigDecimal.ZERO));
  129 + respVoList.add(vo);
  130 + });
  131 +
  132 + // 4. 拼接用户信息
  133 + appendUserInfo(respVoList);
  134 +
  135 + return respVoList;
56 } 136 }
57 137
58 @Override 138 @Override
59 - public List<CrmStatisticsCustomerCountVO> getRecordCount(CrmStatisticsCustomerReqVO reqVO) { 139 + public List<CrmStatisticsFollowupSummaryByDateRespVO> getFollowupSummaryByDate(CrmStatisticsCustomerReqVO reqVO) {
  140 + // 1. 获得用户编号数组
  141 + final List<Long> userIds = getUserIds(reqVO);
  142 + if (CollUtil.isEmpty(userIds)) {
  143 + return Collections.emptyList();
  144 + }
  145 + reqVO.setUserIds(userIds);
  146 +
  147 + // 2. 获取分项统计数据
  148 + reqVO.setSqlDateFormat(getSqlDateFormat(reqVO.getTimes()[0], reqVO.getTimes()[1]));
60 reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); 149 reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType());
61 - return getStat(reqVO, customerMapper::selectRecordCountGroupbyDate); 150 + final List<CrmStatisticsFollowupSummaryByDateRespVO> followupRecordCount = customerMapper.selectFollowupRecordCountGroupbyDate(reqVO);
  151 + final List<CrmStatisticsFollowupSummaryByDateRespVO> followupCustomerCount = customerMapper.selectFollowupCustomerCountGroupbyDate(reqVO);
  152 +
  153 + // 3. 获取时间序列
  154 + final List<String> times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]);
  155 +
  156 + // 4. 合并统计数据
  157 + List<CrmStatisticsFollowupSummaryByDateRespVO> respVoList = new ArrayList<>(times.size());
  158 + final Map<String, Integer> followupRecordCountMap = convertMap(followupRecordCount,
  159 + CrmStatisticsFollowupSummaryByDateRespVO::getTime,
  160 + CrmStatisticsFollowupSummaryByDateRespVO::getFollowupRecordCount);
  161 + final Map<String, Integer> followupCustomerCountMap = convertMap(followupCustomerCount,
  162 + CrmStatisticsFollowupSummaryByDateRespVO::getTime,
  163 + CrmStatisticsFollowupSummaryByDateRespVO::getFollowupCustomerCount);
  164 + times.forEach(time -> respVoList.add(
  165 + new CrmStatisticsFollowupSummaryByDateRespVO().setTime(time)
  166 + .setFollowupRecordCount(followupRecordCountMap.getOrDefault(time, 0))
  167 + .setFollowupCustomerCount(followupCustomerCountMap.getOrDefault(time, 0))
  168 + ));
  169 +
  170 + return respVoList;
62 } 171 }
63 172
64 @Override 173 @Override
65 - public List<CrmStatisticsCustomerCountVO> getDistinctRecordCount(CrmStatisticsCustomerReqVO reqVO) { 174 + public List<CrmStatisticsFollowupSummaryByUserRespVO> getFollowupSummaryByUser(CrmStatisticsCustomerReqVO reqVO) {
  175 + // 1. 获得用户编号数组
  176 + final List<Long> userIds = getUserIds(reqVO);
  177 + if (CollUtil.isEmpty(userIds)) {
  178 + return Collections.emptyList();
  179 + }
  180 + reqVO.setUserIds(userIds);
  181 +
  182 + // 2. 获取分项统计数据
66 reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); 183 reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType());
67 - return getStat(reqVO, customerMapper::selectDistinctRecordCountGroupbyDate); 184 + final List<CrmStatisticsFollowupSummaryByUserRespVO> followupRecordCount = customerMapper.selectFollowupRecordCountGroupbyUser(reqVO);
  185 + final List<CrmStatisticsFollowupSummaryByUserRespVO> followupCustomerCount = customerMapper.selectFollowupCustomerCountGroupbyUser(reqVO);
  186 +
  187 + // 3. 合并统计数据
  188 + final Map<Long, Integer> followupRecordCountMap = convertMap(followupRecordCount,
  189 + CrmStatisticsFollowupSummaryByUserRespVO::getOwnerUserId,
  190 + CrmStatisticsFollowupSummaryByUserRespVO::getFollowupRecordCount);
  191 + final Map<Long, Integer> followupCustomerCountMap = convertMap(followupCustomerCount,
  192 + CrmStatisticsFollowupSummaryByUserRespVO::getOwnerUserId,
  193 + CrmStatisticsFollowupSummaryByUserRespVO::getFollowupCustomerCount);
  194 + List<CrmStatisticsFollowupSummaryByUserRespVO> respVoList = new ArrayList<>(userIds.size());
  195 + userIds.forEach(userId -> {
  196 + final CrmStatisticsFollowupSummaryByUserRespVO vo = new CrmStatisticsFollowupSummaryByUserRespVO()
  197 + .setFollowupRecordCount(followupRecordCountMap.getOrDefault(userId, 0))
  198 + .setFollowupCustomerCount(followupCustomerCountMap.getOrDefault(userId, 0));
  199 + vo.setOwnerUserId(userId);
  200 + respVoList.add(vo);
  201 + });
  202 +
  203 + // 4. 拼接用户信息
  204 + appendUserInfo(respVoList);
  205 +
  206 + return respVoList;
68 } 207 }
69 208
70 @Override 209 @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())) { 210 + public List<CrmStatisticsFollowupSummaryByTypeRespVO> getFollowupSummaryByType(CrmStatisticsCustomerReqVO reqVO) {
  211 + // 1. 获得用户编号数组
  212 + final List<Long> userIds = getUserIds(reqVO);
  213 + if (CollUtil.isEmpty(userIds)) {
79 return Collections.emptyList(); 214 return Collections.emptyList();
80 } 215 }
  216 + reqVO.setUserIds(userIds);
81 217
82 // 2. 获得排行数据 218 // 2. 获得排行数据
83 reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); 219 reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType());
84 - List<CrmStatisticsCustomerCountVO> stats = customerMapper.selectRecordCountGroupbyType(reqVO); 220 + List<CrmStatisticsFollowupSummaryByTypeRespVO> respVoList = customerMapper.selectFollowupRecordCountGroupbyType(reqVO);
85 221
86 // 3. 获取字典数据 222 // 3. 获取字典数据
87 - List<DictDataRespDTO> followUpTypes = dictDataApi.getDictDataList("crm_follow_up_type");  
88 - final Map<String, String> followUpTypeMap = convertMap(followUpTypes, DictDataRespDTO::getValue, DictDataRespDTO::getLabel);  
89 - stats.forEach(stat -> {  
90 - stat.setCategory(followUpTypeMap.get(stat.getCategory())); 223 + List<DictDataRespDTO> followUpTypes = dictDataApi.getDictDataList(CRM_FOLLOW_UP_TYPE);
  224 + final Map<String, String> followUpTypeMap = convertMap(followUpTypes,
  225 + DictDataRespDTO::getValue, DictDataRespDTO::getLabel);
  226 + respVoList.forEach(vo -> {
  227 + vo.setFollowupType(followUpTypeMap.get(vo.getFollowupType()));
  228 + });
  229 +
  230 + return respVoList;
  231 + }
  232 +
  233 + @Override
  234 + public List<CrmStatisticsCustomerContractSummaryRespVO> getContractSummary(CrmStatisticsCustomerReqVO reqVO) {
  235 + // 1. 获得用户编号数组
  236 + final List<Long> userIds = getUserIds(reqVO);
  237 + if (CollUtil.isEmpty(userIds)) {
  238 + return Collections.emptyList();
  239 + }
  240 + reqVO.setUserIds(userIds);
  241 +
  242 + // 2. 获取统计数据
  243 + List<CrmStatisticsCustomerContractSummaryRespVO> respVoList = customerMapper.selectContractSummary(reqVO);
  244 +
  245 + // 3. 设置 创建人、负责人、行业、来源
  246 + // 获取客户所属行业
  247 + Map<String, String> industryMap = convertMap(dictDataApi.getDictDataList(CRM_CUSTOMER_INDUSTRY),
  248 + DictDataRespDTO::getValue, DictDataRespDTO::getLabel);
  249 + // 获取客户来源
  250 + Map<String, String> sourceMap = convertMap(dictDataApi.getDictDataList(CRM_CUSTOMER_SOURCE),
  251 + DictDataRespDTO::getValue, DictDataRespDTO::getLabel);
  252 + // 获取创建人、负责人列表
  253 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSetByFlatMap(respVoList,
  254 + vo -> Stream.of(NumberUtils.parseLong(vo.getCreatorUserId()), vo.getOwnerUserId())));
  255 +
  256 + respVoList.forEach(vo -> {
  257 + MapUtils.findAndThen(industryMap, vo.getIndustryId(), vo::setIndustryName);
  258 + MapUtils.findAndThen(sourceMap, vo.getSource(), vo::setSourceName);
  259 + MapUtils.findAndThen(userMap, NumberUtils.parseLong(vo.getCreatorUserId()),
  260 + user -> vo.setCreatorUserName(user.getNickname()));
  261 + MapUtils.findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname()));
91 }); 262 });
92 263
93 - return stats; 264 + return respVoList;
  265 + }
  266 +
  267 + @Override
  268 + public List<CrmStatisticsCustomerDealCycleByDateRespVO> getCustomerDealCycleByDate(CrmStatisticsCustomerReqVO reqVO) {
  269 + // 1. 获得用户编号数组
  270 + final List<Long> userIds = getUserIds(reqVO);
  271 + if (CollUtil.isEmpty(userIds)) {
  272 + return Collections.emptyList();
  273 + }
  274 + reqVO.setUserIds(userIds);
  275 +
  276 + // 2. 获取分项统计数据
  277 + reqVO.setSqlDateFormat(getSqlDateFormat(reqVO.getTimes()[0], reqVO.getTimes()[1]));
  278 + reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType());
  279 + final List<CrmStatisticsCustomerDealCycleByDateRespVO> customerDealCycle = customerMapper.selectCustomerDealCycleGroupbyDate(reqVO);
  280 +
  281 + // 3. 获取时间序列
  282 + final List<String> times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]);
  283 +
  284 + // 4. 合并统计数据
  285 + List<CrmStatisticsCustomerDealCycleByDateRespVO> respVoList = new ArrayList<>(times.size());
  286 + final Map<String, Double> customerDealCycleMap = convertMap(customerDealCycle,
  287 + CrmStatisticsCustomerDealCycleByDateRespVO::getTime,
  288 + CrmStatisticsCustomerDealCycleByDateRespVO::getCustomerDealCycle);
  289 + times.forEach(time -> respVoList.add(
  290 + new CrmStatisticsCustomerDealCycleByDateRespVO().setTime(time)
  291 + .setCustomerDealCycle(customerDealCycleMap.getOrDefault(time, 0D))
  292 + ));
  293 +
  294 + return respVoList;
94 } 295 }
95 296
96 @Override 297 @Override
97 - public List<CrmStatisticsCustomerCountVO> getCustomerCycle(CrmStatisticsCustomerReqVO reqVO) {  
98 - return getStat(reqVO, customerMapper::selectCustomerCycleGroupbyDate); 298 + public List<CrmStatisticsCustomerDealCycleByUserRespVO> getCustomerDealCycleByUser(CrmStatisticsCustomerReqVO reqVO) {
  299 + // 1. 获得用户编号数组
  300 + final List<Long> userIds = getUserIds(reqVO);
  301 + if (CollUtil.isEmpty(userIds)) {
  302 + return Collections.emptyList();
  303 + }
  304 + reqVO.setUserIds(userIds);
  305 +
  306 + // 2. 获取分项统计数据
  307 + reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType());
  308 + final List<CrmStatisticsCustomerDealCycleByUserRespVO> customerDealCycle = customerMapper.selectCustomerDealCycleGroupbyUser(reqVO);
  309 + final List<CrmStatisticsCustomerSummaryByUserRespVO> customerDealCount = customerMapper.selectCustomerDealCountGroupbyUser(reqVO);
  310 +
  311 + // 3. 合并统计数据
  312 + final Map<Long, Double> customerDealCycleMap = convertMap(customerDealCycle,
  313 + CrmStatisticsCustomerDealCycleByUserRespVO::getOwnerUserId,
  314 + CrmStatisticsCustomerDealCycleByUserRespVO::getCustomerDealCycle);
  315 + final Map<Long, Integer> customerDealCountMap = convertMap(customerDealCount,
  316 + CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId,
  317 + CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount);
  318 + List<CrmStatisticsCustomerDealCycleByUserRespVO> respVoList = new ArrayList<>(userIds.size());
  319 + userIds.forEach(userId -> {
  320 + final CrmStatisticsCustomerDealCycleByUserRespVO vo = new CrmStatisticsCustomerDealCycleByUserRespVO()
  321 + .setCustomerDealCycle(customerDealCycleMap.getOrDefault(userId, 0.0))
  322 + .setCustomerDealCount(customerDealCountMap.getOrDefault(userId, 0));
  323 + vo.setOwnerUserId(userId);
  324 + respVoList.add(vo);
  325 + });
  326 +
  327 + // 4. 拼接用户信息
  328 + appendUserInfo(respVoList);
  329 +
  330 + return respVoList;
  331 + }
  332 +
  333 + /**
  334 + * 拼接用户信息(昵称)
  335 + *
  336 + * @param respVoList 统计数据
  337 + */
  338 + private <T extends CrmStatisticsCustomerByUserBaseRespVO> void appendUserInfo(List<T> respVoList) {
  339 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(respVoList,
  340 + CrmStatisticsCustomerByUserBaseRespVO::getOwnerUserId));
  341 + respVoList.forEach(vo -> MapUtils.findAndThen(userMap,
  342 + vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname())));
99 } 343 }
100 344
101 /** 345 /**
102 - * 获得统计数据 346 + * 获取用户编号数组。如果用户编号为空, 则获得部门下的用户编号数组,包括子部门的所有用户编号
103 * 347 *
104 - * @param reqVO 参数  
105 - * @param statFunction 统计方法  
106 - * @return 统计数据 348 + * @param reqVO 请求参数
  349 + * @return 用户编号数组
107 */ 350 */
108 - private List<CrmStatisticsCustomerCountVO> getStat(CrmStatisticsCustomerReqVO reqVO, Function<CrmStatisticsCustomerReqVO, List<CrmStatisticsCustomerCountVO>> statFunction) {  
109 - // 1. 获得用户编号数组: 如果用户编号为空, 则获得部门下的用户编号数组 351 + private List<Long> getUserIds(CrmStatisticsCustomerReqVO reqVO) {
110 if (ObjUtil.isNotNull(reqVO.getUserId())) { 352 if (ObjUtil.isNotNull(reqVO.getUserId())) {
111 - reqVO.setUserIds(List.of(reqVO.getUserId())); 353 + return List.of(reqVO.getUserId());
112 } else { 354 } else {
113 - reqVO.setUserIds(getUserIds(reqVO.getDeptId()));  
114 - }  
115 - if (CollUtil.isEmpty(reqVO.getUserIds())) {  
116 - return Collections.emptyList(); 355 + // 1. 获得部门列表
  356 + final Long deptId = reqVO.getDeptId();
  357 + List<Long> deptIds = convertList(deptApi.getChildDeptList(deptId), DeptRespDTO::getId);
  358 + deptIds.add(deptId);
  359 + // 2. 获得用户编号
  360 + return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId);
117 } 361 }
  362 + }
118 363
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 364
132 - // 3. 获得排行数据  
133 - List<CrmStatisticsCustomerCountVO> stats = statFunction.apply(reqVO); 365 + /**
  366 + * 判断是否按照 月粒度 统计
  367 + *
  368 + * @param startTime 开始时间
  369 + * @param endTime 结束时间
  370 + * @return 是, 按月粒度, 否则按天粒度统计。
  371 + */
  372 + private boolean queryByMonth(LocalDateTime startTime, LocalDateTime endTime) {
  373 + return LocalDateTimeUtil.between(startTime, endTime).toDays() > 31;
  374 + }
134 375
135 - // 4. 生成时间序列  
136 - List<CrmStatisticsCustomerCountVO> result = CollUtil.newArrayList(); 376 + /**
  377 + * 生成时间序列
  378 + *
  379 + * @param startTime 开始时间
  380 + * @param endTime 结束时间
  381 + * @return 时间序列
  382 + */
  383 + private List<String> generateTimeSeries(LocalDateTime startTime, LocalDateTime endTime) {
  384 + boolean byMonth = queryByMonth(startTime, endTime);
  385 + List<String> times = CollUtil.newArrayList();
137 while (!startTime.isAfter(endTime)) { 386 while (!startTime.isAfter(endTime)) {
138 - final String category = LocalDateTimeUtil.format(startTime, byMonth ? "yyyyMM" : "yyyyMMdd");  
139 - result.add(new CrmStatisticsCustomerCountVO().setCategory(category)); 387 + times.add(LocalDateTimeUtil.format(startTime, byMonth ? TIME_FORMAT_BY_MONTH : TIME_FORMAT_BY_DAY));
140 if (byMonth) 388 if (byMonth)
141 startTime = startTime.plusMonths(1); 389 startTime = startTime.plusMonths(1);
142 else 390 else
143 startTime = startTime.plusDays(1); 391 startTime = startTime.plusDays(1);
144 } 392 }
145 393
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; 394 + return times;
158 } 395 }
159 396
160 -  
161 /** 397 /**
162 - * 获得部门下的用户编号数组,包括子部门的 398 + * 获取 SQL 查询 GROUP BY 的时间格式
163 * 399 *
164 - * @param deptId 部门编号  
165 - * @return 用户编号数组 400 + * @param startTime 开始时间
  401 + * @param endTime 结束时间
  402 + * @return SQL 查询 GROUP BY 的时间格式
166 */ 403 */
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); 404 + private String getSqlDateFormat(LocalDateTime startTime, LocalDateTime endTime) {
  405 + return queryByMonth(startTime, endTime) ? SQL_DATE_FORMAT_BY_MONTH : SQL_DATE_FORMAT_BY_DAY;
173 } 406 }
  407 +
174 } 408 }
yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml
@@ -2,115 +2,240 @@ @@ -2,115 +2,240 @@
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,
  184 + IFNULL( c.price, 0 ) AS receivable_price,
  185 + a.industry_id,
  186 + a.source,
  187 + a.owner_user_id,
  188 + a.creator AS creator_user_id,
  189 + a.create_time,
  190 + b.order_date
  191 + FROM
  192 + crm_customer AS a
  193 + INNER JOIN crm_contract AS b ON a.id = b.customer_id
  194 + LEFT JOIN crm_receivable AS c ON b.id = c.contract_id
  195 + WHERE a.deleted = 0 AND b.deleted = 0 AND c.deleted = 0
  196 + AND b.audit_status = 20
  197 + AND a.owner_user_id IN
  198 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  199 + #{userId}
  200 + </foreach>
  201 + AND b.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
  202 + #{times[1],javaType=java.time.LocalDateTime}
78 </select> 203 </select>
79 204
80 - <select id="selectRecordCountGroupbyType"  
81 - resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO"> 205 + <select id="selectCustomerDealCycleGroupbyDate"
  206 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerDealCycleByDateRespVO">
82 SELECT 207 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 208 + DATE_FORMAT( b.order_date, #{sqlDateFormat} ) AS time,
  209 + IFNULL( TRUNCATE ( AVG( TIMESTAMPDIFF( DAY, a.create_time, b.order_date )), 1 ), 0 ) AS customer_deal_cycle
  210 + FROM crm_customer AS a
  211 + LEFT JOIN crm_contract AS b ON b.customer_id = a.id
  212 + WHERE a.deleted = 0 AND b.deleted = 0
  213 + AND b.audit_status = 20
  214 + AND a.owner_user_id IN
  215 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  216 + #{userId}
  217 + </foreach>
  218 + AND b.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
  219 + #{times[1],javaType=java.time.LocalDateTime}
  220 + GROUP BY time
95 </select> 221 </select>
96 222
97 - <select id="selectCustomerCycleGroupbyDate"  
98 - resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO"> 223 + <select id="selectCustomerDealCycleGroupbyUser"
  224 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerDealCycleByUserRespVO">
99 SELECT 225 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 226 + a.owner_user_id,
  227 + IFNULL( TRUNCATE ( AVG( TIMESTAMPDIFF( DAY, a.create_time, b.order_date )), 1 ), 0 ) AS customer_deal_cycle
  228 + FROM crm_customer AS a
  229 + LEFT JOIN crm_contract AS b ON b.customer_id = a.id
  230 + WHERE a.deleted = 0 AND b.deleted = 0
  231 + AND b.audit_status = 20
  232 + AND a.owner_user_id IN
  233 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  234 + #{userId}
  235 + </foreach>
  236 + AND b.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
  237 + #{times[1],javaType=java.time.LocalDateTime}
  238 + GROUP BY a.owner_user_id
114 </select> 239 </select>
115 240
116 </mapper> 241 </mapper>