Commit 90f82b157de162ecf07cb904ce2fe632d6bdb6ea

Authored by YunaiV
1 parent eab96272

CRM:优化【客户统计】的代码实现

Showing 21 changed files with 497 additions and 614 deletions
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DateIntervalEnum.java
1 package cn.iocoder.yudao.framework.common.enums; 1 package cn.iocoder.yudao.framework.common.enums;
2 2
  3 +import cn.hutool.core.util.ArrayUtil;
3 import cn.iocoder.yudao.framework.common.core.IntArrayValuable; 4 import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
4 import lombok.AllArgsConstructor; 5 import lombok.AllArgsConstructor;
5 import lombok.Getter; 6 import lombok.Getter;
@@ -7,7 +8,7 @@ import lombok.Getter; @@ -7,7 +8,7 @@ import lombok.Getter;
7 import java.util.Arrays; 8 import java.util.Arrays;
8 9
9 /** 10 /**
10 - * 时间间隔类型枚举 11 + * 时间间隔的枚举
11 * 12 *
12 * @author dhb52 13 * @author dhb52
13 */ 14 */
@@ -15,26 +16,19 @@ import java.util.Arrays; @@ -15,26 +16,19 @@ import java.util.Arrays;
15 @AllArgsConstructor 16 @AllArgsConstructor
16 public enum DateIntervalEnum implements IntArrayValuable { 17 public enum DateIntervalEnum implements IntArrayValuable {
17 18
18 - TODAY(1, "今天"),  
19 - YESTERDAY(2, "昨天"),  
20 - THIS_WEEK(3, "本周"),  
21 - LAST_WEEK(4, "上周"),  
22 - THIS_MONTH(5, "本月"),  
23 - LAST_MONTH(6, "上月"),  
24 - THIS_QUARTER(7, "本季度"),  
25 - LAST_QUARTER(8, "上季度"),  
26 - THIS_YEAR(9, "本年"),  
27 - LAST_YEAR(10, "去年"),  
28 - CUSTOMER(11, "自定义"), 19 + DAY(1, "天"),
  20 + WEEK(2, "周"),
  21 + MONTH(3, "月"),
  22 + QUARTER(4, "季度"),
  23 + YEAR(5, "年")
29 ; 24 ;
30 25
31 - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(DateIntervalEnum::getType).toArray(); 26 + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(DateIntervalEnum::getInterval).toArray();
32 27
33 /** 28 /**
34 * 类型 29 * 类型
35 */ 30 */
36 - private final Integer type;  
37 - 31 + private final Integer interval;
38 /** 32 /**
39 * 名称 33 * 名称
40 */ 34 */
@@ -45,4 +39,8 @@ public enum DateIntervalEnum implements IntArrayValuable { @@ -45,4 +39,8 @@ public enum DateIntervalEnum implements IntArrayValuable {
45 return ARRAYS; 39 return ARRAYS;
46 } 40 }
47 41
  42 + public static DateIntervalEnum valueOf(Integer interval) {
  43 + return ArrayUtil.firstMatch(item -> item.getInterval().equals(interval), DateIntervalEnum.values());
  44 + }
  45 +
48 } 46 }
49 \ No newline at end of file 47 \ No newline at end of file
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java
@@ -65,19 +65,11 @@ public class DateUtils { @@ -65,19 +65,11 @@ public class DateUtils {
65 return new Date(System.currentTimeMillis() + duration.toMillis()); 65 return new Date(System.currentTimeMillis() + duration.toMillis());
66 } 66 }
67 67
68 - public static boolean isExpired(Date time) {  
69 - return System.currentTimeMillis() > time.getTime();  
70 - }  
71 -  
72 public static boolean isExpired(LocalDateTime time) { 68 public static boolean isExpired(LocalDateTime time) {
73 LocalDateTime now = LocalDateTime.now(); 69 LocalDateTime now = LocalDateTime.now();
74 return now.isAfter(time); 70 return now.isAfter(time);
75 } 71 }
76 72
77 - public static long diff(Date endTime, Date startTime) {  
78 - return endTime.getTime() - startTime.getTime();  
79 - }  
80 -  
81 /** 73 /**
82 * 创建指定时间 74 * 创建指定时间
83 * 75 *
@@ -135,37 +127,6 @@ public class DateUtils { @@ -135,37 +127,6 @@ public class DateUtils {
135 } 127 }
136 128
137 /** 129 /**
138 - * 计算当期时间相差的日期  
139 - *  
140 - * @param field 日历字段.<br/>eg:Calendar.MONTH,Calendar.DAY_OF_MONTH,<br/>Calendar.HOUR_OF_DAY等.  
141 - * @param amount 相差的数值  
142 - * @return 计算后的日志  
143 - */  
144 - public static Date addDate(int field, int amount) {  
145 - return addDate(null, field, amount);  
146 - }  
147 -  
148 - /**  
149 - * 计算当期时间相差的日期  
150 - *  
151 - * @param date 设置时间  
152 - * @param field 日历字段 例如说,{@link Calendar#DAY_OF_MONTH} 等  
153 - * @param amount 相差的数值  
154 - * @return 计算后的日志  
155 - */  
156 - public static Date addDate(Date date, int field, int amount) {  
157 - if (amount == 0) {  
158 - return date;  
159 - }  
160 - Calendar c = Calendar.getInstance();  
161 - if (date != null) {  
162 - c.setTime(date);  
163 - }  
164 - c.add(field, amount);  
165 - return c.getTime();  
166 - }  
167 -  
168 - /**  
169 * 是否今天 130 * 是否今天
170 * 131 *
171 * @param date 日期 132 * @param date 日期
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java
1 package cn.iocoder.yudao.framework.common.util.date; 1 package cn.iocoder.yudao.framework.common.util.date;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.date.DatePattern;
3 import cn.hutool.core.date.LocalDateTimeUtil; 5 import cn.hutool.core.date.LocalDateTimeUtil;
  6 +import cn.hutool.core.lang.Assert;
  7 +import cn.hutool.core.util.StrUtil;
  8 +import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum;
4 9
5 -import java.time.Duration;  
6 -import java.time.LocalDate;  
7 -import java.time.LocalDateTime;  
8 -import java.time.LocalTime; 10 +import java.time.*;
  11 +import java.time.format.DateTimeParseException;
9 import java.time.temporal.ChronoUnit; 12 import java.time.temporal.ChronoUnit;
10 import java.time.temporal.TemporalAdjusters; 13 import java.time.temporal.TemporalAdjusters;
  14 +import java.util.ArrayList;
  15 +import java.util.List;
11 16
12 /** 17 /**
13 * 时间工具类,用于 {@link java.time.LocalDateTime} 18 * 时间工具类,用于 {@link java.time.LocalDateTime}
@@ -21,6 +26,22 @@ public class LocalDateTimeUtils { @@ -21,6 +26,22 @@ public class LocalDateTimeUtils {
21 */ 26 */
22 public static LocalDateTime EMPTY = buildTime(1970, 1, 1); 27 public static LocalDateTime EMPTY = buildTime(1970, 1, 1);
23 28
  29 + /**
  30 + * 解析时间
  31 + *
  32 + * 相比 {@link LocalDateTimeUtil#parse(CharSequence)} 方法来说,会尽量去解析,直到成功
  33 + *
  34 + * @param time 时间
  35 + * @return 时间字符串
  36 + */
  37 + public static LocalDateTime parse(String time) {
  38 + try {
  39 + return LocalDateTimeUtil.parse(time, DatePattern.NORM_DATE_PATTERN);
  40 + } catch (DateTimeParseException e) {
  41 + return LocalDateTimeUtil.parse(time);
  42 + }
  43 + }
  44 +
24 public static LocalDateTime addTime(Duration duration) { 45 public static LocalDateTime addTime(Duration duration) {
25 return LocalDateTime.now().plus(duration); 46 return LocalDateTime.now().plus(duration);
26 } 47 }
@@ -55,6 +76,21 @@ public class LocalDateTimeUtils { @@ -55,6 +76,21 @@ public class LocalDateTimeUtils {
55 } 76 }
56 77
57 /** 78 /**
  79 + * 判指定断时间,是否在该时间范围内
  80 + *
  81 + * @param startTime 开始时间
  82 + * @param endTime 结束时间
  83 + * @param time 指定时间
  84 + * @return 是否
  85 + */
  86 + public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime, String time) {
  87 + if (startTime == null || endTime == null || time == null) {
  88 + return false;
  89 + }
  90 + return LocalDateTimeUtil.isIn(parse(time), startTime, endTime);
  91 + }
  92 +
  93 + /**
58 * 判断当前时间是否在该时间范围内 94 * 判断当前时间是否在该时间范围内
59 * 95 *
60 * @param startTime 开始时间 96 * @param startTime 开始时间
@@ -123,6 +159,16 @@ public class LocalDateTimeUtils { @@ -123,6 +159,16 @@ public class LocalDateTimeUtils {
123 } 159 }
124 160
125 /** 161 /**
  162 + * 获得指定日期所在季度
  163 + *
  164 + * @param date 日期
  165 + * @return 所在季度
  166 + */
  167 + public static int getQuarterOfYear(LocalDateTime date) {
  168 + return (date.getMonthValue() - 1) / 3 + 1;
  169 + }
  170 +
  171 + /**
126 * 获取指定日期到现在过了几天,如果指定日期在当前日期之后,获取结果为负 172 * 获取指定日期到现在过了几天,如果指定日期在当前日期之后,获取结果为负
127 * 173 *
128 * @param dateTime 日期 174 * @param dateTime 日期
@@ -168,4 +214,94 @@ public class LocalDateTimeUtils { @@ -168,4 +214,94 @@ public class LocalDateTimeUtils {
168 return LocalDateTime.now().with(TemporalAdjusters.firstDayOfYear()).with(LocalTime.MIN); 214 return LocalDateTime.now().with(TemporalAdjusters.firstDayOfYear()).with(LocalTime.MIN);
169 } 215 }
170 216
  217 + public static List<LocalDateTime[]> getDateRangeList(LocalDateTime startTime,
  218 + LocalDateTime endTime,
  219 + Integer interval) {
  220 + // 1.1 找到枚举
  221 + DateIntervalEnum intervalEnum = DateIntervalEnum.valueOf(interval);
  222 + Assert.notNull(intervalEnum, "interval({}} 找不到对应的枚举", interval);
  223 + // 1.2 将时间对齐
  224 + startTime = LocalDateTimeUtil.beginOfDay(startTime);
  225 + endTime = LocalDateTimeUtil.endOfDay(endTime);
  226 +
  227 + // 2. 循环,生成时间范围
  228 + List<LocalDateTime[]> timeRanges = new ArrayList<>();
  229 + switch (intervalEnum) {
  230 + case DateIntervalEnum.DAY:
  231 + while (startTime.isBefore(endTime)) {
  232 + timeRanges.add(new LocalDateTime[]{startTime, startTime.plusDays(1).minusNanos(1)});
  233 + startTime = startTime.plusDays(1);
  234 + }
  235 + break;
  236 + case DateIntervalEnum.WEEK:
  237 + while (startTime.isBefore(endTime)) {
  238 + LocalDateTime endOfWeek = startTime.with(DayOfWeek.SUNDAY).plusDays(1).minusNanos(1);
  239 + timeRanges.add(new LocalDateTime[]{startTime, endOfWeek});
  240 + startTime = endOfWeek.plusNanos(1);
  241 + }
  242 + break;
  243 + case DateIntervalEnum.MONTH:
  244 + while (startTime.isBefore(endTime)) {
  245 + LocalDateTime endOfMonth = startTime.with(TemporalAdjusters.lastDayOfMonth()).plusDays(1).minusNanos(1);
  246 + timeRanges.add(new LocalDateTime[]{startTime, endOfMonth});
  247 + startTime = endOfMonth.plusNanos(1);
  248 + }
  249 + break;
  250 + case DateIntervalEnum.QUARTER:
  251 + while (startTime.isBefore(endTime)) {
  252 + LocalDateTime quarterEnd = startTime.withMonth(getQuarterOfYear(startTime) * 3 + 1)
  253 + .withDayOfMonth(1).minusNanos(1);
  254 + timeRanges.add(new LocalDateTime[]{startTime, quarterEnd});
  255 + startTime = quarterEnd.plusNanos(1);
  256 + }
  257 + break;
  258 + case DateIntervalEnum.YEAR:
  259 + while (startTime.isBefore(endTime)) {
  260 + LocalDateTime endOfYear = startTime.with(TemporalAdjusters.lastDayOfYear()).plusDays(1).minusNanos(1);
  261 + timeRanges.add(new LocalDateTime[]{startTime, endOfYear});
  262 + startTime = endOfYear.plusNanos(1);
  263 + }
  264 + break;
  265 + default:
  266 + throw new IllegalArgumentException("Invalid interval: " + interval);
  267 + }
  268 + // 3. 兜底,最后一个时间,需要保持在 endTime 之前
  269 + LocalDateTime[] lastTimeRange = CollUtil.getLast(timeRanges);
  270 + if (lastTimeRange != null) {
  271 + lastTimeRange[1] = endTime;
  272 + }
  273 + return timeRanges;
  274 + }
  275 +
  276 + /**
  277 + * 格式化时间范围
  278 + *
  279 + * @param startTime 开始时间
  280 + * @param endTime 结束时间
  281 + * @param interval 时间间隔
  282 + * @return 时间范围
  283 + */
  284 + public static String formatDateRange(LocalDateTime startTime, LocalDateTime endTime, Integer interval) {
  285 + // 1. 找到枚举
  286 + DateIntervalEnum intervalEnum = DateIntervalEnum.valueOf(interval);
  287 + Assert.notNull(intervalEnum, "interval({}} 找不到对应的枚举", interval);
  288 +
  289 + // 2. 循环,生成时间范围
  290 + switch (intervalEnum) {
  291 + case DateIntervalEnum.DAY:
  292 + return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN);
  293 + case DateIntervalEnum.WEEK:
  294 + return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN)
  295 + + StrUtil.format("(第 {} 周)", LocalDateTimeUtil.weekOfYear(startTime));
  296 + case DateIntervalEnum.MONTH:
  297 + return LocalDateTimeUtil.format(startTime, DatePattern.NORM_MONTH_PATTERN);
  298 + case DateIntervalEnum.QUARTER:
  299 + return StrUtil.format("{}-Q{}", startTime.getYear(), getQuarterOfYear(startTime));
  300 + case DateIntervalEnum.YEAR:
  301 + return LocalDateTimeUtil.format(startTime, DatePattern.NORM_YEAR_PATTERN);
  302 + default:
  303 + throw new IllegalArgumentException("Invalid interval: " + interval);
  304 + }
  305 + }
  306 +
171 } 307 }
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
@@ -104,6 +104,5 @@ public interface ErrorCodeConstants { @@ -104,6 +104,5 @@ public interface ErrorCodeConstants {
104 ErrorCode FOLLOW_UP_RECORD_DELETE_DENIED = new ErrorCode(1_020_013_001, "删除跟进记录失败,原因:没有权限"); 104 ErrorCode FOLLOW_UP_RECORD_DELETE_DENIED = new ErrorCode(1_020_013_001, "删除跟进记录失败,原因:没有权限");
105 105
106 // ========== 数据统计 1_020_014_000 ========== 106 // ========== 数据统计 1_020_014_000 ==========
107 - ErrorCode STATISTICS_CUSTOMER_TIMES_NOT_SET = new ErrorCode(1_020_014_000, "自定义时间间隔,必须输入时间区间");  
108 107
109 } 108 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http
1 # == 1. 客户总量分析 == 1 # == 1. 客户总量分析 ==
2 ### 1.1 客户总量分析(按日) 2 ### 1.1 客户总量分析(按日)
3 -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 3 +GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-date?deptId=100&interval=1&times[0]=2024-01-01 00:00:00&times[1]=2024-01-29 23:59:59
4 Authorization: Bearer {{token}} 4 Authorization: Bearer {{token}}
5 tenant-id: {{adminTenentId}} 5 tenant-id: {{adminTenentId}}
6 6
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java
@@ -40,25 +40,25 @@ public class CrmStatisticsCustomerController { @@ -40,25 +40,25 @@ public class CrmStatisticsCustomerController {
40 return success(customerService.getCustomerSummaryByUser(reqVO)); 40 return success(customerService.getCustomerSummaryByUser(reqVO));
41 } 41 }
42 42
43 - @GetMapping("/get-followup-summary-by-date") 43 + @GetMapping("/get-follow-up-summary-by-date")
44 @Operation(summary = "获取客户跟进次数分析(按日期)") 44 @Operation(summary = "获取客户跟进次数分析(按日期)")
45 @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") 45 @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")
46 - public CommonResult<List<CrmStatisticsFollowupSummaryByDateRespVO>> getFollowupSummaryByDate(@Valid CrmStatisticsCustomerReqVO reqVO) {  
47 - return success(customerService.getFollowupSummaryByDate(reqVO)); 46 + public CommonResult<List<CrmStatisticsFollowUpSummaryByDateRespVO>> getFollowupSummaryByDate(@Valid CrmStatisticsCustomerReqVO reqVO) {
  47 + return success(customerService.getFollowUpSummaryByDate(reqVO));
48 } 48 }
49 49
50 - @GetMapping("/get-followup-summary-by-user") 50 + @GetMapping("/get-follow-up-summary-by-user")
51 @Operation(summary = "获取客户跟进次数分析(按用户)") 51 @Operation(summary = "获取客户跟进次数分析(按用户)")
52 @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") 52 @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")
53 - public CommonResult<List<CrmStatisticsFollowupSummaryByUserRespVO>> getFollowupSummaryByUser(@Valid CrmStatisticsCustomerReqVO reqVO) {  
54 - return success(customerService.getFollowupSummaryByUser(reqVO)); 53 + public CommonResult<List<CrmStatisticsFollowUpSummaryByUserRespVO>> getFollowUpSummaryByUser(@Valid CrmStatisticsCustomerReqVO reqVO) {
  54 + return success(customerService.getFollowUpSummaryByUser(reqVO));
55 } 55 }
56 56
57 - @GetMapping("/get-followup-summary-by-type") 57 + @GetMapping("/get-follow-up-summary-by-type")
58 @Operation(summary = "获取客户跟进次数分析(按类型)") 58 @Operation(summary = "获取客户跟进次数分析(按类型)")
59 @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") 59 @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")
60 - public CommonResult<List<CrmStatisticsFollowupSummaryByTypeRespVO>> getFollowupSummaryByType(@Valid CrmStatisticsCustomerReqVO reqVO) {  
61 - return success(customerService.getFollowupSummaryByType(reqVO)); 60 + public CommonResult<List<CrmStatisticsFollowUpSummaryByTypeRespVO>> getFollowUpSummaryByType(@Valid CrmStatisticsCustomerReqVO reqVO) {
  61 + return success(customerService.getFollowUpSummaryByType(reqVO));
62 } 62 }
63 63
64 @GetMapping("/get-contract-summary") 64 @GetMapping("/get-contract-summary")
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerByUserBaseRespVO.java
1 package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; 1 package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;
2 2
3 -import com.fasterxml.jackson.annotation.JsonIgnore;  
4 import io.swagger.v3.oas.annotations.media.Schema; 3 import io.swagger.v3.oas.annotations.media.Schema;
5 import lombok.Data; 4 import lombok.Data;
6 5
@@ -13,7 +12,6 @@ import lombok.Data; @@ -13,7 +12,6 @@ import lombok.Data;
13 public class CrmStatisticsCustomerByUserBaseRespVO { 12 public class CrmStatisticsCustomerByUserBaseRespVO {
14 13
15 @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 14 @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
16 - @JsonIgnore  
17 private Long ownerUserId; 15 private Long ownerUserId;
18 16
19 @Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") 17 @Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerContractSummaryRespVO.java
1 package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; 1 package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;
2 2
3 3
4 -import com.fasterxml.jackson.annotation.JsonFormat;  
5 -import com.fasterxml.jackson.annotation.JsonIgnore;  
6 import io.swagger.v3.oas.annotations.media.Schema; 4 import io.swagger.v3.oas.annotations.media.Schema;
7 import lombok.Data; 5 import lombok.Data;
8 6
9 import java.math.BigDecimal; 7 import java.math.BigDecimal;
10 -import java.time.LocalDate;  
11 import java.time.LocalDateTime; 8 import java.time.LocalDateTime;
12 9
13 -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;  
14 -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;  
15 -  
16 @Schema(description = "管理后台 - CRM 客户转化率分析 VO") 10 @Schema(description = "管理后台 - CRM 客户转化率分析 VO")
17 @Data 11 @Data
18 public class CrmStatisticsCustomerContractSummaryRespVO { 12 public class CrmStatisticsCustomerContractSummaryRespVO {
@@ -29,31 +23,19 @@ public class CrmStatisticsCustomerContractSummaryRespVO { @@ -29,31 +23,19 @@ public class CrmStatisticsCustomerContractSummaryRespVO {
29 @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1200.00") 23 @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1200.00")
30 private BigDecimal receivablePrice; 24 private BigDecimal receivablePrice;
31 25
32 - @Schema(description = "客户行业ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")  
33 - @JsonIgnore  
34 - private String industryId;  
35 -  
36 - @Schema(description = "客户行业", requiredMode = Schema.RequiredMode.REQUIRED, example = "金融")  
37 - private String industryName;  
38 -  
39 - @Schema(description = "客户来源ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")  
40 - @JsonIgnore  
41 - private String source; 26 + @Schema(description = "客户行业编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  27 + private Integer industryId;
42 28
43 - @Schema(description = "客户来源", requiredMode = Schema.RequiredMode.REQUIRED, example = "外呼")  
44 - private String sourceName; 29 + @Schema(description = "客户来源编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  30 + private Integer source;
45 31
46 @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 32 @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
47 - @JsonIgnore  
48 private Long ownerUserId; 33 private Long ownerUserId;
49 -  
50 @Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") 34 @Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
51 private String ownerUserName; 35 private String ownerUserName;
52 36
53 @Schema(description = "创建人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") 37 @Schema(description = "创建人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
54 - @JsonIgnore  
55 - private String creatorUserId;  
56 - 38 + private String creator;
57 @Schema(description = "创建人", requiredMode = Schema.RequiredMode.REQUIRED, example = "源码") 39 @Schema(description = "创建人", requiredMode = Schema.RequiredMode.REQUIRED, example = "源码")
58 private String creatorUserName; 40 private String creatorUserName;
59 41
@@ -61,7 +43,6 @@ public class CrmStatisticsCustomerContractSummaryRespVO { @@ -61,7 +43,6 @@ public class CrmStatisticsCustomerContractSummaryRespVO {
61 private LocalDateTime createTime; 43 private LocalDateTime createTime;
62 44
63 @Schema(description = "下单日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02 00:00:00") 45 @Schema(description = "下单日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02 00:00:00")
64 - @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY, timezone = TIME_ZONE_DEFAULT)  
65 - private LocalDate orderDate; 46 + private LocalDateTime orderDate;
66 47
67 } 48 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByDateRespVO.java
@@ -11,6 +11,6 @@ public class CrmStatisticsCustomerDealCycleByDateRespVO { @@ -11,6 +11,6 @@ public class CrmStatisticsCustomerDealCycleByDateRespVO {
11 private String time; 11 private String time;
12 12
13 @Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0") 13 @Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0")
14 - private Double customerDealCycle = 0.0; 14 + private Double customerDealCycle;
15 15
16 } 16 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByUserRespVO.java
@@ -8,9 +8,9 @@ import lombok.Data; @@ -8,9 +8,9 @@ import lombok.Data;
8 public class CrmStatisticsCustomerDealCycleByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO { 8 public class CrmStatisticsCustomerDealCycleByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO {
9 9
10 @Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0") 10 @Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0")
11 - private Double customerDealCycle = 0.0; 11 + private Double customerDealCycle;
12 12
13 @Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 13 @Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
14 - private Integer customerDealCount = 0; 14 + private Integer customerDealCount;
15 15
16 } 16 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java
@@ -30,12 +30,12 @@ public class CrmStatisticsCustomerReqVO { @@ -30,12 +30,12 @@ public class CrmStatisticsCustomerReqVO {
30 * userIds 目前不用前端传递,目前是方便后端通过 deptId 读取编号后,设置回来 30 * userIds 目前不用前端传递,目前是方便后端通过 deptId 读取编号后,设置回来
31 * 后续,可能会支持选择部分用户进行查询 31 * 后续,可能会支持选择部分用户进行查询
32 */ 32 */
33 - @Schema(description = "负责人用户 id 集合", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2") 33 + @Schema(description = "负责人用户 id 集合", hidden = true, example = "2")
34 private List<Long> userIds; 34 private List<Long> userIds;
35 35
36 @Schema(description = "时间间隔类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 36 @Schema(description = "时间间隔类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
37 @InEnum(value = DateIntervalEnum.class, message = "时间间隔类型,必须是 {value}") 37 @InEnum(value = DateIntervalEnum.class, message = "时间间隔类型,必须是 {value}")
38 - private Integer intervalType; 38 + private Integer interval;
39 39
40 /** 40 /**
41 * 前端如果选择自定义时间, 那么前端传递起始-终止时间, 如果选择其他时间间隔类型, 则由后台计算起始-终止时间 41 * 前端如果选择自定义时间, 那么前端传递起始-终止时间, 如果选择其他时间间隔类型, 则由后台计算起始-终止时间
@@ -49,7 +49,8 @@ public class CrmStatisticsCustomerReqVO { @@ -49,7 +49,8 @@ public class CrmStatisticsCustomerReqVO {
49 * group by DATE_FORMAT(field, #{dateFormat}) 49 * group by DATE_FORMAT(field, #{dateFormat})
50 * 非前端传递, 由Service计算后传递给Mapper的参数 50 * 非前端传递, 由Service计算后传递给Mapper的参数
51 */ 51 */
52 - @Schema(description = "Group By 日期格式", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "%Y%m") 52 + @Deprecated
  53 + @Schema(description = "Group By 日期格式", hidden = true, example = "%Y%m")
53 private String sqlDateFormat; 54 private String sqlDateFormat;
54 55
55 } 56 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByDateRespVO.java
@@ -11,9 +11,9 @@ public class CrmStatisticsCustomerSummaryByDateRespVO { @@ -11,9 +11,9 @@ public class CrmStatisticsCustomerSummaryByDateRespVO {
11 private String time; 11 private String time;
12 12
13 @Schema(description = "新建客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 13 @Schema(description = "新建客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
14 - private Integer customerCreateCount = 0; 14 + private Integer customerCreateCount;
15 15
16 @Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 16 @Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
17 - private Integer customerDealCount = 0; 17 + private Integer customerDealCount;
18 18
19 } 19 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByUserRespVO.java
@@ -10,15 +10,15 @@ import java.math.BigDecimal; @@ -10,15 +10,15 @@ import java.math.BigDecimal;
10 public class CrmStatisticsCustomerSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO { 10 public class CrmStatisticsCustomerSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO {
11 11
12 @Schema(description = "新建客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 12 @Schema(description = "新建客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
13 - private Integer customerCreateCount = 0; 13 + private Integer customerCreateCount;
14 14
15 @Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 15 @Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
16 - private Integer customerDealCount = 0; 16 + private Integer customerDealCount;
17 17
18 @Schema(description = "合同总金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") 18 @Schema(description = "合同总金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
19 - private BigDecimal contractPrice = BigDecimal.ZERO; 19 + private BigDecimal contractPrice;
20 20
21 @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") 21 @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
22 - private BigDecimal receivablePrice = BigDecimal.ZERO; 22 + private BigDecimal receivablePrice;
23 23
24 } 24 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByDateRespVO.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowUpSummaryByDateRespVO.java
@@ -5,15 +5,15 @@ import lombok.Data; @@ -5,15 +5,15 @@ import lombok.Data;
5 5
6 @Schema(description = "管理后台 - CRM 跟进次数分析(按日期) VO") 6 @Schema(description = "管理后台 - CRM 跟进次数分析(按日期) VO")
7 @Data 7 @Data
8 -public class CrmStatisticsFollowupSummaryByDateRespVO { 8 +public class CrmStatisticsFollowUpSummaryByDateRespVO {
9 9
10 @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401") 10 @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401")
11 private String time; 11 private String time;
12 12
13 @Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 13 @Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
14 - private Integer followupRecordCount = 0; 14 + private Integer followUpRecordCount;
15 15
16 @Schema(description = "跟进客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 16 @Schema(description = "跟进客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
17 - private Integer followupCustomerCount = 0; 17 + private Integer followUpCustomerCount;
18 18
19 } 19 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByTypeRespVO.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowUpSummaryByTypeRespVO.java
@@ -6,12 +6,12 @@ import lombok.Data; @@ -6,12 +6,12 @@ import lombok.Data;
6 6
7 @Schema(description = "管理后台 - CRM 跟进次数分析(按类型) VO") 7 @Schema(description = "管理后台 - CRM 跟进次数分析(按类型) VO")
8 @Data 8 @Data
9 -public class CrmStatisticsFollowupSummaryByTypeRespVO { 9 +public class CrmStatisticsFollowUpSummaryByTypeRespVO {
10 10
11 @Schema(description = "跟进类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 11 @Schema(description = "跟进类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
12 - private String followupType; 12 + private Integer followUpType;
13 13
14 @Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 14 @Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
15 - private Integer followupRecordCount = 0; 15 + private Integer followUpRecordCount;
16 16
17 } 17 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByUserRespVO.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowUpSummaryByUserRespVO.java
@@ -5,12 +5,12 @@ import lombok.Data; @@ -5,12 +5,12 @@ import lombok.Data;
5 5
6 @Schema(description = "管理后台 - CRM 跟进次数分析(按用户) VO") 6 @Schema(description = "管理后台 - CRM 跟进次数分析(按用户) VO")
7 @Data 7 @Data
8 -public class CrmStatisticsFollowupSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO { 8 +public class CrmStatisticsFollowUpSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO {
9 9
10 @Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 10 @Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
11 - private Integer followupRecordCount = 0; 11 + private Integer followUpRecordCount;
12 12
13 @Schema(description = "跟进客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 13 @Schema(description = "跟进客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
14 - private Integer followupCustomerCount = 0; 14 + private Integer followUpCustomerCount;
15 15
16 } 16 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/rank/CrmStatisticsRankRespVO.java
@@ -17,6 +17,7 @@ public class CrmStatisticsRankRespVO { @@ -17,6 +17,7 @@ public class CrmStatisticsRankRespVO {
17 @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 17 @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
18 private String deptName; 18 private String deptName;
19 19
  20 + // TODO @芋艿:需要改下,金额是 bigdecimal
20 /** 21 /**
21 * 数量是个特别“抽象”的概念,在不同排行下,代表不同含义 22 * 数量是个特别“抽象”的概念,在不同排行下,代表不同含义
22 * <p> 23 * <p>
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java
@@ -13,32 +13,32 @@ import java.util.List; @@ -13,32 +13,32 @@ import java.util.List;
13 @Mapper 13 @Mapper
14 public interface CrmStatisticsCustomerMapper { 14 public interface CrmStatisticsCustomerMapper {
15 15
16 - List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerCreateCountGroupByDate(CrmStatisticsCustomerReqVO reqVO); // 已经 review 16 + List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerCreateCountGroupByDate(CrmStatisticsCustomerReqVO reqVO);
17 17
18 - List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerDealCountGroupByDate(CrmStatisticsCustomerReqVO reqVO); // 已经 review 18 + List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerDealCountGroupByDate(CrmStatisticsCustomerReqVO reqVO);
19 19
20 - List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerCreateCountGroupByUser(CrmStatisticsCustomerReqVO reqVO); // 已经 review 20 + List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerCreateCountGroupByUser(CrmStatisticsCustomerReqVO reqVO);
21 21
22 - List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerDealCountGroupByUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); // 已经 review 22 + List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerDealCountGroupByUser(CrmStatisticsCustomerReqVO reqVO);
23 23
24 - List<CrmStatisticsCustomerSummaryByUserRespVO> selectContractPriceGroupByUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); // 已经 review 24 + List<CrmStatisticsCustomerSummaryByUserRespVO> selectContractPriceGroupByUser(CrmStatisticsCustomerReqVO reqVO);
25 25
26 - List<CrmStatisticsCustomerSummaryByUserRespVO> selectReceivablePriceGroupByUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); // 已经 review 26 + List<CrmStatisticsCustomerSummaryByUserRespVO> selectReceivablePriceGroupByUser(CrmStatisticsCustomerReqVO reqVO);
27 27
28 - List<CrmStatisticsFollowupSummaryByDateRespVO> selectFollowupRecordCountGroupByDate(CrmStatisticsCustomerReqVO reqVO); 28 + List<CrmStatisticsFollowUpSummaryByDateRespVO> selectFollowUpRecordCountGroupByDate(CrmStatisticsCustomerReqVO reqVO);
29 29
30 - List<CrmStatisticsFollowupSummaryByDateRespVO> selectFollowupCustomerCountGroupByDate(CrmStatisticsCustomerReqVO reqVO); 30 + List<CrmStatisticsFollowUpSummaryByDateRespVO> selectFollowUpCustomerCountGroupByDate(CrmStatisticsCustomerReqVO reqVO);
31 31
32 - List<CrmStatisticsFollowupSummaryByUserRespVO> selectFollowupRecordCountGroupByUser(CrmStatisticsCustomerReqVO reqVO); 32 + List<CrmStatisticsFollowUpSummaryByUserRespVO> selectFollowUpRecordCountGroupByUser(CrmStatisticsCustomerReqVO reqVO);
33 33
34 - List<CrmStatisticsFollowupSummaryByUserRespVO> selectFollowupCustomerCountGroupByUser(CrmStatisticsCustomerReqVO reqVO); 34 + List<CrmStatisticsFollowUpSummaryByUserRespVO> selectFollowUpCustomerCountGroupByUser(CrmStatisticsCustomerReqVO reqVO);
35 35
36 List<CrmStatisticsCustomerContractSummaryRespVO> selectContractSummary(CrmStatisticsCustomerReqVO reqVO); 36 List<CrmStatisticsCustomerContractSummaryRespVO> selectContractSummary(CrmStatisticsCustomerReqVO reqVO);
37 37
38 - List<CrmStatisticsFollowupSummaryByTypeRespVO> selectFollowupRecordCountGroupByType(CrmStatisticsCustomerReqVO reqVO); 38 + List<CrmStatisticsFollowUpSummaryByTypeRespVO> selectFollowUpRecordCountGroupByType(CrmStatisticsCustomerReqVO reqVO);
39 39
40 List<CrmStatisticsCustomerDealCycleByDateRespVO> selectCustomerDealCycleGroupByDate(CrmStatisticsCustomerReqVO reqVO); 40 List<CrmStatisticsCustomerDealCycleByDateRespVO> selectCustomerDealCycleGroupByDate(CrmStatisticsCustomerReqVO reqVO);
41 41
42 - List<CrmStatisticsCustomerDealCycleByUserRespVO> selectCustomerDealCycleGroupByUser(CrmStatisticsCustomerReqVO reqVO); 42 + List<CrmStatisticsCustomerDealCycleByUserRespVO> selectCustomerDealCycleGroupByUser(CrmStatisticsCustomerReqVO reqVO); // TODO
43 43
44 } 44 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java
@@ -27,14 +27,13 @@ public interface CrmStatisticsCustomerService { @@ -27,14 +27,13 @@ public interface CrmStatisticsCustomerService {
27 */ 27 */
28 List<CrmStatisticsCustomerSummaryByUserRespVO> getCustomerSummaryByUser(CrmStatisticsCustomerReqVO reqVO); 28 List<CrmStatisticsCustomerSummaryByUserRespVO> getCustomerSummaryByUser(CrmStatisticsCustomerReqVO reqVO);
29 29
30 -  
31 /** 30 /**
32 * 跟进次数分析(按日期) 31 * 跟进次数分析(按日期)
33 * 32 *
34 * @param reqVO 请求参数 33 * @param reqVO 请求参数
35 * @return 统计数据 34 * @return 统计数据
36 */ 35 */
37 - List<CrmStatisticsFollowupSummaryByDateRespVO> getFollowupSummaryByDate(CrmStatisticsCustomerReqVO reqVO); 36 + List<CrmStatisticsFollowUpSummaryByDateRespVO> getFollowUpSummaryByDate(CrmStatisticsCustomerReqVO reqVO);
38 37
39 /** 38 /**
40 * 跟进次数分析(按用户) 39 * 跟进次数分析(按用户)
@@ -42,7 +41,7 @@ public interface CrmStatisticsCustomerService { @@ -42,7 +41,7 @@ public interface CrmStatisticsCustomerService {
42 * @param reqVO 请求参数 41 * @param reqVO 请求参数
43 * @return 统计数据 42 * @return 统计数据
44 */ 43 */
45 - List<CrmStatisticsFollowupSummaryByUserRespVO> getFollowupSummaryByUser(CrmStatisticsCustomerReqVO reqVO); 44 + List<CrmStatisticsFollowUpSummaryByUserRespVO> getFollowUpSummaryByUser(CrmStatisticsCustomerReqVO reqVO);
46 45
47 /** 46 /**
48 * 客户跟进次数分析(按类型) 47 * 客户跟进次数分析(按类型)
@@ -50,8 +49,7 @@ public interface CrmStatisticsCustomerService { @@ -50,8 +49,7 @@ public interface CrmStatisticsCustomerService {
50 * @param reqVO 请求参数 49 * @param reqVO 请求参数
51 * @return 统计数据 50 * @return 统计数据
52 */ 51 */
53 - List<CrmStatisticsFollowupSummaryByTypeRespVO> getFollowupSummaryByType(CrmStatisticsCustomerReqVO reqVO);  
54 - 52 + List<CrmStatisticsFollowUpSummaryByTypeRespVO> getFollowUpSummaryByType(CrmStatisticsCustomerReqVO reqVO);
55 53
56 /** 54 /**
57 * 获取合同摘要信息(客户转化率页面) 55 * 获取合同摘要信息(客户转化率页面)
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java
1 package cn.iocoder.yudao.module.crm.service.statistics; 1 package cn.iocoder.yudao.module.crm.service.statistics;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 -import cn.hutool.core.date.DateTime;  
5 -import cn.hutool.core.date.DateUtil;  
6 -import cn.hutool.core.date.LocalDateTimeUtil;  
7 import cn.hutool.core.util.ObjUtil; 4 import cn.hutool.core.util.ObjUtil;
8 -import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum;  
9 -import cn.iocoder.yudao.framework.common.util.collection.MapUtils; 5 +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
10 import cn.iocoder.yudao.framework.common.util.number.NumberUtils; 6 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
11 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; 7 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*;
12 import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper; 8 import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper;
13 import cn.iocoder.yudao.module.system.api.dept.DeptApi; 9 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
14 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; 10 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
15 -import cn.iocoder.yudao.module.system.api.dict.DictDataApi;  
16 -import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO;  
17 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 11 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
18 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 12 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
19 import jakarta.annotation.Resource; 13 import jakarta.annotation.Resource;
@@ -27,10 +21,8 @@ import java.util.List; @@ -27,10 +21,8 @@ import java.util.List;
27 import java.util.Map; 21 import java.util.Map;
28 import java.util.stream.Stream; 22 import java.util.stream.Stream;
29 23
30 -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;  
31 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; 24 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
32 -import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*;  
33 -import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.STATISTICS_CUSTOMER_TIMES_NOT_SET; 25 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
34 26
35 /** 27 /**
36 * CRM 客户分析 Service 实现类 28 * CRM 客户分析 Service 实现类
@@ -41,13 +33,6 @@ import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.STATISTICS_CU @@ -41,13 +33,6 @@ import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.STATISTICS_CU
41 @Validated 33 @Validated
42 public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerService { 34 public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerService {
43 35
44 - private static final String SQL_DATE_FORMAT_BY_MONTH = "%Y%m";  
45 - private static final String SQL_DATE_FORMAT_BY_DAY = "%Y%m%d";  
46 -  
47 - private static final String TIME_FORMAT_BY_MONTH = "yyyyMM";  
48 - private static final String TIME_FORMAT_BY_DAY = "yyyyMMdd";  
49 -  
50 -  
51 @Resource 36 @Resource
52 private CrmStatisticsCustomerMapper customerMapper; 37 private CrmStatisticsCustomerMapper customerMapper;
53 38
@@ -55,283 +40,211 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe @@ -55,283 +40,211 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
55 private AdminUserApi adminUserApi; 40 private AdminUserApi adminUserApi;
56 @Resource 41 @Resource
57 private DeptApi deptApi; 42 private DeptApi deptApi;
58 - @Resource  
59 - private DictDataApi dictDataApi;  
60 43
61 @Override 44 @Override
62 public List<CrmStatisticsCustomerSummaryByDateRespVO> getCustomerSummaryByDate(CrmStatisticsCustomerReqVO reqVO) { 45 public List<CrmStatisticsCustomerSummaryByDateRespVO> getCustomerSummaryByDate(CrmStatisticsCustomerReqVO reqVO) {
63 // 1. 获得用户编号数组 46 // 1. 获得用户编号数组
64 - List<Long> userIds = getUserIds(reqVO);  
65 - if (CollUtil.isEmpty(userIds)) { 47 + reqVO.setUserIds(getUserIds(reqVO));
  48 + if (CollUtil.isEmpty(reqVO.getUserIds())) {
66 return Collections.emptyList(); 49 return Collections.emptyList();
67 } 50 }
68 - reqVO.setUserIds(userIds);  
69 -  
70 - // 2. 获取分项统计数据  
71 - initParams(reqVO);  
72 - List<CrmStatisticsCustomerSummaryByDateRespVO> customerCreateCountVoList = customerMapper.selectCustomerCreateCountGroupByDate(reqVO);  
73 - List<CrmStatisticsCustomerSummaryByDateRespVO> customerDealCountVoList = customerMapper.selectCustomerDealCountGroupByDate(reqVO);  
74 -  
75 - // 3. 合并数据  
76 - List<String> times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]);  
77 - Map<String, Integer> customerCreateCountMap = convertMap(customerCreateCountVoList,  
78 - CrmStatisticsCustomerSummaryByDateRespVO::getTime,  
79 - CrmStatisticsCustomerSummaryByDateRespVO::getCustomerCreateCount);  
80 - Map<String, Integer> customerDealCountMap = convertMap(customerDealCountVoList,  
81 - CrmStatisticsCustomerSummaryByDateRespVO::getTime,  
82 - CrmStatisticsCustomerSummaryByDateRespVO::getCustomerDealCount);  
83 - List<CrmStatisticsCustomerSummaryByDateRespVO> respVoList = convertList(times,  
84 - time -> new CrmStatisticsCustomerSummaryByDateRespVO()  
85 - .setTime(time)  
86 - .setCustomerCreateCount(customerCreateCountMap.getOrDefault(time, 0))  
87 - .setCustomerDealCount(customerDealCountMap.getOrDefault(time, 0)));  
88 -  
89 - return respVoList; 51 +
  52 + // 2. 按天统计,获取分项统计数据
  53 + List<CrmStatisticsCustomerSummaryByDateRespVO> customerCreateCountList = customerMapper.selectCustomerCreateCountGroupByDate(reqVO);
  54 + List<CrmStatisticsCustomerSummaryByDateRespVO> customerDealCountList = customerMapper.selectCustomerDealCountGroupByDate(reqVO);
  55 +
  56 + // 3. 按照日期间隔,合并数据
  57 + List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval());
  58 + return convertList(timeRanges, times -> {
  59 + Integer customerCreateCount = customerCreateCountList.stream()
  60 + .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))
  61 + .mapToInt(CrmStatisticsCustomerSummaryByDateRespVO::getCustomerCreateCount).sum();
  62 + Integer customerDealCount = customerDealCountList.stream()
  63 + .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))
  64 + .mapToInt(CrmStatisticsCustomerSummaryByDateRespVO::getCustomerDealCount).sum();
  65 + return new CrmStatisticsCustomerSummaryByDateRespVO()
  66 + .setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval()))
  67 + .setCustomerCreateCount(customerCreateCount).setCustomerDealCount(customerDealCount);
  68 + });
90 } 69 }
91 70
92 @Override 71 @Override
93 public List<CrmStatisticsCustomerSummaryByUserRespVO> getCustomerSummaryByUser(CrmStatisticsCustomerReqVO reqVO) { 72 public List<CrmStatisticsCustomerSummaryByUserRespVO> getCustomerSummaryByUser(CrmStatisticsCustomerReqVO reqVO) {
94 // 1. 获得用户编号数组 73 // 1. 获得用户编号数组
95 - List<Long> userIds = getUserIds(reqVO);  
96 - if (CollUtil.isEmpty(userIds)) { 74 + reqVO.setUserIds(getUserIds(reqVO));
  75 + if (CollUtil.isEmpty(reqVO.getUserIds())) {
97 return Collections.emptyList(); 76 return Collections.emptyList();
98 } 77 }
99 - reqVO.setUserIds(userIds);  
100 -  
101 - // 2. 获取分项统计数据  
102 - initParams(reqVO);  
103 - List<CrmStatisticsCustomerSummaryByUserRespVO> customerCreateCount = customerMapper.selectCustomerCreateCountGroupByUser(reqVO);  
104 - List<CrmStatisticsCustomerSummaryByUserRespVO> customerDealCount = customerMapper.selectCustomerDealCountGroupByUser(reqVO);  
105 - List<CrmStatisticsCustomerSummaryByUserRespVO> contractPrice = customerMapper.selectContractPriceGroupByUser(reqVO);  
106 - List<CrmStatisticsCustomerSummaryByUserRespVO> receivablePrice = customerMapper.selectReceivablePriceGroupByUser(reqVO);  
107 -  
108 - // 3. 合并统计数据  
109 - Map<Long, Integer> customerCreateCountMap = convertMap(customerCreateCount,  
110 - CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId,  
111 - CrmStatisticsCustomerSummaryByUserRespVO::getCustomerCreateCount);  
112 - Map<Long, Integer> customerDealCountMap = convertMap(customerDealCount,  
113 - CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId,  
114 - CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount);  
115 - Map<Long, BigDecimal> contractPriceMap = convertMap(contractPrice,  
116 - CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId,  
117 - CrmStatisticsCustomerSummaryByUserRespVO::getContractPrice);  
118 - Map<Long, BigDecimal> receivablePriceMap = convertMap(receivablePrice,  
119 - CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId,  
120 - CrmStatisticsCustomerSummaryByUserRespVO::getReceivablePrice);  
121 - List<CrmStatisticsCustomerSummaryByUserRespVO> respVoList = convertList(userIds, userId -> {  
122 - CrmStatisticsCustomerSummaryByUserRespVO vo = new CrmStatisticsCustomerSummaryByUserRespVO();  
123 - // ownerUserId 为基类属性  
124 - vo.setOwnerUserId(userId);  
125 - vo.setCustomerCreateCount(customerCreateCountMap.getOrDefault(userId, 0))  
126 - .setCustomerDealCount(customerDealCountMap.getOrDefault(userId, 0))  
127 - .setContractPrice(contractPriceMap.getOrDefault(userId, BigDecimal.ZERO))  
128 - .setReceivablePrice(receivablePriceMap.getOrDefault(userId, BigDecimal.ZERO));  
129 - return vo;  
130 - });  
131 -  
132 - // 4. 拼接用户信息  
133 - appendUserInfo(respVoList);  
134 78
135 - return respVoList; 79 + // 2. 按用户统计,获取分项统计数据
  80 + List<CrmStatisticsCustomerSummaryByUserRespVO> customerCreateCountList = customerMapper.selectCustomerCreateCountGroupByUser(reqVO);
  81 + List<CrmStatisticsCustomerSummaryByUserRespVO> customerDealCountList = customerMapper.selectCustomerDealCountGroupByUser(reqVO);
  82 + List<CrmStatisticsCustomerSummaryByUserRespVO> contractPriceList = customerMapper.selectContractPriceGroupByUser(reqVO);
  83 + List<CrmStatisticsCustomerSummaryByUserRespVO> receivablePriceList = customerMapper.selectReceivablePriceGroupByUser(reqVO);
  84 +
  85 + // 3.1 按照用户,合并统计数据
  86 + List<CrmStatisticsCustomerSummaryByUserRespVO> summaryList = convertList(reqVO.getUserIds(), userId -> {
  87 + Integer customerCreateCount = customerCreateCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))
  88 + .mapToInt(CrmStatisticsCustomerSummaryByUserRespVO::getCustomerCreateCount).sum();
  89 + Integer customerDealCount = customerDealCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))
  90 + .mapToInt(CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount).sum();
  91 + BigDecimal contractPrice = contractPriceList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))
  92 + .reduce(BigDecimal.ZERO, (sum, vo) -> sum.add(vo.getContractPrice()), BigDecimal::add);
  93 + BigDecimal receivablePrice = receivablePriceList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))
  94 + .reduce(BigDecimal.ZERO, (sum, vo) -> sum.add(vo.getReceivablePrice()), BigDecimal::add);
  95 + return (CrmStatisticsCustomerSummaryByUserRespVO) new CrmStatisticsCustomerSummaryByUserRespVO()
  96 + .setCustomerCreateCount(customerCreateCount).setCustomerDealCount(customerDealCount)
  97 + .setContractPrice(contractPrice).setReceivablePrice(receivablePrice).setOwnerUserId(userId);
  98 + });
  99 + // 3.2 拼接用户信息
  100 + appendUserInfo(summaryList);
  101 + return summaryList;
136 } 102 }
137 103
138 @Override 104 @Override
139 - public List<CrmStatisticsFollowupSummaryByDateRespVO> getFollowupSummaryByDate(CrmStatisticsCustomerReqVO reqVO) { 105 + public List<CrmStatisticsFollowUpSummaryByDateRespVO> getFollowUpSummaryByDate(CrmStatisticsCustomerReqVO reqVO) {
140 // 1. 获得用户编号数组 106 // 1. 获得用户编号数组
141 - List<Long> userIds = getUserIds(reqVO);  
142 - if (CollUtil.isEmpty(userIds)) { 107 + reqVO.setUserIds(getUserIds(reqVO));
  108 + if (CollUtil.isEmpty(reqVO.getUserIds())) {
143 return Collections.emptyList(); 109 return Collections.emptyList();
144 } 110 }
145 - reqVO.setUserIds(userIds);  
146 -  
147 - // 2. 获取分项统计数据  
148 - initParams(reqVO);  
149 - List<CrmStatisticsFollowupSummaryByDateRespVO> followupRecordCount = customerMapper.selectFollowupRecordCountGroupByDate(reqVO);  
150 - List<CrmStatisticsFollowupSummaryByDateRespVO> followupCustomerCount = customerMapper.selectFollowupCustomerCountGroupByDate(reqVO);  
151 -  
152 - // 3. 合并统计数据  
153 - List<String> times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]);  
154 - Map<String, Integer> followupRecordCountMap = convertMap(followupRecordCount,  
155 - CrmStatisticsFollowupSummaryByDateRespVO::getTime,  
156 - CrmStatisticsFollowupSummaryByDateRespVO::getFollowupRecordCount);  
157 - Map<String, Integer> followupCustomerCountMap = convertMap(followupCustomerCount,  
158 - CrmStatisticsFollowupSummaryByDateRespVO::getTime,  
159 - CrmStatisticsFollowupSummaryByDateRespVO::getFollowupCustomerCount);  
160 - List<CrmStatisticsFollowupSummaryByDateRespVO> respVoList = convertList(times, time ->  
161 - new CrmStatisticsFollowupSummaryByDateRespVO().setTime(time)  
162 - .setFollowupRecordCount(followupRecordCountMap.getOrDefault(time, 0))  
163 - .setFollowupCustomerCount(followupCustomerCountMap.getOrDefault(time, 0))  
164 - );  
165 -  
166 - return respVoList; 111 +
  112 + // 2. 按天统计,获取分项统计数据
  113 + List<CrmStatisticsFollowUpSummaryByDateRespVO> followUpRecordCountList = customerMapper.selectFollowUpRecordCountGroupByDate(reqVO);
  114 + List<CrmStatisticsFollowUpSummaryByDateRespVO> followUpCustomerCountList = customerMapper.selectFollowUpCustomerCountGroupByDate(reqVO);
  115 +
  116 + // 3. 按照时间间隔,合并统计数据
  117 + List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval());
  118 + return convertList(timeRanges, times -> {
  119 + Integer followUpRecordCount = followUpRecordCountList.stream()
  120 + .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))
  121 + .mapToInt(CrmStatisticsFollowUpSummaryByDateRespVO::getFollowUpRecordCount).sum();
  122 + Integer followUpCustomerCount = followUpCustomerCountList.stream()
  123 + .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))
  124 + .mapToInt(CrmStatisticsFollowUpSummaryByDateRespVO::getFollowUpCustomerCount).sum();
  125 + return new CrmStatisticsFollowUpSummaryByDateRespVO()
  126 + .setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval()))
  127 + .setFollowUpCustomerCount(followUpRecordCount).setFollowUpRecordCount(followUpCustomerCount);
  128 + });
167 } 129 }
168 130
169 @Override 131 @Override
170 - public List<CrmStatisticsFollowupSummaryByUserRespVO> getFollowupSummaryByUser(CrmStatisticsCustomerReqVO reqVO) { 132 + public List<CrmStatisticsFollowUpSummaryByUserRespVO> getFollowUpSummaryByUser(CrmStatisticsCustomerReqVO reqVO) {
171 // 1. 获得用户编号数组 133 // 1. 获得用户编号数组
172 - List<Long> userIds = getUserIds(reqVO);  
173 - if (CollUtil.isEmpty(userIds)) { 134 + reqVO.setUserIds(getUserIds(reqVO));
  135 + if (CollUtil.isEmpty(reqVO.getUserIds())) {
174 return Collections.emptyList(); 136 return Collections.emptyList();
175 } 137 }
176 - reqVO.setUserIds(userIds);  
177 -  
178 - // 2. 获取分项统计数据  
179 - initParams(reqVO);  
180 - List<CrmStatisticsFollowupSummaryByUserRespVO> followupRecordCount = customerMapper.selectFollowupRecordCountGroupByUser(reqVO);  
181 - List<CrmStatisticsFollowupSummaryByUserRespVO> followupCustomerCount = customerMapper.selectFollowupCustomerCountGroupByUser(reqVO);  
182 -  
183 - // 3. 合并统计数据  
184 - Map<Long, Integer> followupRecordCountMap = convertMap(followupRecordCount,  
185 - CrmStatisticsFollowupSummaryByUserRespVO::getOwnerUserId,  
186 - CrmStatisticsFollowupSummaryByUserRespVO::getFollowupRecordCount);  
187 - Map<Long, Integer> followupCustomerCountMap = convertMap(followupCustomerCount,  
188 - CrmStatisticsFollowupSummaryByUserRespVO::getOwnerUserId,  
189 - CrmStatisticsFollowupSummaryByUserRespVO::getFollowupCustomerCount);  
190 - List<CrmStatisticsFollowupSummaryByUserRespVO> respVoList = convertList(userIds, userId -> {  
191 - CrmStatisticsFollowupSummaryByUserRespVO vo = new CrmStatisticsFollowupSummaryByUserRespVO()  
192 - .setFollowupRecordCount(followupRecordCountMap.getOrDefault(userId, 0))  
193 - .setFollowupCustomerCount(followupCustomerCountMap.getOrDefault(userId, 0));  
194 - // ownerUserId 为基类属性  
195 - vo.setOwnerUserId(userId);  
196 - return vo;  
197 - });  
198 138
199 - // 4. 拼接用户信息  
200 - appendUserInfo(respVoList);  
201 - return respVoList; 139 + // 2. 按用户统计,获取分项统计数据
  140 + List<CrmStatisticsFollowUpSummaryByUserRespVO> followUpRecordCountList = customerMapper.selectFollowUpRecordCountGroupByUser(reqVO);
  141 + List<CrmStatisticsFollowUpSummaryByUserRespVO> followUpCustomerCountList = customerMapper.selectFollowUpCustomerCountGroupByUser(reqVO);
  142 +
  143 + // 3.1 按照用户,合并统计数据
  144 + List<CrmStatisticsFollowUpSummaryByUserRespVO> summaryList = convertList(reqVO.getUserIds(), userId -> {
  145 + Integer followUpRecordCount = followUpRecordCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))
  146 + .mapToInt(CrmStatisticsFollowUpSummaryByUserRespVO::getFollowUpRecordCount).sum();
  147 + Integer followUpCustomerCount = followUpCustomerCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))
  148 + .mapToInt(CrmStatisticsFollowUpSummaryByUserRespVO::getFollowUpCustomerCount).sum();
  149 + return (CrmStatisticsFollowUpSummaryByUserRespVO) new CrmStatisticsFollowUpSummaryByUserRespVO()
  150 + .setFollowUpCustomerCount(followUpRecordCount).setFollowUpRecordCount(followUpCustomerCount).setOwnerUserId(userId);
  151 + });
  152 + // 3.2 拼接用户信息
  153 + appendUserInfo(summaryList);
  154 + return summaryList;
202 } 155 }
203 156
204 @Override 157 @Override
205 - public List<CrmStatisticsFollowupSummaryByTypeRespVO> getFollowupSummaryByType(CrmStatisticsCustomerReqVO reqVO) { 158 + public List<CrmStatisticsFollowUpSummaryByTypeRespVO> getFollowUpSummaryByType(CrmStatisticsCustomerReqVO reqVO) {
206 // 1. 获得用户编号数组 159 // 1. 获得用户编号数组
207 - List<Long> userIds = getUserIds(reqVO);  
208 - if (CollUtil.isEmpty(userIds)) { 160 + reqVO.setUserIds(getUserIds(reqVO));
  161 + if (CollUtil.isEmpty(reqVO.getUserIds())) {
209 return Collections.emptyList(); 162 return Collections.emptyList();
210 } 163 }
211 - reqVO.setUserIds(userIds);  
212 -  
213 - // 2. 获得排行数据  
214 - initParams(reqVO);  
215 - List<CrmStatisticsFollowupSummaryByTypeRespVO> respVoList = customerMapper.selectFollowupRecordCountGroupByType(reqVO);  
216 -  
217 - // 3. 获取字典数据  
218 - List<DictDataRespDTO> followUpTypes = dictDataApi.getDictDataList(CRM_FOLLOW_UP_TYPE);  
219 - Map<String, String> followUpTypeMap = convertMap(followUpTypes,  
220 - DictDataRespDTO::getValue, DictDataRespDTO::getLabel);  
221 - respVoList.forEach(vo -> {  
222 - vo.setFollowupType(followUpTypeMap.get(vo.getFollowupType()));  
223 - });  
224 164
225 - return respVoList; 165 + // 2. 获得跟进数据
  166 + return customerMapper.selectFollowUpRecordCountGroupByType(reqVO);
226 } 167 }
227 168
228 @Override 169 @Override
229 public List<CrmStatisticsCustomerContractSummaryRespVO> getContractSummary(CrmStatisticsCustomerReqVO reqVO) { 170 public List<CrmStatisticsCustomerContractSummaryRespVO> getContractSummary(CrmStatisticsCustomerReqVO reqVO) {
230 // 1. 获得用户编号数组 171 // 1. 获得用户编号数组
231 - List<Long> userIds = getUserIds(reqVO);  
232 - if (CollUtil.isEmpty(userIds)) { 172 + reqVO.setUserIds(getUserIds(reqVO));
  173 + if (CollUtil.isEmpty(reqVO.getUserIds())) {
233 return Collections.emptyList(); 174 return Collections.emptyList();
234 } 175 }
235 - reqVO.setUserIds(userIds);  
236 -  
237 - // 2. 获取统计数据  
238 - initParams(reqVO);  
239 - List<CrmStatisticsCustomerContractSummaryRespVO> respVoList = customerMapper.selectContractSummary(reqVO);  
240 -  
241 - // 3. 设置 创建人、负责人、行业、来源  
242 - // 3.1 获取客户所属行业  
243 - Map<String, String> industryMap = convertMap(dictDataApi.getDictDataList(CRM_CUSTOMER_INDUSTRY),  
244 - DictDataRespDTO::getValue, DictDataRespDTO::getLabel);  
245 - // 3.2 获取客户来源  
246 - Map<String, String> sourceMap = convertMap(dictDataApi.getDictDataList(CRM_CUSTOMER_SOURCE),  
247 - DictDataRespDTO::getValue, DictDataRespDTO::getLabel);  
248 - // 3.3 获取创建人、负责人列表  
249 - Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSetByFlatMap(respVoList,  
250 - vo -> Stream.of(NumberUtils.parseLong(vo.getCreatorUserId()), vo.getOwnerUserId())));  
251 - // 3.4 设置 创建人、负责人、行业、来源  
252 - respVoList.forEach(vo -> {  
253 - MapUtils.findAndThen(industryMap, vo.getIndustryId(), vo::setIndustryName);  
254 - MapUtils.findAndThen(sourceMap, vo.getSource(), vo::setSourceName);  
255 - MapUtils.findAndThen(userMap, NumberUtils.parseLong(vo.getCreatorUserId()),  
256 - user -> vo.setCreatorUserName(user.getNickname()));  
257 - MapUtils.findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname()));  
258 - });  
259 176
260 - return respVoList; 177 + // 2. 按用户统计,获取统计数据
  178 + List<CrmStatisticsCustomerContractSummaryRespVO> summaryList = customerMapper.selectContractSummary(reqVO);
  179 +
  180 + // 3. 拼接信息
  181 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
  182 + convertSetByFlatMap(summaryList, vo -> Stream.of(NumberUtils.parseLong(vo.getCreator()), vo.getOwnerUserId())));
  183 + summaryList.forEach(vo -> {
  184 + findAndThen(userMap, NumberUtils.parseLong(vo.getCreator()), user -> vo.setCreatorUserName(user.getNickname()));
  185 + findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname()));
  186 + });
  187 + return summaryList;
261 } 188 }
262 189
263 @Override 190 @Override
264 public List<CrmStatisticsCustomerDealCycleByDateRespVO> getCustomerDealCycleByDate(CrmStatisticsCustomerReqVO reqVO) { 191 public List<CrmStatisticsCustomerDealCycleByDateRespVO> getCustomerDealCycleByDate(CrmStatisticsCustomerReqVO reqVO) {
265 // 1. 获得用户编号数组 192 // 1. 获得用户编号数组
266 - List<Long> userIds = getUserIds(reqVO);  
267 - if (CollUtil.isEmpty(userIds)) { 193 + reqVO.setUserIds(getUserIds(reqVO));
  194 + if (CollUtil.isEmpty(reqVO.getUserIds())) {
268 return Collections.emptyList(); 195 return Collections.emptyList();
269 } 196 }
270 - reqVO.setUserIds(userIds);  
271 -  
272 - // 2. 获取分项统计数据  
273 - initParams(reqVO);  
274 - List<CrmStatisticsCustomerDealCycleByDateRespVO> customerDealCycle = customerMapper.selectCustomerDealCycleGroupByDate(reqVO);  
275 -  
276 - // 3. 合并统计数据  
277 - List<String> times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]);  
278 - Map<String, Double> customerDealCycleMap = convertMap(customerDealCycle,  
279 - CrmStatisticsCustomerDealCycleByDateRespVO::getTime,  
280 - CrmStatisticsCustomerDealCycleByDateRespVO::getCustomerDealCycle);  
281 - List<CrmStatisticsCustomerDealCycleByDateRespVO> respVoList = convertList(times, time ->  
282 - new CrmStatisticsCustomerDealCycleByDateRespVO().setTime(time)  
283 - .setCustomerDealCycle(customerDealCycleMap.getOrDefault(time, 0D))  
284 - );  
285 -  
286 - return respVoList; 197 +
  198 + // 2. 按天统计,获取分项统计数据
  199 + List<CrmStatisticsCustomerDealCycleByDateRespVO> customerDealCycleList = customerMapper.selectCustomerDealCycleGroupByDate(reqVO);
  200 +
  201 + // 3. 按照日期间隔,合并统计数据
  202 + List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval());
  203 + return convertList(timeRanges, times -> {
  204 + Double customerDealCycle = customerDealCycleList.stream()
  205 + .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))
  206 + .mapToDouble(CrmStatisticsCustomerDealCycleByDateRespVO::getCustomerDealCycle).sum();
  207 + return new CrmStatisticsCustomerDealCycleByDateRespVO()
  208 + .setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval()))
  209 + .setCustomerDealCycle(customerDealCycle);
  210 + });
287 } 211 }
288 212
289 @Override 213 @Override
290 public List<CrmStatisticsCustomerDealCycleByUserRespVO> getCustomerDealCycleByUser(CrmStatisticsCustomerReqVO reqVO) { 214 public List<CrmStatisticsCustomerDealCycleByUserRespVO> getCustomerDealCycleByUser(CrmStatisticsCustomerReqVO reqVO) {
291 // 1. 获得用户编号数组 215 // 1. 获得用户编号数组
292 - List<Long> userIds = getUserIds(reqVO);  
293 - if (CollUtil.isEmpty(userIds)) { 216 + reqVO.setUserIds(getUserIds(reqVO));
  217 + if (CollUtil.isEmpty(reqVO.getUserIds())) {
294 return Collections.emptyList(); 218 return Collections.emptyList();
295 } 219 }
296 - reqVO.setUserIds(userIds);  
297 -  
298 - // 2. 获取分项统计数据  
299 - initParams(reqVO);  
300 - List<CrmStatisticsCustomerDealCycleByUserRespVO> customerDealCycle = customerMapper.selectCustomerDealCycleGroupByUser(reqVO);  
301 - List<CrmStatisticsCustomerSummaryByUserRespVO> customerDealCount = customerMapper.selectCustomerDealCountGroupByUser(reqVO);  
302 -  
303 - // 3. 合并统计数据  
304 - Map<Long, Double> customerDealCycleMap = convertMap(customerDealCycle,  
305 - CrmStatisticsCustomerDealCycleByUserRespVO::getOwnerUserId,  
306 - CrmStatisticsCustomerDealCycleByUserRespVO::getCustomerDealCycle);  
307 - Map<Long, Integer> customerDealCountMap = convertMap(customerDealCount,  
308 - CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId,  
309 - CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount);  
310 - List<CrmStatisticsCustomerDealCycleByUserRespVO> respVoList = convertList(userIds, userId -> {  
311 - CrmStatisticsCustomerDealCycleByUserRespVO vo = new CrmStatisticsCustomerDealCycleByUserRespVO()  
312 - .setCustomerDealCycle(customerDealCycleMap.getOrDefault(userId, 0.0))  
313 - .setCustomerDealCount(customerDealCountMap.getOrDefault(userId, 0));  
314 - // ownerUserId 为基类属性  
315 - vo.setOwnerUserId(userId);  
316 - return vo;  
317 - });  
318 -  
319 - // 4. 拼接用户信息  
320 - appendUserInfo(respVoList);  
321 220
322 - return respVoList; 221 + // 2. 按用户统计,获取分项统计数据
  222 + List<CrmStatisticsCustomerDealCycleByUserRespVO> customerDealCycleList = customerMapper.selectCustomerDealCycleGroupByUser(reqVO);
  223 + List<CrmStatisticsCustomerSummaryByUserRespVO> customerDealCountList = customerMapper.selectCustomerDealCountGroupByUser(reqVO);
  224 +
  225 + // 3.1 按照用户,合并统计数据
  226 + List<CrmStatisticsCustomerDealCycleByUserRespVO> summaryList = convertList(reqVO.getUserIds(), userId -> {
  227 + Double customerDealCycle = customerDealCycleList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))
  228 + .mapToDouble(CrmStatisticsCustomerDealCycleByUserRespVO::getCustomerDealCycle).sum();
  229 + Integer customerDealCount = customerDealCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))
  230 + .mapToInt(CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount).sum();
  231 + return (CrmStatisticsCustomerDealCycleByUserRespVO) new CrmStatisticsCustomerDealCycleByUserRespVO()
  232 + .setCustomerDealCycle(customerDealCycle).setCustomerDealCount(customerDealCount).setOwnerUserId(userId);
  233 + });
  234 + // 3.2 拼接用户信息
  235 + appendUserInfo(summaryList);
  236 + return summaryList;
323 } 237 }
324 238
325 /** 239 /**
326 * 拼接用户信息(昵称) 240 * 拼接用户信息(昵称)
327 * 241 *
328 - * @param respVoList 统计数据 242 + * @param voList 统计数据
329 */ 243 */
330 - private <T extends CrmStatisticsCustomerByUserBaseRespVO> void appendUserInfo(List<T> respVoList) {  
331 - Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(respVoList,  
332 - CrmStatisticsCustomerByUserBaseRespVO::getOwnerUserId));  
333 - respVoList.forEach(vo -> MapUtils.findAndThen(userMap,  
334 - vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname()))); 244 + private <T extends CrmStatisticsCustomerByUserBaseRespVO> void appendUserInfo(List<T> voList) {
  245 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
  246 + convertSet(voList, CrmStatisticsCustomerByUserBaseRespVO::getOwnerUserId));
  247 + voList.forEach(vo -> findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname())));
335 } 248 }
336 249
337 /** 250 /**
@@ -347,113 +260,10 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe @@ -347,113 +260,10 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
347 } 260 }
348 // 情况二:选中某个部门 261 // 情况二:选中某个部门
349 // 2.1 获得部门列表 262 // 2.1 获得部门列表
350 - Long deptId = reqVO.getDeptId();  
351 - List<Long> deptIds = convertList(deptApi.getChildDeptList(deptId), DeptRespDTO::getId);  
352 - deptIds.add(deptId); 263 + List<Long> deptIds = convertList(deptApi.getChildDeptList(reqVO.getDeptId()), DeptRespDTO::getId);
  264 + deptIds.add(reqVO.getDeptId());
353 // 2.2 获得用户编号 265 // 2.2 获得用户编号
354 return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId); 266 return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId);
355 } 267 }
356 268
357 -  
358 - /**  
359 - * 判断是否按照 月粒度 统计  
360 - *  
361 - * @param startTime 开始时间  
362 - * @param endTime 结束时间  
363 - * @return 是, 按月粒度, 否则按天粒度统计。  
364 - */  
365 - private boolean queryByMonth(LocalDateTime startTime, LocalDateTime endTime) {  
366 - return LocalDateTimeUtil.between(startTime, endTime).toDays() > 31;  
367 - }  
368 -  
369 - /**  
370 - * 生成时间序列  
371 - *  
372 - * @param startTime 开始时间  
373 - * @param endTime 结束时间  
374 - * @return 时间序列  
375 - */  
376 - private List<String> generateTimeSeries(LocalDateTime startTime, LocalDateTime endTime) {  
377 - boolean byMonth = queryByMonth(startTime, endTime);  
378 - List<String> times = CollUtil.newArrayList();  
379 - while (!startTime.isAfter(endTime)) {  
380 - times.add(LocalDateTimeUtil.format(startTime, byMonth ? TIME_FORMAT_BY_MONTH : TIME_FORMAT_BY_DAY));  
381 - if (byMonth)  
382 - startTime = startTime.plusMonths(1);  
383 - else  
384 - startTime = startTime.plusDays(1);  
385 - }  
386 -  
387 - return times;  
388 - }  
389 -  
390 - /**  
391 - * 获取 SQL 查询 GROUP BY 的时间格式  
392 - *  
393 - * @param startTime 开始时间  
394 - * @param endTime 结束时间  
395 - * @return SQL 查询 GROUP BY 的时间格式  
396 - */  
397 - private String getSqlDateFormat(LocalDateTime startTime, LocalDateTime endTime) {  
398 - return queryByMonth(startTime, endTime) ? SQL_DATE_FORMAT_BY_MONTH : SQL_DATE_FORMAT_BY_DAY;  
399 - }  
400 -  
401 - private void initParams(CrmStatisticsCustomerReqVO reqVO) {  
402 - final Integer intervalType = reqVO.getIntervalType();  
403 -  
404 - // 1. 自定义时间间隔,必须输入起始日期-结束日期  
405 - if (DateIntervalEnum.CUSTOMER.getType().equals(intervalType)) {  
406 - if (ObjUtil.isEmpty(reqVO.getTimes()) || reqVO.getTimes().length != 2) {  
407 - throw exception(STATISTICS_CUSTOMER_TIMES_NOT_SET);  
408 - }  
409 - // 设置 mapper sqlDateFormat 参数  
410 - reqVO.setSqlDateFormat(getSqlDateFormat(reqVO.getTimes()[0], reqVO.getTimes()[1]));  
411 - // 自定义日期无需计算日期参数  
412 - return;  
413 - }  
414 -  
415 - // 2. 根据时间区间类型计算时间段区间日期  
416 - DateTime beginDate = null;  
417 - DateTime endDate = null;  
418 - if (DateIntervalEnum.TODAY.getType().equals(intervalType)) {  
419 - beginDate = DateUtil.beginOfDay(DateUtil.date());  
420 - endDate = DateUtil.endOfDay(DateUtil.date());  
421 - } else if (DateIntervalEnum.YESTERDAY.getType().equals(intervalType)) {  
422 - beginDate = DateUtil.offsetDay(DateUtil.date(), -1);  
423 - endDate = DateUtil.offsetDay(DateUtil.date(), -1);  
424 - } else if (DateIntervalEnum.THIS_WEEK.getType().equals(intervalType)) {  
425 - beginDate = DateUtil.beginOfWeek(DateUtil.date());  
426 - endDate = DateUtil.endOfWeek(DateUtil.date());  
427 - } else if (DateIntervalEnum.LAST_WEEK.getType().equals(intervalType)) {  
428 - beginDate = DateUtil.beginOfWeek(DateUtil.offsetWeek(DateUtil.date(), -1));  
429 - endDate = DateUtil.endOfWeek(DateUtil.offsetWeek(DateUtil.date(), -1));  
430 - } else if (DateIntervalEnum.THIS_MONTH.getType().equals(intervalType)) {  
431 - beginDate = DateUtil.beginOfMonth(DateUtil.date());  
432 - endDate = DateUtil.endOfMonth(DateUtil.date());  
433 - } else if (DateIntervalEnum.LAST_MONTH.getType().equals(intervalType)) {  
434 - beginDate = DateUtil.beginOfMonth(DateUtil.offsetMonth(DateUtil.date(), -1));  
435 - endDate = DateUtil.endOfMonth(DateUtil.offsetMonth(DateUtil.date(), -1));  
436 - } else if (DateIntervalEnum.THIS_QUARTER.getType().equals(intervalType)) {  
437 - beginDate = DateUtil.beginOfQuarter(DateUtil.date());  
438 - endDate = DateUtil.endOfQuarter(DateUtil.date());  
439 - } else if (DateIntervalEnum.LAST_QUARTER.getType().equals(intervalType)) {  
440 - beginDate = DateUtil.beginOfQuarter(DateUtil.offsetMonth(DateUtil.date(), -3));  
441 - endDate = DateUtil.endOfQuarter(DateUtil.offsetMonth(DateUtil.date(), -3));  
442 - } else if (DateIntervalEnum.THIS_YEAR.getType().equals(intervalType)) {  
443 - beginDate = DateUtil.beginOfYear(DateUtil.date());  
444 - endDate = DateUtil.endOfYear(DateUtil.date());  
445 - } else if (DateIntervalEnum.LAST_YEAR.getType().equals(intervalType)) {  
446 - beginDate = DateUtil.beginOfYear(DateUtil.offsetMonth(DateUtil.date(), -12));  
447 - endDate = DateUtil.endOfYear(DateUtil.offsetMonth(DateUtil.date(), -12));  
448 - }  
449 -  
450 - // 3. 计算开始、结束日期时间,并设置reqVo  
451 - LocalDateTime[] times = new LocalDateTime[2];  
452 - times[0] = LocalDateTimeUtil.beginOfDay(LocalDateTimeUtil.of(beginDate));  
453 - times[1] = LocalDateTimeUtil.endOfDay(LocalDateTimeUtil.of(endDate));  
454 - // 3.1 设置 mapper 时间区间 参数  
455 - reqVO.setTimes(times);  
456 - // 3.2 设置 mapper sqlDateFormat 参数  
457 - reqVO.setSqlDateFormat(getSqlDateFormat(times[0], times[1]));  
458 - }  
459 } 269 }
yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml
@@ -5,31 +5,31 @@ @@ -5,31 +5,31 @@
5 <select id="selectCustomerCreateCountGroupByDate" 5 <select id="selectCustomerCreateCountGroupByDate"
6 resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByDateRespVO"> 6 resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByDateRespVO">
7 SELECT 7 SELECT
8 - DATE_FORMAT( create_time, #{sqlDateFormat} ) AS time, 8 + DATE_FORMAT(create_time, '%Y-%m-%d') AS time,
9 COUNT(*) AS customerCreateCount 9 COUNT(*) AS customerCreateCount
10 - FROM crm_customer  
11 - WHERE deleted = 0  
12 - AND owner_user_id IN  
13 - <foreach collection="userIds" item="userId" open="(" close=")" separator=",">  
14 - #{userId}  
15 - </foreach>  
16 - AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}  
17 - GROUP BY time 10 + FROM crm_customer
  11 + WHERE deleted = 0
  12 + AND owner_user_id IN
  13 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  14 + #{userId}
  15 + </foreach>
  16 + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
  17 + GROUP BY time
18 </select> 18 </select>
19 19
20 <select id="selectCustomerDealCountGroupByDate" 20 <select id="selectCustomerDealCountGroupByDate"
21 resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByDateRespVO"> 21 resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByDateRespVO">
22 SELECT 22 SELECT
23 - DATE_FORMAT( order_date, #{sqlDateFormat} ) AS time, 23 + DATE_FORMAT( order_date, '%Y-%m-%d' ) AS time,
24 COUNT( DISTINCT customer_id ) AS customerDealCount 24 COUNT( DISTINCT customer_id ) AS customerDealCount
25 - FROM crm_contract 25 + FROM crm_contract
26 WHERE deleted = 0 26 WHERE deleted = 0
27 - AND audit_status = 20  
28 - AND owner_user_id IN  
29 - <foreach collection="userIds" item="userId" open="(" close=")" separator=",">  
30 - #{userId}  
31 - </foreach>  
32 - AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} 27 + AND audit_status = 20
  28 + AND owner_user_id IN
  29 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  30 + #{userId}
  31 + </foreach>
  32 + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
33 GROUP BY time 33 GROUP BY time
34 </select> 34 </select>
35 35
@@ -38,13 +38,13 @@ @@ -38,13 +38,13 @@
38 SELECT 38 SELECT
39 owner_user_id, 39 owner_user_id,
40 COUNT(1) AS customer_create_count 40 COUNT(1) AS customer_create_count
41 - FROM crm_customer  
42 - WHERE deleted = 0  
43 - AND owner_user_id in  
44 - <foreach collection="userIds" item="userId" open="(" close=")" separator=",">  
45 - #{userId}  
46 - </foreach>  
47 - AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} 41 + FROM crm_customer
  42 + WHERE deleted = 0
  43 + AND owner_user_id in
  44 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  45 + #{userId}
  46 + </foreach>
  47 + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
48 GROUP BY owner_user_id 48 GROUP BY owner_user_id
49 </select> 49 </select>
50 50
@@ -53,16 +53,16 @@ @@ -53,16 +53,16 @@
53 SELECT 53 SELECT
54 customer.owner_user_id, 54 customer.owner_user_id,
55 COUNT( DISTINCT customer.id ) AS customer_deal_count 55 COUNT( DISTINCT customer.id ) AS customer_deal_count
56 - FROM crm_customer AS customer  
57 - LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id  
58 - WHERE customer.deleted = 0 AND contract.deleted = 0  
59 - AND contract.audit_status = 20  
60 - AND customer.owner_user_id IN  
61 - <foreach collection="userIds" item="userId" open="(" close=")" separator=",">  
62 - #{userId}  
63 - </foreach>  
64 - AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}  
65 - GROUP BY customer.owner_user_id 56 + FROM crm_customer AS customer
  57 + LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id
  58 + WHERE customer.deleted = 0 AND contract.deleted = 0
  59 + AND contract.audit_status = 20
  60 + AND customer.owner_user_id IN
  61 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  62 + #{userId}
  63 + </foreach>
  64 + AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
  65 + GROUP BY customer.owner_user_id
66 </select> 66 </select>
67 67
68 <select id="selectContractPriceGroupByUser" 68 <select id="selectContractPriceGroupByUser"
@@ -70,15 +70,15 @@ @@ -70,15 +70,15 @@
70 SELECT 70 SELECT
71 owner_user_id, 71 owner_user_id,
72 IFNULL(SUM(total_price), 0) AS contract_price 72 IFNULL(SUM(total_price), 0) AS contract_price
73 - FROM crm_contract  
74 - WHERE deleted = 0  
75 - AND audit_status = 20  
76 - AND owner_user_id in  
77 - <foreach collection="userIds" item="userId" open="(" close=")" separator=",">  
78 - #{userId}  
79 - </foreach>  
80 - AND order_date BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}  
81 - GROUP BY owner_user_id 73 + FROM crm_contract
  74 + WHERE deleted = 0
  75 + AND audit_status = 20
  76 + AND owner_user_id in
  77 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  78 + #{userId}
  79 + </foreach>
  80 + AND order_date BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
  81 + GROUP BY owner_user_id
82 </select> 82 </select>
83 83
84 <select id="selectReceivablePriceGroupByUser" 84 <select id="selectReceivablePriceGroupByUser"
@@ -86,132 +86,132 @@ @@ -86,132 +86,132 @@
86 SELECT 86 SELECT
87 owner_user_id, 87 owner_user_id,
88 IFNULL(SUM(price), 0) AS receivable_price 88 IFNULL(SUM(price), 0) AS receivable_price
89 - FROM crm_receivable  
90 - WHERE deleted = 0  
91 - AND audit_status = 20  
92 - AND owner_user_id IN  
93 - <foreach collection="userIds" item="userId" open="(" close=")" separator=",">  
94 - #{userId}  
95 - </foreach>  
96 - AND return_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}  
97 - GROUP BY owner_user_id 89 + FROM crm_receivable
  90 + WHERE deleted = 0
  91 + AND audit_status = 20
  92 + AND owner_user_id IN
  93 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  94 + #{userId}
  95 + </foreach>
  96 + AND return_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
  97 + GROUP BY owner_user_id
98 </select> 98 </select>
99 99
100 - <select id="selectFollowupRecordCountGroupByDate"  
101 - resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowupSummaryByDateRespVO"> 100 + <select id="selectFollowUpRecordCountGroupByDate"
  101 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowUpSummaryByDateRespVO">
102 SELECT 102 SELECT
103 - DATE_FORMAT( create_time, #{sqlDateFormat} ) AS time,  
104 - COUNT(*) AS followup_record_count  
105 - FROM crm_follow_up_record  
106 - WHERE creator IN  
107 - <foreach collection="userIds" item="userId" open="(" close=")" separator=",">  
108 - #{userId}  
109 - </foreach>  
110 - AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}  
111 - AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}  
112 - GROUP BY time 103 + DATE_FORMAT( create_time, '%Y-%m-%d' ) AS time,
  104 + COUNT(*) AS follow_up_record_count
  105 + FROM crm_follow_up_record
  106 + WHERE creator IN
  107 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  108 + #{userId}
  109 + </foreach>
  110 + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
  111 + AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
  112 + GROUP BY time
113 </select> 113 </select>
114 114
115 - <select id="selectFollowupCustomerCountGroupByDate"  
116 - resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowupSummaryByDateRespVO"> 115 + <select id="selectFollowUpCustomerCountGroupByDate"
  116 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowUpSummaryByDateRespVO">
117 SELECT 117 SELECT
118 - DATE_FORMAT( create_time, #{sqlDateFormat} ) AS time,  
119 - COUNT(DISTINCT biz_id) AS followup_customer_count  
120 - FROM crm_follow_up_record  
121 - WHERE creator IN  
122 - <foreach collection="userIds" item="userId" open="(" close=")" separator=",">  
123 - #{userId}  
124 - </foreach>  
125 - AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}  
126 - AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}  
127 - GROUP BY time 118 + DATE_FORMAT( create_time, '%Y-%m-%d' ) AS time,
  119 + COUNT(DISTINCT biz_id) AS follow_up_customer_count
  120 + FROM crm_follow_up_record
  121 + WHERE creator IN
  122 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  123 + #{userId}
  124 + </foreach>
  125 + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
  126 + AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
  127 + GROUP BY time
128 </select> 128 </select>
129 129
130 - <select id="selectFollowupRecordCountGroupByUser"  
131 - resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowupSummaryByUserRespVO"> 130 + <select id="selectFollowUpRecordCountGroupByUser"
  131 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowUpSummaryByUserRespVO">
132 SELECT 132 SELECT
133 creator as owner_user_id, 133 creator as owner_user_id,
134 - COUNT(*) AS followup_record_count  
135 - FROM crm_follow_up_record  
136 - WHERE creator IN  
137 - <foreach collection="userIds" item="userId" open="(" close=")" separator=",">  
138 - #{userId}  
139 - </foreach>  
140 - AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}  
141 - AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}  
142 - GROUP BY creator 134 + COUNT(*) AS follow_up_record_count
  135 + FROM crm_follow_up_record
  136 + WHERE creator IN
  137 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  138 + #{userId}
  139 + </foreach>
  140 + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
  141 + AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
  142 + GROUP BY creator
143 </select> 143 </select>
144 144
145 - <select id="selectFollowupCustomerCountGroupByUser"  
146 - resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowupSummaryByUserRespVO"> 145 + <select id="selectFollowUpCustomerCountGroupByUser"
  146 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowUpSummaryByUserRespVO">
147 SELECT 147 SELECT
148 creator as owner_user_id, 148 creator as owner_user_id,
149 - COUNT(DISTINCT biz_id) AS followup_customer_count  
150 - FROM crm_follow_up_record  
151 - WHERE creator IN  
152 - <foreach collection="userIds" item="userId" open="(" close=")" separator=",">  
153 - #{userId}  
154 - </foreach>  
155 - AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}  
156 - AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}  
157 - GROUP BY creator 149 + COUNT(DISTINCT biz_id) AS follow_up_customer_count
  150 + FROM crm_follow_up_record
  151 + WHERE creator IN
  152 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  153 + #{userId}
  154 + </foreach>
  155 + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
  156 + AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
  157 + GROUP BY creator
158 </select> 158 </select>
159 159
160 - <select id="selectFollowupRecordCountGroupByType"  
161 - resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowupSummaryByTypeRespVO"> 160 + <select id="selectFollowUpRecordCountGroupByType"
  161 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowUpSummaryByTypeRespVO">
162 SELECT 162 SELECT
163 - type AS followupType,  
164 - COUNT(*) AS followup_record_count  
165 - FROM crm_follow_up_record  
166 - WHERE creator IN  
167 - <foreach collection="userIds" item="userId" open="(" close=")" separator=",">  
168 - #{userId}  
169 - </foreach>  
170 - AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}  
171 - AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}  
172 - GROUP BY followupType 163 + type AS follow_up_type,
  164 + COUNT(*) AS follow_up_record_count
  165 + FROM crm_follow_up_record
  166 + WHERE creator IN
  167 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  168 + #{userId}
  169 + </foreach>
  170 + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
  171 + AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
  172 + GROUP BY follow_up_type
173 </select> 173 </select>
174 174
175 <select id="selectContractSummary" 175 <select id="selectContractSummary"
176 resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerContractSummaryRespVO"> 176 resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerContractSummaryRespVO">
177 SELECT 177 SELECT
178 - customer.`name` AS customer_name,  
179 - contract.`name` AS contract_name, 178 + customer.name AS customer_name,
  179 + contract.name AS contract_name,
180 contract.total_price, 180 contract.total_price,
181 IFNULL( receivable.price, 0 ) AS receivable_price, 181 IFNULL( receivable.price, 0 ) AS receivable_price,
182 customer.industry_id, 182 customer.industry_id,
183 customer.source, 183 customer.source,
184 customer.owner_user_id, 184 customer.owner_user_id,
185 - customer.creator AS creator_user_id, 185 + customer.creator,
186 customer.create_time, 186 customer.create_time,
187 contract.order_date 187 contract.order_date
188 - FROM crm_customer AS customer  
189 - INNER JOIN crm_contract AS contract ON customer.id = contract.customer_id  
190 - LEFT JOIN crm_receivable AS receivable ON contract.id = receivable.contract_id  
191 - WHERE customer.deleted = 0 AND contract.deleted = 0 AND receivable.deleted = 0  
192 - AND contract.audit_status = 20  
193 - AND customer.owner_user_id IN  
194 - <foreach collection="userIds" item="userId" open="(" close=")" separator=",">  
195 - #{userId}  
196 - </foreach>  
197 - AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} 188 + FROM crm_customer AS customer
  189 + INNER JOIN crm_contract AS contract ON customer.id = contract.customer_id
  190 + LEFT JOIN crm_receivable AS receivable ON contract.id = receivable.contract_id
  191 + WHERE customer.deleted = 0 AND contract.deleted = 0 AND receivable.deleted = 0
  192 + AND contract.audit_status = 20
  193 + AND customer.owner_user_id IN
  194 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  195 + #{userId}
  196 + </foreach>
  197 + AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
198 </select> 198 </select>
199 199
200 <select id="selectCustomerDealCycleGroupByDate" 200 <select id="selectCustomerDealCycleGroupByDate"
201 resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerDealCycleByDateRespVO"> 201 resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerDealCycleByDateRespVO">
202 SELECT 202 SELECT
203 - DATE_FORMAT( contract.order_date, #{sqlDateFormat} ) AS time, 203 + DATE_FORMAT( contract.order_date, '%Y-%m-%d' ) AS time,
204 IFNULL( TRUNCATE ( AVG( TIMESTAMPDIFF( DAY, customer.create_time, contract.order_date )), 1 ), 0 ) AS customer_deal_cycle 204 IFNULL( TRUNCATE ( AVG( TIMESTAMPDIFF( DAY, customer.create_time, contract.order_date )), 1 ), 0 ) AS customer_deal_cycle
205 - FROM crm_customer AS customer  
206 - LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id  
207 - WHERE customer.deleted = 0 AND contract.deleted = 0  
208 - AND contract.audit_status = 20  
209 - AND customer.owner_user_id IN  
210 - <foreach collection="userIds" item="userId" open="(" close=")" separator=",">  
211 - #{userId}  
212 - </foreach>  
213 - AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}  
214 - GROUP BY time 205 + FROM crm_customer AS customer
  206 + LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id
  207 + WHERE customer.deleted = 0 AND contract.deleted = 0
  208 + AND contract.audit_status = 20
  209 + AND customer.owner_user_id IN
  210 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  211 + #{userId}
  212 + </foreach>
  213 + AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
  214 + GROUP BY time
215 </select> 215 </select>
216 216
217 <select id="selectCustomerDealCycleGroupByUser" 217 <select id="selectCustomerDealCycleGroupByUser"
@@ -219,16 +219,16 @@ @@ -219,16 +219,16 @@
219 SELECT 219 SELECT
220 customer.owner_user_id, 220 customer.owner_user_id,
221 IFNULL( TRUNCATE ( AVG( TIMESTAMPDIFF( DAY, customer.create_time, contract.order_date )), 1 ), 0 ) AS customer_deal_cycle 221 IFNULL( TRUNCATE ( AVG( TIMESTAMPDIFF( DAY, customer.create_time, contract.order_date )), 1 ), 0 ) AS customer_deal_cycle
222 - FROM crm_customer AS customer  
223 - LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id  
224 - WHERE customer.deleted = 0 AND contract.deleted = 0  
225 - AND contract.audit_status = 20  
226 - AND customer.owner_user_id IN  
227 - <foreach collection="userIds" item="userId" open="(" close=")" separator=",">  
228 - #{userId}  
229 - </foreach>  
230 - AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}  
231 - GROUP BY customer.owner_user_id 222 + FROM crm_customer AS customer
  223 + LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id
  224 + WHERE customer.deleted = 0 AND contract.deleted = 0
  225 + AND contract.audit_status = 20
  226 + AND customer.owner_user_id IN
  227 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  228 + #{userId}
  229 + </foreach>
  230 + AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
  231 + GROUP BY customer.owner_user_id
232 </select> 232 </select>
233 233
234 </mapper> 234 </mapper>