From a23ea45632640ec50c5472aa89796b8fcb160531 Mon Sep 17 00:00:00 2001 From: dhb52 Date: Mon, 4 Mar 2024 23:51:25 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20[CRM-=E5=AE=A2=E6=88=B7=E7=BB=9F?= =?UTF-8?q?=E8=AE=A1]=E6=A0=B9=E6=8D=AECode-Review=20=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CrmStatisticsCustomerController.http | 61 ++- .../CrmStatisticsCustomerController.java | 72 ++-- ...CrmStatisticsCustomerByUserBaseRespVO.java | 18 + ...atisticsCustomerContractSummaryRespVO.java | 51 +++ .../CrmStatisticsCustomerCountVO.java | 34 -- ...atisticsCustomerDealCycleByDateRespVO.java | 16 + ...atisticsCustomerDealCycleByUserRespVO.java | 16 + ...StatisticsCustomerSummaryByDateRespVO.java | 19 + ...StatisticsCustomerSummaryByUserRespVO.java | 24 ++ ...StatisticsFollowupSummaryByDateRespVO.java | 19 + ...StatisticsFollowupSummaryByTypeRespVO.java | 17 + ...StatisticsFollowupSummaryByUserRespVO.java | 16 + .../CrmStatisticsCustomerMapper.java | 31 +- .../CrmStatisticsCustomerService.java | 58 ++- .../CrmStatisticsCustomerServiceImpl.java | 375 +++++++++++++----- .../CrmStatisticsCustomerMapper.xml | 325 ++++++++++----- 16 files changed, 847 insertions(+), 305 deletions(-) create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerByUserBaseRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerContractSummaryRespVO.java delete mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerCountVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByDateRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByUserRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByDateRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByUserRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByDateRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByTypeRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByUserRespVO.java diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http index 13ae81228..0c422f986 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http @@ -1,39 +1,68 @@ -### 新建客户总量分析(按日) -GET {{baseUrl}}/crm/statistics-customer/get-total-customer-count?deptId=100×[0]=2024-12-01 00:00:00×[1]=2024-12-12 23:59:59 +# == 1. 客户总量分析 == +### 1.1 客户总量分析(按日) +GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-date?deptId=100×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} -### 新建客户总量分析(按月) -GET {{baseUrl}}/crm/statistics-customer/get-total-customer-count?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 +### 1.2 客户总量分析(按月) +GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-date?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} -### 成交客户总量分析(按日) -GET {{baseUrl}}/crm/statistics-customer/get-deal-total-customer-count?deptId=100×[0]=2024-12-01 00:00:00×[1]=2024-12-12 23:59:59 +### 1.3 客户总量统计(按用户) +GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-user?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} -### 成交客户总量分析(按月) -GET {{baseUrl}}/crm/statistics-customer/get-deal-total-customer-count?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 + +# == 2. 客户跟进次数分析 == +### 2.1 客户跟进次数分析(按日) +GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-date?deptId=100×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} -### 获取客户跟进次数(按日) -GET {{baseUrl}}/crm/statistics-customer/get-record-count?deptId=100×[0]=2024-12-01 00:00:00×[1]=2024-12-12 23:59:59 +### 2.2 客户跟进次数分析(按月) +GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-date?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} -### 获取客户跟进次数(按月) -GET {{baseUrl}}/crm/statistics-customer/get-record-count?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 +### 2.3 客户总量统计(按用户) +GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-user?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} -### 获取已跟进客户数(按日) -GET {{baseUrl}}/crm/statistics-customer/get-distinct-record-count?deptId=100×[0]=2024-12-01 00:00:00×[1]=2024-12-12 23:59:59 + +# == 3. 客户跟进方式分析 == +### 3.1 客户跟进方式分析 +GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-type?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} +tenant-id: {{adminTenentId}} -### 获取已跟进客户数(按月) -GET {{baseUrl}}/crm/statistics-customer/get-distinct-record-count?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 + +# == 4. 客户成交周期 == +### 4.1 合同摘要信息(客户转化率页面) +GET {{baseUrl}}/crm/statistics-customer/get-contract-summary?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} +tenant-id: {{adminTenentId}} + + +# == 5. 客户成交周期 == +### 5.1 客户成交周期(按日) +GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-date?deptId=100×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59 +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} +tenant-id: {{adminTenentId}} + +### 5.2 客户成交周期(按月) +GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-date?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} +tenant-id: {{adminTenentId}} + +### 5.3 获取客户成交周期(按用户) +GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-user?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} tenant-id: {{adminTenentId}} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java index b51740daf..4a0b2e760 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java @@ -1,8 +1,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.statistics; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsCustomerService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -18,8 +17,7 @@ import java.util.List; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -// TODO @dhb52:数据统计 员工客户分析,改成“客户统计” -@Tag(name = "管理后台 - CRM 数据统计 员工客户分析") +@Tag(name = "管理后台 - CRM 客户统计") @RestController @RequestMapping("/crm/statistics-customer") @Validated @@ -28,50 +26,60 @@ public class CrmStatisticsCustomerController { @Resource private CrmStatisticsCustomerService customerService; - // TODO @dhb52:建议 getCustomerCount 和 getDealTotalCustomerCount 搞成一个接口; - // 1. 数量接口:【方法:getCustomerSummaryByDate】,VO:CrmStatisticsCustomerSummaryByDateRespVO,然后里面是 time、customerCreateCount customerDealCount - // 2. 按人统计:【方法:getCustomerSummaryByUser】,VO:CrmStatisticsCustomerSummaryByOwnerRespVO,然后里面是 ownerUserId、ownerUserName、customerCreateCount customerDealCount、contractPrice、receivablePrice;客户成交率、未回款金额、回款完成率,交给前端计算; - - @GetMapping("/get-total-customer-count") - @Operation(summary = "获得新建客户数量") + @GetMapping("/get-customer-summary-by-date") + @Operation(summary = "获取客户总量分析(按日期)") @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") - public CommonResult> getTotalCustomerCount(@Valid CrmStatisticsCustomerReqVO reqVO) { - return success(customerService.getTotalCustomerCount(reqVO)); + public CommonResult> getCustomerSummaryByDate(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getCustomerSummaryByDate(reqVO)); } - @GetMapping("/get-deal-total-customer-count") - @Operation(summary = "获得成交客户数量") + @GetMapping("/get-customer-summary-by-user") + @Operation(summary = "获取客户总量分析(按用户)") @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") - public CommonResult> getDealTotalCustomerCount(@Valid CrmStatisticsCustomerReqVO reqVO) { - return success(customerService.getDealTotalCustomerCount(reqVO)); + public CommonResult> getCustomerSummaryByUser(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getCustomerSummaryByUser(reqVO)); } - @GetMapping("/get-record-count") - @Operation(summary = "获取客户跟进次数") + @GetMapping("/get-followup-summary-by-date") + @Operation(summary = "获取客户跟进次数分析(按日期)") @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") - public CommonResult> getRecordCount(@Valid CrmStatisticsCustomerReqVO reqVO) { - return success(customerService.getRecordCount(reqVO)); + public CommonResult> getFollowupSummaryByDate(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getFollowupSummaryByDate(reqVO)); } - @GetMapping("/get-distinct-record-count") - @Operation(summary = "获取已跟进客户数") + @GetMapping("/get-followup-summary-by-user") + @Operation(summary = "获取客户跟进次数分析(按用户)") @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") - public CommonResult> getDistinctRecordCount(@Valid CrmStatisticsCustomerReqVO reqVO) { - return success(customerService.getDistinctRecordCount(reqVO)); + public CommonResult> getFollowupSummaryByUser(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getFollowupSummaryByUser(reqVO)); } - @GetMapping("/get-record-type-count") - @Operation(summary = "获取客户跟进方式统计数") + @GetMapping("/get-followup-summary-by-type") + @Operation(summary = "获取客户跟进次数分析(按类型)") @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") - public CommonResult> getRecordTypeCount(@Valid CrmStatisticsCustomerReqVO reqVO) { - return success(customerService.getRecordTypeCount(reqVO)); + public CommonResult> getFollowupSummaryByType(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getFollowupSummaryByType(reqVO)); } - @GetMapping("/get-customer-cycle") - @Operation(summary = "获取客户成交周期") + @GetMapping("/get-contract-summary") + @Operation(summary = "获取合同摘要信息(客户转化率页面)") @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") - public CommonResult> getCustomerCycle(@Valid CrmStatisticsCustomerReqVO reqVO) { - return success(customerService.getCustomerCycle(reqVO)); + public CommonResult> getContractSummary(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getContractSummary(reqVO)); + } + + @GetMapping("/get-customer-deal-cycle-by-date") + @Operation(summary = "获取客户成交周期(按日期)") + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") + public CommonResult> getCustomerDealCycleByDate(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getCustomerDealCycleByDate(reqVO)); + } + + @GetMapping("/get-customer-deal-cycle-by-user") + @Operation(summary = "获取客户成交周期(按用户)") + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") + public CommonResult> getCustomerDealCycleByUser(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getCustomerDealCycleByUser(reqVO)); } } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerByUserBaseRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerByUserBaseRespVO.java new file mode 100644 index 000000000..82f74f230 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerByUserBaseRespVO.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 用户客户统计响应 Base VO + */ +@Data +public class CrmStatisticsCustomerByUserBaseRespVO { + + @Schema(description = "负责人ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long ownerUserId; + + @Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") + private String ownerUserName; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerContractSummaryRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerContractSummaryRespVO.java new file mode 100644 index 000000000..7cb02e521 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerContractSummaryRespVO.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - CRM 客户转化率分析 VO") +@Data +public class CrmStatisticsCustomerContractSummaryRespVO { + + @Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") + private String customerName; + + @Schema(description = "合同名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "演示合同") + private String contractName; + + @Schema(description = "合同总金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1200.00") + private BigDecimal totalPrice; + + @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1200.00") + private BigDecimal receivablePrice; + + @Schema(description = "客户行业", requiredMode = Schema.RequiredMode.REQUIRED, example = "金融") + private String customerType; + + @Schema(description = "客户来源", requiredMode = Schema.RequiredMode.REQUIRED, example = "外呼") + private String customerSource; + + @Schema(description = "负责人ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long ownerUserId; + + @Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") + private String ownerUserName; + + @Schema(description = "创建人ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Long creatorUserId; + + @Schema(description = "创建人", requiredMode = Schema.RequiredMode.REQUIRED, example = "源码") + private String creatorUserName; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-01 13:24:26") + private LocalDateTime createTime; + + @Schema(description = "下单日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02 00:00:00") + private LocalDate orderDate; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerCountVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerCountVO.java deleted file mode 100644 index a2537db9a..000000000 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerCountVO.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - - -@Schema(description = "管理后台 - CRM 数据统计 员工客户分析 VO") -@Data -public class CrmStatisticsCustomerCountVO { - - /** - * 时间轴 - *

- * group by DATE_FORMAT(create_date, '%Y%m') - */ - @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401") - private String category; - - /** - * 数量是个特别“抽象”的概念,在不同排行下,代表不同含义 - *

- * 1. 金额:合同金额排行、回款金额排行 - * 2. 个数:签约合同排行、产品销量排行、产品销量排行、新增客户数排行、新增联系人排行、跟进次数排行、跟进客户数排行 - */ - @Schema(description = "数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer count = 0; - - /** - * 成交周期(天) - */ - @Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0") - private Double cycle = 0.0; - -} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByDateRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByDateRespVO.java new file mode 100644 index 000000000..44b2526e8 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByDateRespVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - CRM 客户成交周期分析(按日期) VO") +@Data +public class CrmStatisticsCustomerDealCycleByDateRespVO { + + @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401") + private String time; + + @Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0") + private Double customerDealCycle = 0.0; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByUserRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByUserRespVO.java new file mode 100644 index 000000000..6c8137983 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByUserRespVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - CRM 成交周期分析(按用户) VO") +@Data +public class CrmStatisticsCustomerDealCycleByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO { + + @Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0") + private Double customerDealCycle = 0.0; + + @Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer customerDealCount = 0; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByDateRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByDateRespVO.java new file mode 100644 index 000000000..866a58f1e --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByDateRespVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - CRM 客户总量分析(按日期) VO") +@Data +public class CrmStatisticsCustomerSummaryByDateRespVO { + + @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401") + private String time; + + @Schema(description = "新建客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer customerCreateCount = 0; + + @Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer customerDealCount = 0; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByUserRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByUserRespVO.java new file mode 100644 index 000000000..ffa5e21ae --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByUserRespVO.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - CRM 客户总量分析(按用户) VO") +@Data +public class CrmStatisticsCustomerSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO { + + @Schema(description = "新建客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer customerCreateCount = 0; + + @Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer customerDealCount = 0; + + @Schema(description = "合同总金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal contractPrice = BigDecimal.valueOf(0); + + @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal receivablePrice = BigDecimal.valueOf(0); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByDateRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByDateRespVO.java new file mode 100644 index 000000000..6bd6f1d4d --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByDateRespVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - CRM 跟进次数分析(按日期) VO") +@Data +public class CrmStatisticsFollowupSummaryByDateRespVO { + + @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401") + private String time; + + @Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer followupRecordCount = 0; + + @Schema(description = "跟进客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer followupCustomerCount = 0; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByTypeRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByTypeRespVO.java new file mode 100644 index 000000000..c722305a5 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByTypeRespVO.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - CRM 跟进次数分析(按类型) VO") +@Data +public class CrmStatisticsFollowupSummaryByTypeRespVO { + + @Schema(description = "跟进类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private String followupType; + + @Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer followupRecordCount = 0; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByUserRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByUserRespVO.java new file mode 100644 index 000000000..5cd610c36 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByUserRespVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - CRM 跟进次数分析(按用户) VO") +@Data +public class CrmStatisticsFollowupSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO { + + @Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer followupRecordCount = 0; + + @Schema(description = "跟进客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer followupCustomerCount = 0; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java index 464c521c6..6bababa26 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.module.crm.dal.mysql.statistics; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; import org.apache.ibatis.annotations.Mapper; import java.util.List; @@ -14,16 +13,32 @@ import java.util.List; @Mapper public interface CrmStatisticsCustomerMapper { - List selectCustomerCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); + List selectCustomerCreateCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); - List selectDealCustomerCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); + List selectCustomerDealCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); - List selectRecordCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); + List selectCustomerCreateCountGroupbyUser(CrmStatisticsCustomerReqVO reqVO); - List selectDistinctRecordCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); + List selectCustomerDealCountGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); - List selectRecordCountGroupbyType(CrmStatisticsCustomerReqVO reqVO); + List selectContractPriceGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); - List selectCustomerCycleGroupbyDate(CrmStatisticsCustomerReqVO reqVO); + List selectReceivablePriceGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); + + List selectFollowupRecordCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); + + List selectFollowupCustomerCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); + + List selectFollowupRecordCountGroupbyUser(CrmStatisticsCustomerReqVO reqVO); + + List selectFollowupCustomerCountGroupbyUser(CrmStatisticsCustomerReqVO reqVO); + + List selectContractSummary(CrmStatisticsCustomerReqVO reqVO); + + List selectFollowupRecordCountGroupbyType(CrmStatisticsCustomerReqVO reqVO); + + List selectCustomerDealCycleGroupbyDate(CrmStatisticsCustomerReqVO reqVO); + + List selectCustomerDealCycleGroupbyUser(CrmStatisticsCustomerReqVO reqVO); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java index e568816d6..546124701 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java @@ -1,64 +1,80 @@ package cn.iocoder.yudao.module.crm.service.statistics; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; import java.util.List; /** - * CRM 数据统计 员工客户分析 Service 接口 + * CRM 客户分析 Service 接口 * * @author dhb52 */ public interface CrmStatisticsCustomerService { /** - * 获取新建客户数量 + * 总量分析(按日期) * * @param reqVO 请求参数 - * @return 新建客户数量统计 + * @return 统计数据 */ - List getTotalCustomerCount(CrmStatisticsCustomerReqVO reqVO); + List getCustomerSummaryByDate(CrmStatisticsCustomerReqVO reqVO); /** - * 获取成交客户数量 + * 总量分析(按用户) * * @param reqVO 请求参数 - * @return 成交客户数量统计 + * @return 统计数据 */ - List getDealTotalCustomerCount(CrmStatisticsCustomerReqVO reqVO); + List getCustomerSummaryByUser(CrmStatisticsCustomerReqVO reqVO); /** - * 获取客户跟进次数 + * 跟进次数分析(按日期) * * @param reqVO 请求参数 - * @return 客户跟进次数 + * @return 统计数据 */ - List getRecordCount(CrmStatisticsCustomerReqVO reqVO); + List getFollowupSummaryByDate(CrmStatisticsCustomerReqVO reqVO); /** - * 获取已跟进客户数 + * 跟进次数分析(按用户) * * @param reqVO 请求参数 - * @return 已跟进客户数 + * @return 统计数据 */ - List getDistinctRecordCount(CrmStatisticsCustomerReqVO reqVO); + List getFollowupSummaryByUser(CrmStatisticsCustomerReqVO reqVO); /** - * 获取客户跟进方式统计数 + * 客户跟进次数分析(按类型) * * @param reqVO 请求参数 - * @return 客户跟进方式统计数 + * @return 统计数据 */ - List getRecordTypeCount(CrmStatisticsCustomerReqVO reqVO); + List getFollowupSummaryByType(CrmStatisticsCustomerReqVO reqVO); + /** - * 获取客户成交周期 + * 获取合同摘要信息(客户转化率页面) * * @param reqVO 请求参数 - * @return 客户成交周期 + * @return 合同摘要列表 */ - List getCustomerCycle(CrmStatisticsCustomerReqVO reqVO); + List getContractSummary(CrmStatisticsCustomerReqVO reqVO); + + /** + * 客户成交周期(按日期) + * + * @param reqVO 请求参数 + * @return 统计数据 + */ + List getCustomerDealCycleByDate(CrmStatisticsCustomerReqVO reqVO); + + /** + * 客户成交周期(按用户) + * + * @param reqVO 请求参数 + * @return 统计数据 + */ + List getCustomerDealCycleByUser(CrmStatisticsCustomerReqVO reqVO); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java index 94eb560d1..77bd0fcf6 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java +++ b/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; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.util.ObjUtil; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper; import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; import cn.iocoder.yudao.module.system.api.dept.DeptApi; @@ -17,17 +17,14 @@ import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; +import java.math.BigDecimal; import java.time.LocalDateTime; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.function.Function; +import java.util.*; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; /** - * CRM 数据统计 员工客户分析 Service 实现类 + * CRM 客户分析 Service 实现类 * * @author dhb52 */ @@ -35,6 +32,13 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. @Validated public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerService { + private static final String SQL_DATE_FORMAT_BY_MONTH = "%Y%m"; + private static final String SQL_DATE_FORMAT_BY_DAY = "%Y%m%d"; + + private static final String TIME_FORMAT_BY_MONTH = "yyyyMM"; + private static final String TIME_FORMAT_BY_DAY = "yyyyMMdd"; + + @Resource private CrmStatisticsCustomerMapper customerMapper; @@ -45,130 +49,315 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe @Resource private DictDataApi dictDataApi; - @Override - public List getTotalCustomerCount(CrmStatisticsCustomerReqVO reqVO) { - return getStat(reqVO, customerMapper::selectCustomerCountGroupbyDate); - } @Override - public List getDealTotalCustomerCount(CrmStatisticsCustomerReqVO reqVO) { - return getStat(reqVO, customerMapper::selectDealCustomerCountGroupbyDate); - } - - @Override - public List getRecordCount(CrmStatisticsCustomerReqVO reqVO) { - reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); - return getStat(reqVO, customerMapper::selectRecordCountGroupbyDate); - } - - @Override - public List getDistinctRecordCount(CrmStatisticsCustomerReqVO reqVO) { - reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); - return getStat(reqVO, customerMapper::selectDistinctRecordCountGroupbyDate); - } - - @Override - public List getRecordTypeCount(CrmStatisticsCustomerReqVO reqVO) { - // 1. 获得用户编号数组: 如果用户编号为空, 则获得部门下的用户编号数组 - if (ObjUtil.isNotNull(reqVO.getUserId())) { - reqVO.setUserIds(List.of(reqVO.getUserId())); - } else { - reqVO.setUserIds(getUserIds(reqVO.getDeptId())); - } - if (CollUtil.isEmpty(reqVO.getUserIds())) { + public List getCustomerSummaryByDate(CrmStatisticsCustomerReqVO reqVO) { + // 1. 获得用户编号数组 + final List userIds = getUserIds(reqVO); + if (CollUtil.isEmpty(userIds)) { return Collections.emptyList(); } + reqVO.setUserIds(userIds); + + // 2. 获取分项统计数据 + reqVO.setSqlDateFormat(getSqlDateFormat(reqVO.getTimes()[0], reqVO.getTimes()[1])); + final List customerCreateCount = customerMapper.selectCustomerCreateCountGroupbyDate(reqVO); + final List customerDealCount = customerMapper.selectCustomerDealCountGroupbyDate(reqVO); + + // 3. 获取时间序列 + final List times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]); + + // 4. 合并统计数据 + List result = new ArrayList<>(times.size()); + final Map customerCreateCountMap = convertMap(customerCreateCount, CrmStatisticsCustomerSummaryByDateRespVO::getTime, CrmStatisticsCustomerSummaryByDateRespVO::getCustomerCreateCount); + final Map customerDealCountMap = convertMap(customerDealCount, CrmStatisticsCustomerSummaryByDateRespVO::getTime, CrmStatisticsCustomerSummaryByDateRespVO::getCustomerDealCount); + times.forEach(time -> result.add( + new CrmStatisticsCustomerSummaryByDateRespVO().setTime(time) + .setCustomerCreateCount(customerCreateCountMap.getOrDefault(time, 0)) + .setCustomerDealCount(customerDealCountMap.getOrDefault(time, 0)) + )); + + return result; + } + + @Override + public List getCustomerSummaryByUser(CrmStatisticsCustomerReqVO reqVO) { + // 1. 获得用户编号数组 + final List userIds = getUserIds(reqVO); + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + reqVO.setUserIds(userIds); + + // 2. 获取分项统计数据 + final List customerCreateCount = customerMapper.selectCustomerCreateCountGroupbyUser(reqVO); + final List customerDealCount = customerMapper.selectCustomerDealCountGroupbyUser(reqVO); + final List contractPrice = customerMapper.selectContractPriceGroupbyUser(reqVO); + final List receivablePrice = customerMapper.selectReceivablePriceGroupbyUser(reqVO); + + // 3. 合并统计数据 + final Map customerCreateCountMap = convertMap(customerCreateCount, CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, CrmStatisticsCustomerSummaryByUserRespVO::getCustomerCreateCount); + final Map customerDealCountMap = convertMap(customerDealCount, CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount); + final Map contractPriceMap = convertMap(contractPrice, CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, CrmStatisticsCustomerSummaryByUserRespVO::getContractPrice); + final Map receivablePriceMap = convertMap(receivablePrice, CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, CrmStatisticsCustomerSummaryByUserRespVO::getReceivablePrice); + List result = new ArrayList<>(userIds.size()); + userIds.forEach(userId -> { + final CrmStatisticsCustomerSummaryByUserRespVO respVO = new CrmStatisticsCustomerSummaryByUserRespVO(); + respVO.setOwnerUserId(userId); + respVO.setCustomerCreateCount(customerCreateCountMap.getOrDefault(userId, 0)) + .setCustomerDealCount(customerDealCountMap.getOrDefault(userId, 0)) + .setContractPrice(contractPriceMap.getOrDefault(userId, BigDecimal.valueOf(0))) + .setReceivablePrice(receivablePriceMap.getOrDefault(userId, BigDecimal.valueOf(0))); + result.add(respVO); + }); + + // 4. 拼接用户信息 + appendUserInfo(result); + + return result; + } + + @Override + public List getFollowupSummaryByDate(CrmStatisticsCustomerReqVO reqVO) { + // 1. 获得用户编号数组 + final List userIds = getUserIds(reqVO); + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + reqVO.setUserIds(userIds); + + // 2. 获取分项统计数据 + reqVO.setSqlDateFormat(getSqlDateFormat(reqVO.getTimes()[0], reqVO.getTimes()[1])); + reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); + final List followupRecordCount = customerMapper.selectFollowupRecordCountGroupbyDate(reqVO); + final List followupCustomerCount = customerMapper.selectFollowupCustomerCountGroupbyDate(reqVO); + + // 3. 获取时间序列 + final List times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]); + + // 4. 合并统计数据 + List result = new ArrayList<>(times.size()); + final Map followupRecordCountMap = convertMap(followupRecordCount, CrmStatisticsFollowupSummaryByDateRespVO::getTime, CrmStatisticsFollowupSummaryByDateRespVO::getFollowupRecordCount); + final Map followupCustomerCountMap = convertMap(followupCustomerCount, CrmStatisticsFollowupSummaryByDateRespVO::getTime, CrmStatisticsFollowupSummaryByDateRespVO::getFollowupCustomerCount); + times.forEach(time -> result.add( + new CrmStatisticsFollowupSummaryByDateRespVO().setTime(time) + .setFollowupRecordCount(followupRecordCountMap.getOrDefault(time, 0)) + .setFollowupCustomerCount(followupCustomerCountMap.getOrDefault(time, 0)) + )); + + return result; + } + + @Override + public List getFollowupSummaryByUser(CrmStatisticsCustomerReqVO reqVO) { + // 1. 获得用户编号数组 + final List userIds = getUserIds(reqVO); + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + reqVO.setUserIds(userIds); + + // 2. 获取分项统计数据 + reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); + final List followupRecordCount = customerMapper.selectFollowupRecordCountGroupbyUser(reqVO); + final List followupCustomerCount = customerMapper.selectFollowupCustomerCountGroupbyUser(reqVO); + + // 3. 合并统计数据 + final Map followupRecordCountMap = convertMap(followupRecordCount, CrmStatisticsFollowupSummaryByUserRespVO::getOwnerUserId, CrmStatisticsFollowupSummaryByUserRespVO::getFollowupRecordCount); + final Map followupCustomerCountMap = convertMap(followupCustomerCount, CrmStatisticsFollowupSummaryByUserRespVO::getOwnerUserId, CrmStatisticsFollowupSummaryByUserRespVO::getFollowupCustomerCount); + List result = new ArrayList<>(userIds.size()); + userIds.forEach(userId -> { + final CrmStatisticsFollowupSummaryByUserRespVO stat = new CrmStatisticsFollowupSummaryByUserRespVO() + .setFollowupRecordCount(followupRecordCountMap.getOrDefault(userId, 0)) + .setFollowupCustomerCount(followupCustomerCountMap.getOrDefault(userId, 0)); + stat.setOwnerUserId(userId); + result.add(stat); + }); + + // 4. 拼接用户信息 + appendUserInfo(result); + + return result; + } + + @Override + public List getFollowupSummaryByType(CrmStatisticsCustomerReqVO reqVO) { + // 1. 获得用户编号数组 + final List userIds = getUserIds(reqVO); + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + reqVO.setUserIds(userIds); // 2. 获得排行数据 reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); - List stats = customerMapper.selectRecordCountGroupbyType(reqVO); + List stats = customerMapper.selectFollowupRecordCountGroupbyType(reqVO); // 3. 获取字典数据 List followUpTypes = dictDataApi.getDictDataList("crm_follow_up_type"); final Map followUpTypeMap = convertMap(followUpTypes, DictDataRespDTO::getValue, DictDataRespDTO::getLabel); stats.forEach(stat -> { - stat.setCategory(followUpTypeMap.get(stat.getCategory())); + stat.setFollowupType(followUpTypeMap.get(stat.getFollowupType())); }); return stats; } @Override - public List getCustomerCycle(CrmStatisticsCustomerReqVO reqVO) { - return getStat(reqVO, customerMapper::selectCustomerCycleGroupbyDate); + public List getContractSummary(CrmStatisticsCustomerReqVO reqVO) { + // 1. 获得用户编号数组 + final List userIds = getUserIds(reqVO); + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + reqVO.setUserIds(userIds); + + List contractSummary = customerMapper.selectContractSummary(reqVO); + + // 2. 拼接用户信息 + final Set userIdSet = new HashSet<>(); + userIdSet.addAll(userIds); + userIdSet.addAll(convertSet(contractSummary, CrmStatisticsCustomerContractSummaryRespVO::getCreatorUserId)); + final Map userMap = adminUserApi.getUserMap(userIdSet); + contractSummary.forEach(contract -> contract.setCreatorUserName(userMap.get(contract.getCreatorUserId()).getNickname()) + .setOwnerUserName(userMap.get(contract.getOwnerUserId()).getNickname())); + + return contractSummary; + } + + @Override + public List getCustomerDealCycleByDate(CrmStatisticsCustomerReqVO reqVO) { + // 1. 获得用户编号数组 + final List userIds = getUserIds(reqVO); + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + reqVO.setUserIds(userIds); + + // 2. 获取分项统计数据 + reqVO.setSqlDateFormat(getSqlDateFormat(reqVO.getTimes()[0], reqVO.getTimes()[1])); + reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); + final List customerDealCycle = customerMapper.selectCustomerDealCycleGroupbyDate(reqVO); + + // 3. 获取时间序列 + final List times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]); + + // 4. 合并统计数据 + List result = new ArrayList<>(times.size()); + final Map customerDealCycleMap = convertMap(customerDealCycle, CrmStatisticsCustomerDealCycleByDateRespVO::getTime, CrmStatisticsCustomerDealCycleByDateRespVO::getCustomerDealCycle); + times.forEach(time -> result.add( + new CrmStatisticsCustomerDealCycleByDateRespVO().setTime(time) + .setCustomerDealCycle(customerDealCycleMap.getOrDefault(time, Double.valueOf(0))) + )); + + return result; + } + + @Override + public List getCustomerDealCycleByUser(CrmStatisticsCustomerReqVO reqVO) { + // 1. 获得用户编号数组 + final List userIds = getUserIds(reqVO); + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + reqVO.setUserIds(userIds); + + // 2. 获取分项统计数据 + reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); + final List customerDealCycle = customerMapper.selectCustomerDealCycleGroupbyUser(reqVO); + final List customerDealCount = customerMapper.selectCustomerDealCountGroupbyUser(reqVO); + + // 3. 合并统计数据 + final Map customerDealCycleMap = convertMap(customerDealCycle, CrmStatisticsCustomerDealCycleByUserRespVO::getOwnerUserId, CrmStatisticsCustomerDealCycleByUserRespVO::getCustomerDealCycle); + final Map customerDealCountMap = convertMap(customerDealCount, CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount); + List result = new ArrayList<>(userIds.size()); + userIds.forEach(userId -> { + final CrmStatisticsCustomerDealCycleByUserRespVO stat = new CrmStatisticsCustomerDealCycleByUserRespVO() + .setCustomerDealCycle(customerDealCycleMap.getOrDefault(userId, 0.0)) + .setCustomerDealCount(customerDealCountMap.getOrDefault(userId, 0)); + stat.setOwnerUserId(userId); + result.add(stat); + }); + + // 4. 拼接用户信息 + appendUserInfo(result); + + return result; } /** - * 获得统计数据 + * 拼接用户信息(昵称) * - * @param reqVO 参数 - * @param statFunction 统计方法 - * @return 统计数据 + * @param stats 统计数据 */ - private List getStat(CrmStatisticsCustomerReqVO reqVO, Function> statFunction) { - // 1. 获得用户编号数组: 如果用户编号为空, 则获得部门下的用户编号数组 + private void appendUserInfo(List stats) { + Map userMap = adminUserApi.getUserMap(convertSet(stats, CrmStatisticsCustomerByUserBaseRespVO::getOwnerUserId)); + stats.forEach(stat -> MapUtils.findAndThen(userMap, stat.getOwnerUserId(), user -> { + stat.setOwnerUserName(user.getNickname()); + })); + } + + /** + * 获取用户编号数组。如果用户编号为空, 则获得部门下的用户编号数组,包括子部门的所有用户编号 + * + * @param reqVO 请求参数 + * @return 用户编号数组 + */ + private List getUserIds(CrmStatisticsCustomerReqVO reqVO) { if (ObjUtil.isNotNull(reqVO.getUserId())) { - reqVO.setUserIds(List.of(reqVO.getUserId())); + return List.of(reqVO.getUserId()); } else { - reqVO.setUserIds(getUserIds(reqVO.getDeptId())); - } - if (CollUtil.isEmpty(reqVO.getUserIds())) { - return Collections.emptyList(); + // 1. 获得部门列表 + final Long deptId = reqVO.getDeptId(); + List deptIds = convertList(deptApi.getChildDeptList(deptId), DeptRespDTO::getId); + deptIds.add(deptId); + // 2. 获得用户编号 + return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId); } + } - // 2. 生成日期格式 - LocalDateTime startTime = reqVO.getTimes()[0]; - final LocalDateTime endTime = reqVO.getTimes()[1]; - final long days = LocalDateTimeUtil.between(startTime, endTime).toDays(); - boolean byMonth = days > 31; - if (byMonth) { - // 按月 - reqVO.setSqlDateFormat("%Y%m"); - } else { - // 按日 - reqVO.setSqlDateFormat("%Y%m%d"); - } - // 3. 获得排行数据 - List stats = statFunction.apply(reqVO); + /** + * 判断是否按照 月粒度 统计 + * + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return 是, 按月粒度, 否则按天粒度统计。 + */ + private boolean queryByMonth(LocalDateTime startTime, LocalDateTime endTime) { + return LocalDateTimeUtil.between(startTime, endTime).toDays() > 31; + } - // 4. 生成时间序列 - List result = CollUtil.newArrayList(); + /** + * 生成时间序列 + * + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return 时间序列 + */ + private List generateTimeSeries(LocalDateTime startTime, LocalDateTime endTime) { + boolean byMonth = queryByMonth(startTime, endTime); + List times = CollUtil.newArrayList(); while (!startTime.isAfter(endTime)) { - final String category = LocalDateTimeUtil.format(startTime, byMonth ? "yyyyMM" : "yyyyMMdd"); - result.add(new CrmStatisticsCustomerCountVO().setCategory(category)); + times.add(LocalDateTimeUtil.format(startTime, byMonth ? TIME_FORMAT_BY_MONTH : TIME_FORMAT_BY_DAY)); if (byMonth) startTime = startTime.plusMonths(1); else startTime = startTime.plusDays(1); } - // 5. 使用时间序列填充结果 - final Map statMap = convertMap(stats, - CrmStatisticsCustomerCountVO::getCategory, - Function.identity()); - result.forEach(r -> { - if (statMap.containsKey(r.getCategory())) { - r.setCount(statMap.get(r.getCategory()).getCount()) - .setCycle(statMap.get(r.getCategory()).getCycle()); - } - }); - - return result; + return times; } - /** - * 获得部门下的用户编号数组,包括子部门的 + * 获取 SQL 查询 GROUP BY 的时间格式 * - * @param deptId 部门编号 - * @return 用户编号数组 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return SQL 查询 GROUP BY 的时间格式 */ - public List getUserIds(Long deptId) { - // 1. 获得部门列表 - List deptIds = convertList(deptApi.getChildDeptList(deptId), DeptRespDTO::getId); - deptIds.add(deptId); - // 2. 获得用户编号 - return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId); + private String getSqlDateFormat(LocalDateTime startTime, LocalDateTime endTime) { + return queryByMonth(startTime, endTime) ? SQL_DATE_FORMAT_BY_MONTH : SQL_DATE_FORMAT_BY_DAY; } + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml index 06affccab..c612d4a24 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml @@ -2,115 +2,238 @@ - - SELECT - DATE_FORMAT( create_time, #{sqlDateFormat,javaType=java.lang.String} ) AS category, - count(*) AS count + DATE_FORMAT( create_time, #{sqlDateFormat} ) AS time, + count(*) AS customerCreateCount + FROM crm_customer + WHERE deleted = 0 + AND owner_user_id IN + + #{userId} + + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND + #{times[1],javaType=java.time.LocalDateTime} + GROUP BY time + + + + + + + + + + + + + + + + + + + + + + + + - SELECT - DATE_FORMAT( b.order_date, #{sqlDateFormat,javaType=java.lang.String} ) AS category, - count( DISTINCT a.id ) AS count - FROM - crm_customer AS a - LEFT JOIN crm_contract AS b ON b.customer_id = a.id - WHERE - a.deleted = 0 AND b.deleted = 0 - AND b.audit_status = 20 - AND a.owner_user_id IN - - #{userId} - - AND b.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND - #{times[1],javaType=java.time.LocalDateTime} - GROUP BY category + DATE_FORMAT( b.order_date, #{sqlDateFormat} ) AS time, + IFNULL( TRUNCATE ( AVG( TIMESTAMPDIFF( DAY, a.create_time, b.order_date )), 1 ), 0 ) AS customer_deal_cycle + FROM crm_customer AS a + LEFT JOIN crm_contract AS b ON b.customer_id = a.id + WHERE a.deleted = 0 AND b.deleted = 0 + AND b.audit_status = 20 + AND a.owner_user_id IN + + #{userId} + + AND b.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND + #{times[1],javaType=java.time.LocalDateTime} + GROUP BY time - SELECT - DATE_FORMAT( create_time, #{sqlDateFormat,javaType=java.lang.String} ) AS category, - count(*) AS count - FROM - crm_follow_up_record - WHERE - creator IN - - #{userId} - - AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND - #{times[1],javaType=java.time.LocalDateTime} - AND biz_type = #{bizType,javaType=java.lang.Integer} - GROUP BY category - - - - - - -