Parcourir la source

!909 feat: [CRM-客户分析]增加[时间间隔]选项
Merge pull request !909 from dhb52/develop

芋道源码 il y a 1 an
Parent
commit
eab9627253

+ 48 - 0
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DateIntervalEnum.java

@@ -0,0 +1,48 @@
+package cn.iocoder.yudao.framework.common.enums;
+
+import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * 时间间隔类型枚举
+ *
+ * @author dhb52
+ */
+@Getter
+@AllArgsConstructor
+public enum DateIntervalEnum implements IntArrayValuable {
+
+    TODAY(1, "今天"),
+    YESTERDAY(2, "昨天"),
+    THIS_WEEK(3, "本周"),
+    LAST_WEEK(4, "上周"),
+    THIS_MONTH(5, "本月"),
+    LAST_MONTH(6, "上月"),
+    THIS_QUARTER(7, "本季度"),
+    LAST_QUARTER(8, "上季度"),
+    THIS_YEAR(9, "本年"),
+    LAST_YEAR(10, "去年"),
+    CUSTOMER(11, "自定义"),
+    ;
+
+    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(DateIntervalEnum::getType).toArray();
+
+    /**
+     * 类型
+     */
+    private final Integer type;
+
+    /**
+     * 名称
+     */
+    private final String name;
+
+    @Override
+    public int[] array() {
+        return ARRAYS;
+    }
+
+}

+ 3 - 0
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java

@@ -103,4 +103,7 @@ public interface ErrorCodeConstants {
     ErrorCode FOLLOW_UP_RECORD_NOT_EXISTS = new ErrorCode(1_020_013_000, "跟进记录不存在");
     ErrorCode FOLLOW_UP_RECORD_DELETE_DENIED = new ErrorCode(1_020_013_001, "删除跟进记录失败,原因:没有权限");
 
+    // ========== 数据统计 1_020_014_000 ==========
+    ErrorCode STATISTICS_CUSTOMER_TIMES_NOT_SET = new ErrorCode(1_020_014_000, "自定义时间间隔,必须输入时间区间");
+
 }

+ 11 - 11
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http

@@ -1,40 +1,40 @@
 # == 1. 客户总量分析 ==
 ### 1.1 客户总量分析(按日)
-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
+GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-date?deptId=100&intervalType=11&times[0]=2024-01-01 00:00:00&times[1]=2024-01-29 23:59:59
 Authorization: Bearer {{token}}
 tenant-id: {{adminTenentId}}
 
 ### 1.2 客户总量分析(按月)
-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
+GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-date?deptId=100&intervalType=11&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59
 Authorization: Bearer {{token}}
 tenant-id: {{adminTenentId}}
 
 ### 1.3 客户总量统计(按用户)
-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
+GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-user?deptId=100&intervalType=11&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59
 Authorization: Bearer {{token}}
 tenant-id: {{adminTenentId}}
 
 
 # == 2. 客户跟进次数分析 ==
 ### 2.1 客户跟进次数分析(按日)
-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
+GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-date?deptId=100&intervalType=11&times[0]=2024-01-01 00:00:00&times[1]=2024-01-29 23:59:59
 Authorization: Bearer {{token}}
 tenant-id: {{adminTenentId}}
 
 ### 2.2 客户跟进次数分析(按月)
-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
+GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-date?deptId=100&intervalType=11&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59
 Authorization: Bearer {{token}}
 tenant-id: {{adminTenentId}}
 
 ### 2.3 客户总量统计(按用户)
-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
+GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-user?deptId=100&intervalType=11&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59
 Authorization: Bearer {{token}}
 tenant-id: {{adminTenentId}}
 
 
 # == 3. 客户跟进方式分析 ==
 ### 3.1 客户跟进方式分析
-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
+GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-type?deptId=100&intervalType=11&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59
 Authorization: Bearer {{token}}
 tenant-id: {{adminTenentId}}
 tenant-id: {{adminTenentId}}
@@ -42,7 +42,7 @@ tenant-id: {{adminTenentId}}
 
 # == 4. 客户成交周期 ==
 ### 4.1 合同摘要信息(客户转化率页面)
-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
+GET {{baseUrl}}/crm/statistics-customer/get-contract-summary?deptId=100&intervalType=11&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59
 Authorization: Bearer {{token}}
 tenant-id: {{adminTenentId}}
 tenant-id: {{adminTenentId}}
@@ -50,19 +50,19 @@ tenant-id: {{adminTenentId}}
 
 # == 5. 客户成交周期 ==
 ### 5.1 客户成交周期(按日)
-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
+GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-date?deptId=100&intervalType=11&times[0]=2024-01-01 00:00:00&times[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&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59
+GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-date?deptId=100&intervalType=11&times[0]=2023-01-01 00:00:00&times[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&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59
+GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-user?deptId=100&intervalType=11&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59
 Authorization: Bearer {{token}}
 tenant-id: {{adminTenentId}}
 tenant-id: {{adminTenentId}}

+ 12 - 15
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java

@@ -1,8 +1,8 @@
 package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;
 
-import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
+import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum;
+import cn.iocoder.yudao.framework.common.validation.InEnum;
 import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotEmpty;
 import jakarta.validation.constraints.NotNull;
 import lombok.Data;
 import org.springframework.format.annotation.DateTimeFormat;
@@ -28,31 +28,28 @@ public class CrmStatisticsCustomerReqVO {
 
     /**
      * userIds 目前不用前端传递,目前是方便后端通过 deptId 读取编号后,设置回来
-     * <p>
      * 后续,可能会支持选择部分用户进行查询
      */
     @Schema(description = "负责人用户 id 集合", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2")
     private List<Long> userIds;
 
-    @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.REQUIRED)
+    @Schema(description = "时间间隔类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @InEnum(value = DateIntervalEnum.class, message = "时间间隔类型,必须是 {value}")
+    private Integer intervalType;
+
+    /**
+     * 前端如果选择自定义时间, 那么前端传递起始-终止时间, 如果选择其他时间间隔类型, 则由后台计算起始-终止时间
+     * 并作为参数传递给Mapper
+     */
+    @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
     @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
-    @NotEmpty(message = "时间范围不能为空")
     private LocalDateTime[] times;
 
-    // TODO @dhb52:这个时间间隔,建议前端传递;例如说:字段叫 interval,枚举有天、周、月、季度、年。因为一般分析类的系统,都是交给用户选择筛选时间间隔,而我们这里是默认根据日期选项,默认对应的 interval 而已
-    // 然后实现上,可以在 common 包的 enums 加个 DateIntervalEnum,里面一个是 interval 字段,枚举过去,然后有个 pattern 字段,用于格式化时间格式;
-    // 这样的话,可以通过 interval 获取到 pattern,然后前端就可以根据 pattern 格式化时间,计算还是交给数据库
     /**
      * group by DATE_FORMAT(field, #{dateFormat})
+     * 非前端传递, 由Service计算后传递给Mapper的参数
      */
     @Schema(description = "Group By 日期格式", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "%Y%m")
     private String sqlDateFormat;
 
-    // TODO @dhb52:这个字段,目前是不是没啥用呀?
-    /**
-     * 数据类型 {@link CrmBizTypeEnum}
-     */
-    @Schema(description = "数据类型", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2")
-    private Integer bizType;
-
 }

+ 13 - 14
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java

@@ -13,33 +13,32 @@ import java.util.List;
 @Mapper
 public interface CrmStatisticsCustomerMapper {
 
-    // TODO @dhb52:拼写,GroupBy。一般 idea 如果出现绿色的警告,可能是单词拼写错误,建议是要修改的哈;
-    List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerCreateCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); // 已经 review
+    List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerCreateCountGroupByDate(CrmStatisticsCustomerReqVO reqVO); // 已经 review
 
-    List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerDealCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); // 已经 review
+    List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerDealCountGroupByDate(CrmStatisticsCustomerReqVO reqVO); // 已经 review
 
-    List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerCreateCountGroupbyUser(CrmStatisticsCustomerReqVO reqVO); // 已经 review
+    List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerCreateCountGroupByUser(CrmStatisticsCustomerReqVO reqVO); // 已经 review
 
-    List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerDealCountGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); // 已经 review
+    List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerDealCountGroupByUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); // 已经 review
 
-    List<CrmStatisticsCustomerSummaryByUserRespVO> selectContractPriceGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); // 已经 review
+    List<CrmStatisticsCustomerSummaryByUserRespVO> selectContractPriceGroupByUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); // 已经 review
 
-    List<CrmStatisticsCustomerSummaryByUserRespVO> selectReceivablePriceGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO);  // 已经 review
+    List<CrmStatisticsCustomerSummaryByUserRespVO> selectReceivablePriceGroupByUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO);  // 已经 review
 
-    List<CrmStatisticsFollowupSummaryByDateRespVO> selectFollowupRecordCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO);
+    List<CrmStatisticsFollowupSummaryByDateRespVO> selectFollowupRecordCountGroupByDate(CrmStatisticsCustomerReqVO reqVO);
 
-    List<CrmStatisticsFollowupSummaryByDateRespVO> selectFollowupCustomerCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO);
+    List<CrmStatisticsFollowupSummaryByDateRespVO> selectFollowupCustomerCountGroupByDate(CrmStatisticsCustomerReqVO reqVO);
 
-    List<CrmStatisticsFollowupSummaryByUserRespVO> selectFollowupRecordCountGroupbyUser(CrmStatisticsCustomerReqVO reqVO);
+    List<CrmStatisticsFollowupSummaryByUserRespVO> selectFollowupRecordCountGroupByUser(CrmStatisticsCustomerReqVO reqVO);
 
-    List<CrmStatisticsFollowupSummaryByUserRespVO> selectFollowupCustomerCountGroupbyUser(CrmStatisticsCustomerReqVO reqVO);
+    List<CrmStatisticsFollowupSummaryByUserRespVO> selectFollowupCustomerCountGroupByUser(CrmStatisticsCustomerReqVO reqVO);
 
     List<CrmStatisticsCustomerContractSummaryRespVO> selectContractSummary(CrmStatisticsCustomerReqVO reqVO);
 
-    List<CrmStatisticsFollowupSummaryByTypeRespVO> selectFollowupRecordCountGroupbyType(CrmStatisticsCustomerReqVO reqVO);
+    List<CrmStatisticsFollowupSummaryByTypeRespVO> selectFollowupRecordCountGroupByType(CrmStatisticsCustomerReqVO reqVO);
 
-    List<CrmStatisticsCustomerDealCycleByDateRespVO> selectCustomerDealCycleGroupbyDate(CrmStatisticsCustomerReqVO reqVO);
+    List<CrmStatisticsCustomerDealCycleByDateRespVO> selectCustomerDealCycleGroupByDate(CrmStatisticsCustomerReqVO reqVO);
 
-    List<CrmStatisticsCustomerDealCycleByUserRespVO> selectCustomerDealCycleGroupbyUser(CrmStatisticsCustomerReqVO reqVO);
+    List<CrmStatisticsCustomerDealCycleByUserRespVO> selectCustomerDealCycleGroupByUser(CrmStatisticsCustomerReqVO reqVO);
 
 }

+ 145 - 93
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java

@@ -1,13 +1,15 @@
 package cn.iocoder.yudao.module.crm.service.statistics;
 
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.date.LocalDateTimeUtil;
 import cn.hutool.core.util.ObjUtil;
+import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum;
 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
 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;
 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
 import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
@@ -20,14 +22,15 @@ import org.springframework.validation.annotation.Validated;
 
 import java.math.BigDecimal;
 import java.time.LocalDateTime;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Stream;
 
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
 import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*;
+import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.STATISTICS_CUSTOMER_TIMES_NOT_SET;
 
 /**
  * CRM 客户分析 Service 实现类
@@ -58,114 +61,107 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
     @Override
     public List<CrmStatisticsCustomerSummaryByDateRespVO> getCustomerSummaryByDate(CrmStatisticsCustomerReqVO reqVO) {
         // 1. 获得用户编号数组
-        final List<Long> userIds = getUserIds(reqVO);
+        List<Long> userIds = getUserIds(reqVO);
         if (CollUtil.isEmpty(userIds)) {
             return Collections.emptyList();
         }
         reqVO.setUserIds(userIds);
 
         // 2. 获取分项统计数据
-        // TODO @dhb52:如果是 list 变量,要么 List 要么 s 后缀
-        reqVO.setSqlDateFormat(getSqlDateFormat(reqVO.getTimes()[0], reqVO.getTimes()[1]));
-        final List<CrmStatisticsCustomerSummaryByDateRespVO> customerCreateCount = customerMapper.selectCustomerCreateCountGroupbyDate(reqVO);
-        final List<CrmStatisticsCustomerSummaryByDateRespVO> customerDealCount = customerMapper.selectCustomerDealCountGroupbyDate(reqVO);
-
-        // 3. 获取时间序列
-        // TODO @dhb52:3 和 4 其实做的是一类事情,所以可以考虑 3.1 获取时间序列、3.2 合并统计数据 这样注释;然后中间就不空行了;就是说,一般空行的目的,是让逻辑分片,看着整体性更好,但是不能让逻辑感觉碎碎的;
-        final List<String> times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]);
-
-        // 4. 合并统计数据
-        // TODO @dhb52:这个是不是要 add 到 respVoList 里?或者还可以 convertList(times, time -> new CrmStatisticsCustomerDealCycleByDateRespVO()...)
-        List<CrmStatisticsCustomerSummaryByDateRespVO> respVoList = new ArrayList<>(times.size());
-        final Map<String, Integer> customerCreateCountMap = convertMap(customerCreateCount,
+        initParams(reqVO);
+        List<CrmStatisticsCustomerSummaryByDateRespVO> customerCreateCountVoList = customerMapper.selectCustomerCreateCountGroupByDate(reqVO);
+        List<CrmStatisticsCustomerSummaryByDateRespVO> customerDealCountVoList = customerMapper.selectCustomerDealCountGroupByDate(reqVO);
+
+        // 3. 合并数据
+        List<String> times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]);
+        Map<String, Integer> customerCreateCountMap = convertMap(customerCreateCountVoList,
             CrmStatisticsCustomerSummaryByDateRespVO::getTime,
             CrmStatisticsCustomerSummaryByDateRespVO::getCustomerCreateCount);
-        final Map<String, Integer> customerDealCountMap = convertMap(customerDealCount,
+        Map<String, Integer> customerDealCountMap = convertMap(customerDealCountVoList,
             CrmStatisticsCustomerSummaryByDateRespVO::getTime,
             CrmStatisticsCustomerSummaryByDateRespVO::getCustomerDealCount);
-        times.forEach(time -> respVoList.add(
-            new CrmStatisticsCustomerSummaryByDateRespVO().setTime(time)
+        List<CrmStatisticsCustomerSummaryByDateRespVO> respVoList = convertList(times,
+            time -> new CrmStatisticsCustomerSummaryByDateRespVO()
+                .setTime(time)
                 .setCustomerCreateCount(customerCreateCountMap.getOrDefault(time, 0))
-                .setCustomerDealCount(customerDealCountMap.getOrDefault(time, 0))
-        ));
+                .setCustomerDealCount(customerDealCountMap.getOrDefault(time, 0)));
+
         return respVoList;
     }
 
     @Override
     public List<CrmStatisticsCustomerSummaryByUserRespVO> getCustomerSummaryByUser(CrmStatisticsCustomerReqVO reqVO) {
         // 1. 获得用户编号数组
-        final List<Long> userIds = getUserIds(reqVO);
+        List<Long> userIds = getUserIds(reqVO);
         if (CollUtil.isEmpty(userIds)) {
             return Collections.emptyList();
         }
         reqVO.setUserIds(userIds);
 
         // 2. 获取分项统计数据
-        final List<CrmStatisticsCustomerSummaryByUserRespVO> customerCreateCount = customerMapper.selectCustomerCreateCountGroupbyUser(reqVO);
-        final List<CrmStatisticsCustomerSummaryByUserRespVO> customerDealCount = customerMapper.selectCustomerDealCountGroupbyUser(reqVO);
-        final List<CrmStatisticsCustomerSummaryByUserRespVO> contractPrice = customerMapper.selectContractPriceGroupbyUser(reqVO);
-        final List<CrmStatisticsCustomerSummaryByUserRespVO> receivablePrice = customerMapper.selectReceivablePriceGroupbyUser(reqVO);
+        initParams(reqVO);
+        List<CrmStatisticsCustomerSummaryByUserRespVO> customerCreateCount = customerMapper.selectCustomerCreateCountGroupByUser(reqVO);
+        List<CrmStatisticsCustomerSummaryByUserRespVO> customerDealCount = customerMapper.selectCustomerDealCountGroupByUser(reqVO);
+        List<CrmStatisticsCustomerSummaryByUserRespVO> contractPrice = customerMapper.selectContractPriceGroupByUser(reqVO);
+        List<CrmStatisticsCustomerSummaryByUserRespVO> receivablePrice = customerMapper.selectReceivablePriceGroupByUser(reqVO);
 
         // 3. 合并统计数据
-        final Map<Long, Integer> customerCreateCountMap = convertMap(customerCreateCount,
+        Map<Long, Integer> customerCreateCountMap = convertMap(customerCreateCount,
             CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId,
             CrmStatisticsCustomerSummaryByUserRespVO::getCustomerCreateCount);
-        final Map<Long, Integer> customerDealCountMap = convertMap(customerDealCount,
+        Map<Long, Integer> customerDealCountMap = convertMap(customerDealCount,
             CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId,
             CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount);
-        final Map<Long, BigDecimal> contractPriceMap = convertMap(contractPrice,
+        Map<Long, BigDecimal> contractPriceMap = convertMap(contractPrice,
             CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId,
             CrmStatisticsCustomerSummaryByUserRespVO::getContractPrice);
-        final Map<Long, BigDecimal> receivablePriceMap = convertMap(receivablePrice,
+        Map<Long, BigDecimal> receivablePriceMap = convertMap(receivablePrice,
             CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId,
             CrmStatisticsCustomerSummaryByUserRespVO::getReceivablePrice);
-        List<CrmStatisticsCustomerSummaryByUserRespVO> respVoList = new ArrayList<>(userIds.size());
-        userIds.forEach(userId -> {
-            final CrmStatisticsCustomerSummaryByUserRespVO vo = new CrmStatisticsCustomerSummaryByUserRespVO();
+        List<CrmStatisticsCustomerSummaryByUserRespVO> respVoList = convertList(userIds, userId -> {
+            CrmStatisticsCustomerSummaryByUserRespVO vo = new CrmStatisticsCustomerSummaryByUserRespVO();
+            // ownerUserId 为基类属性
             vo.setOwnerUserId(userId);
             vo.setCustomerCreateCount(customerCreateCountMap.getOrDefault(userId, 0))
                 .setCustomerDealCount(customerDealCountMap.getOrDefault(userId, 0))
                 .setContractPrice(contractPriceMap.getOrDefault(userId, BigDecimal.ZERO))
                 .setReceivablePrice(receivablePriceMap.getOrDefault(userId, BigDecimal.ZERO));
-            respVoList.add(vo);
+            return vo;
         });
 
         // 4. 拼接用户信息
         appendUserInfo(respVoList);
+
         return respVoList;
     }
 
     @Override
     public List<CrmStatisticsFollowupSummaryByDateRespVO> getFollowupSummaryByDate(CrmStatisticsCustomerReqVO reqVO) {
         // 1. 获得用户编号数组
-        final List<Long> userIds = getUserIds(reqVO);
+        List<Long> 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<CrmStatisticsFollowupSummaryByDateRespVO> followupRecordCount = customerMapper.selectFollowupRecordCountGroupbyDate(reqVO);
-        final List<CrmStatisticsFollowupSummaryByDateRespVO> followupCustomerCount = customerMapper.selectFollowupCustomerCountGroupbyDate(reqVO);
-
-        // 3. 获取时间序列
-        final List<String> times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]);
+        initParams(reqVO);
+        List<CrmStatisticsFollowupSummaryByDateRespVO> followupRecordCount = customerMapper.selectFollowupRecordCountGroupByDate(reqVO);
+        List<CrmStatisticsFollowupSummaryByDateRespVO> followupCustomerCount = customerMapper.selectFollowupCustomerCountGroupByDate(reqVO);
 
-        // 4. 合并统计数据
-        List<CrmStatisticsFollowupSummaryByDateRespVO> respVoList = new ArrayList<>(times.size());
-        final Map<String, Integer> followupRecordCountMap = convertMap(followupRecordCount,
+        // 3. 合并统计数据
+        List<String> times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]);
+        Map<String, Integer> followupRecordCountMap = convertMap(followupRecordCount,
             CrmStatisticsFollowupSummaryByDateRespVO::getTime,
             CrmStatisticsFollowupSummaryByDateRespVO::getFollowupRecordCount);
-        final Map<String, Integer> followupCustomerCountMap = convertMap(followupCustomerCount,
+        Map<String, Integer> followupCustomerCountMap = convertMap(followupCustomerCount,
             CrmStatisticsFollowupSummaryByDateRespVO::getTime,
             CrmStatisticsFollowupSummaryByDateRespVO::getFollowupCustomerCount);
-        times.forEach(time -> respVoList.add(
+        List<CrmStatisticsFollowupSummaryByDateRespVO> respVoList = convertList(times, time ->
             new CrmStatisticsFollowupSummaryByDateRespVO().setTime(time)
                 .setFollowupRecordCount(followupRecordCountMap.getOrDefault(time, 0))
                 .setFollowupCustomerCount(followupCustomerCountMap.getOrDefault(time, 0))
-        ));
+        );
 
         return respVoList;
     }
@@ -173,31 +169,31 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
     @Override
     public List<CrmStatisticsFollowupSummaryByUserRespVO> getFollowupSummaryByUser(CrmStatisticsCustomerReqVO reqVO) {
         // 1. 获得用户编号数组
-        final List<Long> userIds = getUserIds(reqVO);
+        List<Long> userIds = getUserIds(reqVO);
         if (CollUtil.isEmpty(userIds)) {
             return Collections.emptyList();
         }
         reqVO.setUserIds(userIds);
 
         // 2. 获取分项统计数据
-        reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType());
-        final List<CrmStatisticsFollowupSummaryByUserRespVO> followupRecordCount = customerMapper.selectFollowupRecordCountGroupbyUser(reqVO);
-        final List<CrmStatisticsFollowupSummaryByUserRespVO> followupCustomerCount = customerMapper.selectFollowupCustomerCountGroupbyUser(reqVO);
+        initParams(reqVO);
+        List<CrmStatisticsFollowupSummaryByUserRespVO> followupRecordCount = customerMapper.selectFollowupRecordCountGroupByUser(reqVO);
+        List<CrmStatisticsFollowupSummaryByUserRespVO> followupCustomerCount = customerMapper.selectFollowupCustomerCountGroupByUser(reqVO);
 
         // 3. 合并统计数据
-        final Map<Long, Integer> followupRecordCountMap = convertMap(followupRecordCount,
+        Map<Long, Integer> followupRecordCountMap = convertMap(followupRecordCount,
             CrmStatisticsFollowupSummaryByUserRespVO::getOwnerUserId,
             CrmStatisticsFollowupSummaryByUserRespVO::getFollowupRecordCount);
-        final Map<Long, Integer> followupCustomerCountMap = convertMap(followupCustomerCount,
+        Map<Long, Integer> followupCustomerCountMap = convertMap(followupCustomerCount,
             CrmStatisticsFollowupSummaryByUserRespVO::getOwnerUserId,
             CrmStatisticsFollowupSummaryByUserRespVO::getFollowupCustomerCount);
-        List<CrmStatisticsFollowupSummaryByUserRespVO> respVoList = new ArrayList<>(userIds.size());
-        userIds.forEach(userId -> {
-            final CrmStatisticsFollowupSummaryByUserRespVO vo = new CrmStatisticsFollowupSummaryByUserRespVO()
+        List<CrmStatisticsFollowupSummaryByUserRespVO> respVoList = convertList(userIds, userId -> {
+            CrmStatisticsFollowupSummaryByUserRespVO vo = new CrmStatisticsFollowupSummaryByUserRespVO()
                 .setFollowupRecordCount(followupRecordCountMap.getOrDefault(userId, 0))
                 .setFollowupCustomerCount(followupCustomerCountMap.getOrDefault(userId, 0));
+            // ownerUserId 为基类属性
             vo.setOwnerUserId(userId);
-            respVoList.add(vo);
+            return vo;
         });
 
         // 4. 拼接用户信息
@@ -208,19 +204,19 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
     @Override
     public List<CrmStatisticsFollowupSummaryByTypeRespVO> getFollowupSummaryByType(CrmStatisticsCustomerReqVO reqVO) {
         // 1. 获得用户编号数组
-        final List<Long> userIds = getUserIds(reqVO);
+        List<Long> userIds = getUserIds(reqVO);
         if (CollUtil.isEmpty(userIds)) {
             return Collections.emptyList();
         }
         reqVO.setUserIds(userIds);
 
         // 2. 获得排行数据
-        reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType());
-        List<CrmStatisticsFollowupSummaryByTypeRespVO> respVoList = customerMapper.selectFollowupRecordCountGroupbyType(reqVO);
+        initParams(reqVO);
+        List<CrmStatisticsFollowupSummaryByTypeRespVO> respVoList = customerMapper.selectFollowupRecordCountGroupByType(reqVO);
 
         // 3. 获取字典数据
         List<DictDataRespDTO> followUpTypes = dictDataApi.getDictDataList(CRM_FOLLOW_UP_TYPE);
-        final Map<String, String> followUpTypeMap = convertMap(followUpTypes,
+        Map<String, String> followUpTypeMap = convertMap(followUpTypes,
             DictDataRespDTO::getValue, DictDataRespDTO::getLabel);
         respVoList.forEach(vo -> {
             vo.setFollowupType(followUpTypeMap.get(vo.getFollowupType()));
@@ -232,26 +228,27 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
     @Override
     public List<CrmStatisticsCustomerContractSummaryRespVO> getContractSummary(CrmStatisticsCustomerReqVO reqVO) {
         // 1. 获得用户编号数组
-        final List<Long> userIds = getUserIds(reqVO);
+        List<Long> userIds = getUserIds(reqVO);
         if (CollUtil.isEmpty(userIds)) {
             return Collections.emptyList();
         }
         reqVO.setUserIds(userIds);
 
         // 2. 获取统计数据
+        initParams(reqVO);
         List<CrmStatisticsCustomerContractSummaryRespVO> respVoList = customerMapper.selectContractSummary(reqVO);
 
         // 3. 设置 创建人、负责人、行业、来源
-        // 获取客户所属行业
+        // 3.1 获取客户所属行业
         Map<String, String> industryMap = convertMap(dictDataApi.getDictDataList(CRM_CUSTOMER_INDUSTRY),
             DictDataRespDTO::getValue, DictDataRespDTO::getLabel);
-        // 获取客户来源
+        // 3.2 获取客户来源
         Map<String, String> sourceMap = convertMap(dictDataApi.getDictDataList(CRM_CUSTOMER_SOURCE),
             DictDataRespDTO::getValue, DictDataRespDTO::getLabel);
-        // 获取创建人、负责人列表
+        // 3.3 获取创建人、负责人列表
         Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSetByFlatMap(respVoList,
             vo -> Stream.of(NumberUtils.parseLong(vo.getCreatorUserId()), vo.getOwnerUserId())));
-
+        // 3.4 设置 创建人、负责人、行业、来源
         respVoList.forEach(vo -> {
             MapUtils.findAndThen(industryMap, vo.getIndustryId(), vo::setIndustryName);
             MapUtils.findAndThen(sourceMap, vo.getSource(), vo::setSourceName);
@@ -266,60 +263,57 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
     @Override
     public List<CrmStatisticsCustomerDealCycleByDateRespVO> getCustomerDealCycleByDate(CrmStatisticsCustomerReqVO reqVO) {
         // 1. 获得用户编号数组
-        final List<Long> userIds = getUserIds(reqVO);
+        List<Long> 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<CrmStatisticsCustomerDealCycleByDateRespVO> customerDealCycle = customerMapper.selectCustomerDealCycleGroupbyDate(reqVO);
-
-        // 3. 获取时间序列
-        final List<String> times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]);
+        initParams(reqVO);
+        List<CrmStatisticsCustomerDealCycleByDateRespVO> customerDealCycle = customerMapper.selectCustomerDealCycleGroupByDate(reqVO);
 
-        // 4. 合并统计数据
-        List<CrmStatisticsCustomerDealCycleByDateRespVO> respVoList = new ArrayList<>(times.size());
-        final Map<String, Double> customerDealCycleMap = convertMap(customerDealCycle,
+        // 3. 合并统计数据
+        List<String> times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]);
+        Map<String, Double> customerDealCycleMap = convertMap(customerDealCycle,
             CrmStatisticsCustomerDealCycleByDateRespVO::getTime,
             CrmStatisticsCustomerDealCycleByDateRespVO::getCustomerDealCycle);
-        times.forEach(time -> respVoList.add(
+        List<CrmStatisticsCustomerDealCycleByDateRespVO> respVoList = convertList(times, time ->
             new CrmStatisticsCustomerDealCycleByDateRespVO().setTime(time)
                 .setCustomerDealCycle(customerDealCycleMap.getOrDefault(time, 0D))
-        ));
+        );
+
         return respVoList;
     }
 
     @Override
     public List<CrmStatisticsCustomerDealCycleByUserRespVO> getCustomerDealCycleByUser(CrmStatisticsCustomerReqVO reqVO) {
         // 1. 获得用户编号数组
-        final List<Long> userIds = getUserIds(reqVO);
+        List<Long> userIds = getUserIds(reqVO);
         if (CollUtil.isEmpty(userIds)) {
             return Collections.emptyList();
         }
         reqVO.setUserIds(userIds);
 
         // 2. 获取分项统计数据
-        reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType());
-        final List<CrmStatisticsCustomerDealCycleByUserRespVO> customerDealCycle = customerMapper.selectCustomerDealCycleGroupbyUser(reqVO);
-        final List<CrmStatisticsCustomerSummaryByUserRespVO> customerDealCount = customerMapper.selectCustomerDealCountGroupbyUser(reqVO);
+        initParams(reqVO);
+        List<CrmStatisticsCustomerDealCycleByUserRespVO> customerDealCycle = customerMapper.selectCustomerDealCycleGroupByUser(reqVO);
+        List<CrmStatisticsCustomerSummaryByUserRespVO> customerDealCount = customerMapper.selectCustomerDealCountGroupByUser(reqVO);
 
         // 3. 合并统计数据
-        final Map<Long, Double> customerDealCycleMap = convertMap(customerDealCycle,
+        Map<Long, Double> customerDealCycleMap = convertMap(customerDealCycle,
             CrmStatisticsCustomerDealCycleByUserRespVO::getOwnerUserId,
             CrmStatisticsCustomerDealCycleByUserRespVO::getCustomerDealCycle);
-        final Map<Long, Integer> customerDealCountMap = convertMap(customerDealCount,
+        Map<Long, Integer> customerDealCountMap = convertMap(customerDealCount,
             CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId,
             CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount);
-        List<CrmStatisticsCustomerDealCycleByUserRespVO> respVoList = new ArrayList<>(userIds.size());
-        userIds.forEach(userId -> {
-            final CrmStatisticsCustomerDealCycleByUserRespVO vo = new CrmStatisticsCustomerDealCycleByUserRespVO()
+        List<CrmStatisticsCustomerDealCycleByUserRespVO> respVoList = convertList(userIds, userId -> {
+            CrmStatisticsCustomerDealCycleByUserRespVO vo = new CrmStatisticsCustomerDealCycleByUserRespVO()
                 .setCustomerDealCycle(customerDealCycleMap.getOrDefault(userId, 0.0))
                 .setCustomerDealCount(customerDealCountMap.getOrDefault(userId, 0));
+            // ownerUserId 为基类属性
             vo.setOwnerUserId(userId);
-            respVoList.add(vo);
+            return vo;
         });
 
         // 4. 拼接用户信息
@@ -335,8 +329,9 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
      */
     private <T extends CrmStatisticsCustomerByUserBaseRespVO> void appendUserInfo(List<T> respVoList) {
         Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(respVoList,
-                CrmStatisticsCustomerByUserBaseRespVO::getOwnerUserId));
-        respVoList.forEach(vo -> MapUtils.findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname())));
+            CrmStatisticsCustomerByUserBaseRespVO::getOwnerUserId));
+        respVoList.forEach(vo -> MapUtils.findAndThen(userMap,
+            vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname())));
     }
 
     /**
@@ -352,7 +347,7 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
         }
         // 情况二:选中某个部门
         // 2.1 获得部门列表
-        final Long deptId = reqVO.getDeptId();
+        Long deptId = reqVO.getDeptId();
         List<Long> deptIds = convertList(deptApi.getChildDeptList(deptId), DeptRespDTO::getId);
         deptIds.add(deptId);
         // 2.2 获得用户编号
@@ -378,7 +373,6 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
      * @param endTime   结束时间
      * @return 时间序列
      */
-    // TODO @dhb52:可以抽象到 DateUtils 里,开始时间、结束时间,事件间隔,然后返回这个哈;
     private List<String> generateTimeSeries(LocalDateTime startTime, LocalDateTime endTime) {
         boolean byMonth = queryByMonth(startTime, endTime);
         List<String> times = CollUtil.newArrayList();
@@ -404,4 +398,62 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
         return queryByMonth(startTime, endTime) ? SQL_DATE_FORMAT_BY_MONTH : SQL_DATE_FORMAT_BY_DAY;
     }
 
+    private void initParams(CrmStatisticsCustomerReqVO reqVO) {
+        final Integer intervalType = reqVO.getIntervalType();
+
+        // 1. 自定义时间间隔,必须输入起始日期-结束日期
+        if (DateIntervalEnum.CUSTOMER.getType().equals(intervalType)) {
+            if (ObjUtil.isEmpty(reqVO.getTimes()) || reqVO.getTimes().length != 2) {
+                throw exception(STATISTICS_CUSTOMER_TIMES_NOT_SET);
+            }
+            // 设置 mapper sqlDateFormat 参数
+            reqVO.setSqlDateFormat(getSqlDateFormat(reqVO.getTimes()[0], reqVO.getTimes()[1]));
+            // 自定义日期无需计算日期参数
+            return;
+        }
+
+        // 2. 根据时间区间类型计算时间段区间日期
+        DateTime beginDate = null;
+        DateTime endDate = null;
+        if (DateIntervalEnum.TODAY.getType().equals(intervalType)) {
+            beginDate = DateUtil.beginOfDay(DateUtil.date());
+            endDate = DateUtil.endOfDay(DateUtil.date());
+        } else if (DateIntervalEnum.YESTERDAY.getType().equals(intervalType)) {
+            beginDate = DateUtil.offsetDay(DateUtil.date(), -1);
+            endDate = DateUtil.offsetDay(DateUtil.date(), -1);
+        } else if (DateIntervalEnum.THIS_WEEK.getType().equals(intervalType)) {
+            beginDate = DateUtil.beginOfWeek(DateUtil.date());
+            endDate = DateUtil.endOfWeek(DateUtil.date());
+        } else if (DateIntervalEnum.LAST_WEEK.getType().equals(intervalType)) {
+            beginDate = DateUtil.beginOfWeek(DateUtil.offsetWeek(DateUtil.date(), -1));
+            endDate = DateUtil.endOfWeek(DateUtil.offsetWeek(DateUtil.date(), -1));
+        } else if (DateIntervalEnum.THIS_MONTH.getType().equals(intervalType)) {
+            beginDate = DateUtil.beginOfMonth(DateUtil.date());
+            endDate = DateUtil.endOfMonth(DateUtil.date());
+        } else if (DateIntervalEnum.LAST_MONTH.getType().equals(intervalType)) {
+            beginDate = DateUtil.beginOfMonth(DateUtil.offsetMonth(DateUtil.date(), -1));
+            endDate = DateUtil.endOfMonth(DateUtil.offsetMonth(DateUtil.date(), -1));
+        } else if (DateIntervalEnum.THIS_QUARTER.getType().equals(intervalType)) {
+            beginDate = DateUtil.beginOfQuarter(DateUtil.date());
+            endDate = DateUtil.endOfQuarter(DateUtil.date());
+        } else if (DateIntervalEnum.LAST_QUARTER.getType().equals(intervalType)) {
+            beginDate = DateUtil.beginOfQuarter(DateUtil.offsetMonth(DateUtil.date(), -3));
+            endDate = DateUtil.endOfQuarter(DateUtil.offsetMonth(DateUtil.date(), -3));
+        } else if (DateIntervalEnum.THIS_YEAR.getType().equals(intervalType)) {
+            beginDate = DateUtil.beginOfYear(DateUtil.date());
+            endDate = DateUtil.endOfYear(DateUtil.date());
+        } else if (DateIntervalEnum.LAST_YEAR.getType().equals(intervalType)) {
+            beginDate = DateUtil.beginOfYear(DateUtil.offsetMonth(DateUtil.date(), -12));
+            endDate = DateUtil.endOfYear(DateUtil.offsetMonth(DateUtil.date(), -12));
+        }
+
+        // 3. 计算开始、结束日期时间,并设置reqVo
+        LocalDateTime[] times = new LocalDateTime[2];
+        times[0] = LocalDateTimeUtil.beginOfDay(LocalDateTimeUtil.of(beginDate));
+        times[1] = LocalDateTimeUtil.endOfDay(LocalDateTimeUtil.of(endDate));
+        // 3.1 设置 mapper 时间区间 参数
+        reqVO.setTimes(times);
+        // 3.2 设置 mapper sqlDateFormat 参数
+        reqVO.setSqlDateFormat(getSqlDateFormat(times[0], times[1]));
+    }
 }

+ 177 - 195
yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml

@@ -2,251 +2,233 @@
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper">
 
-    <!-- TODO @dhb52:数据库的关键字,进行大写。例如说,COUNT -->
-
-    <select id="selectCustomerCreateCountGroupbyDate"
+    <select id="selectCustomerCreateCountGroupByDate"
             resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByDateRespVO">
         SELECT
-        <!-- TODO @dhb52:下面这个,缩进一个 tab;这样可读性更好哈 -->
-        DATE_FORMAT( create_time, #{sqlDateFormat} ) AS time,
-        COUNT(*) AS customerCreateCount
-        FROM crm_customer
-        WHERE deleted = 0
-        AND owner_user_id IN
-        <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
-            #{userId}
-        </foreach>
-        AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
-        <!-- TODO @dhb52:这可以考虑不换行,直接跟在后面的 AND,更连贯哈; -->
-        #{times[1],javaType=java.time.LocalDateTime}
-        GROUP BY time
+            DATE_FORMAT( create_time, #{sqlDateFormat} ) AS time,
+            COUNT(*) AS customerCreateCount
+          FROM crm_customer
+         WHERE deleted = 0
+           AND owner_user_id IN
+                <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
+                    #{userId}
+                </foreach>
+           AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
+         GROUP BY time
     </select>
 
-    <select id="selectCustomerDealCountGroupbyDate"
+    <select id="selectCustomerDealCountGroupByDate"
             resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByDateRespVO">
         SELECT
-        <!-- TODO @dhb52:下面这个,缩进一个 tab;这样可读性更好哈 -->
-        <!-- TODO @dhb52:表变量最好不要用 a、b;可以用 customer 和 constract;虽然长一点,但是一眼看的清楚哈 -->
-        DATE_FORMAT( b.order_date, #{sqlDateFormat} ) AS time,
-        count( DISTINCT a.id ) AS customerDealCount
-        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
-        <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
-            #{userId}
-        </foreach>
-        <!-- TODO @dhb52:这个应该是 order_date 的范围哈;貌似如果改成这样,不需要查询 customer 表,只要 contract 表就 ok 拉 -->
-        AND b.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
-        #{times[1],javaType=java.time.LocalDateTime}
+            DATE_FORMAT( order_date, #{sqlDateFormat} ) AS time,
+            COUNT( DISTINCT customer_id ) AS customerDealCount
+         FROM crm_contract
+        WHERE deleted = 0
+          AND audit_status = 20
+          AND owner_user_id IN
+                <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
+                    #{userId}
+                </foreach>
+          AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
         GROUP BY time
     </select>
 
-    <!-- TODO @dhb52:根据上面建议,进行优化 -->
-    <select id="selectCustomerCreateCountGroupbyUser"
+    <select id="selectCustomerCreateCountGroupByUser"
             resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByUserRespVO">
-        SELECT owner_user_id, COUNT(1) AS customer_create_count
-        FROM crm_customer
-        WHERE deleted = 0
-        AND owner_user_id in
-        <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
-            #{userId}
-        </foreach>
-        AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
-        #{times[1],javaType=java.time.LocalDateTime}
+        SELECT
+            owner_user_id,
+            COUNT(1) AS customer_create_count
+          FROM crm_customer
+         WHERE deleted = 0
+           AND owner_user_id in
+                <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
+                    #{userId}
+                </foreach>
+          AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
         GROUP BY owner_user_id
     </select>
 
-    <!-- TODO @dhb52:根据上面建议,进行优化 -->
-    <select id="selectCustomerDealCountGroupbyUser"
+    <select id="selectCustomerDealCountGroupByUser"
             resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByUserRespVO">
-        SELECT a.owner_user_id, count( DISTINCT a.id ) AS customer_deal_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
-        <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
-            #{userId}
-        </foreach>
-        AND b.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
-        #{times[1],javaType=java.time.LocalDateTime}
-        GROUP BY a.owner_user_id
+        SELECT
+            customer.owner_user_id,
+            COUNT( DISTINCT customer.id ) AS customer_deal_count
+          FROM crm_customer AS customer
+                LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id
+         WHERE customer.deleted = 0 AND contract.deleted = 0
+           AND contract.audit_status = 20
+           AND customer.owner_user_id IN
+                <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
+                    #{userId}
+                </foreach>
+           AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
+         GROUP BY customer.owner_user_id
     </select>
 
-    <!-- TODO @dhb52:根据上面建议,进行优化 -->
-    <select id="selectContractPriceGroupbyUser"
+    <select id="selectContractPriceGroupByUser"
             resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByUserRespVO">
-        SELECT owner_user_id, IFNULL(SUM(total_price), 0) AS contract_price
-        FROM crm_contract
-        WHERE deleted = 0
-        AND audit_status = 20
-        AND owner_user_id in
-        <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
-            #{userId}
-        </foreach>
-        AND order_date BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
-        #{times[1],javaType=java.time.LocalDateTime}
-        GROUP BY owner_user_id
+        SELECT
+            owner_user_id,
+            IFNULL(SUM(total_price), 0) AS contract_price
+          FROM crm_contract
+         WHERE deleted = 0
+           AND audit_status = 20
+           AND owner_user_id in
+                <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
+                    #{userId}
+                </foreach>
+           AND order_date BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
+         GROUP BY owner_user_id
     </select>
 
-
-    <!-- TODO @dhb52:根据上面建议,进行优化 -->
-    <select id="selectReceivablePriceGroupbyUser"
+    <select id="selectReceivablePriceGroupByUser"
             resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByUserRespVO">
-        SELECT owner_user_id,
-        IFNULL(SUM(price), 0) AS receivable_price
-        FROM crm_receivable
-        WHERE deleted = 0
-        AND audit_status = 20
-        AND owner_user_id IN
-        <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
-            #{userId}
-        </foreach>
-        AND return_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
-        #{times[1],javaType=java.time.LocalDateTime}
-        GROUP BY owner_user_id
+        SELECT
+            owner_user_id,
+            IFNULL(SUM(price), 0) AS receivable_price
+          FROM crm_receivable
+         WHERE deleted = 0
+           AND audit_status = 20
+           AND owner_user_id IN
+                 <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
+                     #{userId}
+                 </foreach>
+           AND return_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
+         GROUP BY owner_user_id
     </select>
 
-    <select id="selectFollowupRecordCountGroupbyDate"
+    <select id="selectFollowupRecordCountGroupByDate"
             resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowupSummaryByDateRespVO">
-        SELECT DATE_FORMAT( create_time, #{sqlDateFormat} ) AS time,
-        count(*) AS followup_record_count
-        FROM crm_follow_up_record
-        WHERE creator IN
-        <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
-            #{userId}
-        </foreach>
-        AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
-        #{times[1],javaType=java.time.LocalDateTime}
-        AND biz_type = #{bizType}
-        GROUP BY time
+        SELECT
+            DATE_FORMAT( create_time, #{sqlDateFormat} ) AS time,
+            COUNT(*) AS followup_record_count
+          FROM crm_follow_up_record
+         WHERE creator IN
+                <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
+                    #{userId}
+                </foreach>
+           AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
+           AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
+         GROUP BY time
     </select>
 
-    <select id="selectFollowupCustomerCountGroupbyDate"
+    <select id="selectFollowupCustomerCountGroupByDate"
             resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowupSummaryByDateRespVO">
         SELECT
-        DATE_FORMAT( create_time, #{sqlDateFormat} ) AS time,
-        count(DISTINCT biz_id) AS followup_customer_count
-        FROM crm_follow_up_record
-        WHERE creator IN
-        <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
-            #{userId}
-        </foreach>
-        AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
-        #{times[1],javaType=java.time.LocalDateTime}
-        AND biz_type = #{bizType}
-        GROUP BY time
+            DATE_FORMAT( create_time, #{sqlDateFormat} ) AS time,
+            COUNT(DISTINCT biz_id) AS followup_customer_count
+          FROM crm_follow_up_record
+         WHERE creator IN
+                <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
+                    #{userId}
+                </foreach>
+           AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
+           AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
+         GROUP BY time
     </select>
 
-    <select id="selectFollowupRecordCountGroupbyUser"
+    <select id="selectFollowupRecordCountGroupByUser"
             resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowupSummaryByUserRespVO">
-        SELECT creator as owner_user_id,
-        count(*) AS followup_record_count
-        FROM crm_follow_up_record
-        WHERE creator IN
-        <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
-            #{userId}
-        </foreach>
-        AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
-        #{times[1],javaType=java.time.LocalDateTime}
-        AND biz_type = #{bizType}
-        GROUP BY creator
+        SELECT
+            creator as owner_user_id,
+            COUNT(*) AS followup_record_count
+          FROM crm_follow_up_record
+         WHERE creator IN
+                <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
+                    #{userId}
+                </foreach>
+           AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
+           AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
+         GROUP BY creator
     </select>
 
-    <select id="selectFollowupCustomerCountGroupbyUser"
+    <select id="selectFollowupCustomerCountGroupByUser"
             resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowupSummaryByUserRespVO">
-        SELECT creator as owner_user_id,
-        count(DISTINCT biz_id) AS followup_customer_count
-        FROM crm_follow_up_record
-        WHERE creator IN
-        <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
-            #{userId}
-        </foreach>
-        AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
-        #{times[1],javaType=java.time.LocalDateTime}
-        AND biz_type = #{bizType}
-        GROUP BY creator
+        SELECT
+            creator as owner_user_id,
+            COUNT(DISTINCT biz_id) AS followup_customer_count
+          FROM crm_follow_up_record
+         WHERE creator IN
+                <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
+                    #{userId}
+                </foreach>
+           AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
+           AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
+         GROUP BY creator
     </select>
 
-    <select id="selectFollowupRecordCountGroupbyType"
+    <select id="selectFollowupRecordCountGroupByType"
             resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowupSummaryByTypeRespVO">
         SELECT
-        type AS followupType,
-        count(*) AS followup_record_count
-        FROM crm_follow_up_record
-        WHERE creator IN
-        <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
-            #{userId}
-        </foreach>
-        AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
-        #{times[1],javaType=java.time.LocalDateTime}
-        AND biz_type = #{bizType}
-        GROUP BY followupType
+            type AS followupType,
+            COUNT(*) AS followup_record_count
+          FROM crm_follow_up_record
+         WHERE creator IN
+                <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
+                    #{userId}
+                </foreach>
+           AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
+           AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
+         GROUP BY followupType
     </select>
 
     <select id="selectContractSummary"
             resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerContractSummaryRespVO">
         SELECT
-        a.`name` AS customer_name,
-        b.`name` AS contract_name,
-        b.total_price,
-        IFNULL( c.price, 0 ) AS receivable_price,
-        a.industry_id,
-        a.source,
-        a.owner_user_id,
-        a.creator AS creator_user_id,
-        a.create_time,
-        b.order_date
-        FROM
-        crm_customer AS a
-        INNER JOIN crm_contract AS b ON a.id = b.customer_id
-        LEFT JOIN crm_receivable AS c ON b.id = c.contract_id
-        WHERE a.deleted = 0 AND b.deleted = 0 AND c.deleted = 0
-        AND b.audit_status = 20
-        AND a.owner_user_id IN
-        <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
-            #{userId}
-        </foreach>
-        AND b.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
-        #{times[1],javaType=java.time.LocalDateTime}
+            customer.`name` AS customer_name,
+            contract.`name` AS contract_name,
+            contract.total_price,
+            IFNULL( receivable.price, 0 ) AS receivable_price,
+            customer.industry_id,
+            customer.source,
+            customer.owner_user_id,
+            customer.creator AS creator_user_id,
+            customer.create_time,
+            contract.order_date
+          FROM crm_customer AS customer
+                INNER JOIN crm_contract AS contract ON customer.id = contract.customer_id
+                LEFT JOIN crm_receivable AS receivable ON contract.id = receivable.contract_id
+         WHERE customer.deleted = 0 AND contract.deleted = 0 AND receivable.deleted = 0
+           AND contract.audit_status = 20
+           AND customer.owner_user_id IN
+                <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
+                    #{userId}
+                </foreach>
+           AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
     </select>
 
-    <select id="selectCustomerDealCycleGroupbyDate"
+    <select id="selectCustomerDealCycleGroupByDate"
             resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerDealCycleByDateRespVO">
         SELECT
-        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
-        <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
-            #{userId}
-        </foreach>
-        AND b.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
-        #{times[1],javaType=java.time.LocalDateTime}
-        GROUP BY time
+            DATE_FORMAT( contract.order_date, #{sqlDateFormat} ) AS time,
+            IFNULL( TRUNCATE ( AVG( TIMESTAMPDIFF( DAY, customer.create_time, contract.order_date )), 1 ), 0 ) AS customer_deal_cycle
+          FROM crm_customer AS customer
+                LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id
+         WHERE customer.deleted = 0 AND contract.deleted = 0
+           AND contract.audit_status = 20
+           AND customer.owner_user_id IN
+                <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
+                    #{userId}
+                </foreach>
+           AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
+         GROUP BY time
     </select>
 
-    <select id="selectCustomerDealCycleGroupbyUser"
+    <select id="selectCustomerDealCycleGroupByUser"
             resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerDealCycleByUserRespVO">
         SELECT
-        a.owner_user_id,
-        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
-        <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
-            #{userId}
-        </foreach>
-        AND b.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
-        #{times[1],javaType=java.time.LocalDateTime}
-        GROUP BY a.owner_user_id
+            customer.owner_user_id,
+            IFNULL( TRUNCATE ( AVG( TIMESTAMPDIFF( DAY, customer.create_time, contract.order_date )), 1 ), 0 ) AS customer_deal_cycle
+          FROM crm_customer AS customer
+                LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id
+         WHERE customer.deleted = 0 AND contract.deleted = 0
+           AND contract.audit_status = 20
+           AND customer.owner_user_id IN
+                <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
+                    #{userId}
+                </foreach>
+           AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
+         GROUP BY customer.owner_user_id
     </select>
 
 </mapper>