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 4 Authorization: Bearer {{token}}
4 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 9 Authorization: Bearer {{token}}
9 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 14 Authorization: Bearer {{token}}
14 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 38 Authorization: Bearer {{token}}
19 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 46 Authorization: Bearer {{token}}
24 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 54 Authorization: Bearer {{token}}
29 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 60 Authorization: Bearer {{token}}
34 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 66 Authorization: Bearer {{token}}
  67 +tenant-id: {{adminTenentId}}
39 68 tenant-id: {{adminTenentId}}
40 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 1 package cn.iocoder.yudao.module.crm.controller.admin.statistics;
2 2  
3 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 5 import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsCustomerService;
7 6 import io.swagger.v3.oas.annotations.Operation;
8 7 import io.swagger.v3.oas.annotations.tags.Tag;
... ... @@ -18,8 +17,7 @@ import java.util.List;
18 17  
19 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 21 @RestController
24 22 @RequestMapping("/crm/statistics-customer")
25 23 @Validated
... ... @@ -28,50 +26,60 @@ public class CrmStatisticsCustomerController {
28 26 @Resource
29 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 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 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 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 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 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 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 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 4 import org.apache.ibatis.annotations.Mapper;
6 5  
7 6 import java.util.List;
... ... @@ -14,16 +13,32 @@ import java.util.List;
14 13 @Mapper
15 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 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 5 import java.util.List;
7 6  
8 7 /**
9   - * CRM 数据统计 员工客户分析 Service 接口
  8 + * CRM 客户分析 Service 接口
10 9 *
11 10 * @author dhb52
12 11 */
13 12 public interface CrmStatisticsCustomerService {
14 13  
15 14 /**
16   - * 获取新建客户数量
  15 + * 总量分析(按日期)
17 16 *
18 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 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 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 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 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 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 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.date.LocalDateTimeUtil;
5 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 8 import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper;
9 9 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
10 10 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
... ... @@ -17,17 +17,14 @@ import jakarta.annotation.Resource;
17 17 import org.springframework.stereotype.Service;
18 18 import org.springframework.validation.annotation.Validated;
19 19  
  20 +import java.math.BigDecimal;
20 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 29 * @author dhb52
33 30 */
... ... @@ -35,6 +32,13 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
35 32 @Validated
36 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 42 @Resource
39 43 private CrmStatisticsCustomerMapper customerMapper;
40 44  
... ... @@ -45,130 +49,315 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
45 49 @Resource
46 50 private DictDataApi dictDataApi;
47 51  
  52 +
48 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 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 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 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 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 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 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 188 return Collections.emptyList();
80 189 }
  190 + reqVO.setUserIds(userIds);
81 191  
82 192 // 2. 获得排行数据
83 193 reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType());
84   - List<CrmStatisticsCustomerCountVO> stats = customerMapper.selectRecordCountGroupbyType(reqVO);
  194 + List<CrmStatisticsFollowupSummaryByTypeRespVO> stats = customerMapper.selectFollowupRecordCountGroupbyType(reqVO);
85 195  
86 196 // 3. 获取字典数据
87 197 List<DictDataRespDTO> followUpTypes = dictDataApi.getDictDataList("crm_follow_up_type");
88 198 final Map<String, String> followUpTypeMap = convertMap(followUpTypes, DictDataRespDTO::getValue, DictDataRespDTO::getLabel);
89 199 stats.forEach(stat -> {
90   - stat.setCategory(followUpTypeMap.get(stat.getCategory()));
  200 + stat.setFollowupType(followUpTypeMap.get(stat.getFollowupType()));
91 201 });
92 202  
93 203 return stats;
94 204 }
95 205  
96 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 307 if (ObjUtil.isNotNull(reqVO.getUserId())) {
111   - reqVO.setUserIds(List.of(reqVO.getUserId()));
  308 + return List.of(reqVO.getUserId());
112 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 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 343 if (byMonth)
141 344 startTime = startTime.plusMonths(1);
142 345 else
143 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 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3 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 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 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 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 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 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 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 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 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 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 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 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 237 </select>
115 238  
116 239 </mapper>
... ...