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 1 package cn.iocoder.yudao.framework.common.enums;
2 2  
  3 +import cn.hutool.core.util.ArrayUtil;
3 4 import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
4 5 import lombok.AllArgsConstructor;
5 6 import lombok.Getter;
... ... @@ -7,7 +8,7 @@ import lombok.Getter;
7 8 import java.util.Arrays;
8 9  
9 10 /**
10   - * 时间间隔类型枚举
  11 + * 时间间隔的枚举
11 12 *
12 13 * @author dhb52
13 14 */
... ... @@ -15,26 +16,19 @@ import java.util.Arrays;
15 16 @AllArgsConstructor
16 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 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 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 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 68 public static boolean isExpired(LocalDateTime time) {
73 69 LocalDateTime now = LocalDateTime.now();
74 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 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 132 * @param date 日期
... ...
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java
1 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 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 12 import java.time.temporal.ChronoUnit;
10 13 import java.time.temporal.TemporalAdjusters;
  14 +import java.util.ArrayList;
  15 +import java.util.List;
11 16  
12 17 /**
13 18 * 时间工具类,用于 {@link java.time.LocalDateTime}
... ... @@ -21,6 +26,22 @@ public class LocalDateTimeUtils {
21 26 */
22 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 45 public static LocalDateTime addTime(Duration duration) {
25 46 return LocalDateTime.now().plus(duration);
26 47 }
... ... @@ -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 96 * @param startTime 开始时间
... ... @@ -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 174 * @param dateTime 日期
... ... @@ -168,4 +214,94 @@ public class LocalDateTimeUtils {
168 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 104 ErrorCode FOLLOW_UP_RECORD_DELETE_DENIED = new ErrorCode(1_020_013_001, "删除跟进记录失败,原因:没有权限");
105 105  
106 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. 客户总量分析 ==
2 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 4 Authorization: Bearer {{token}}
5 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 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 44 @Operation(summary = "获取客户跟进次数分析(按日期)")
45 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 51 @Operation(summary = "获取客户跟进次数分析(按用户)")
52 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 58 @Operation(summary = "获取客户跟进次数分析(按类型)")
59 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 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 1 package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;
2 2  
3   -import com.fasterxml.jackson.annotation.JsonIgnore;
4 3 import io.swagger.v3.oas.annotations.media.Schema;
5 4 import lombok.Data;
6 5  
... ... @@ -13,7 +12,6 @@ import lombok.Data;
13 12 public class CrmStatisticsCustomerByUserBaseRespVO {
14 13  
15 14 @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
16   - @JsonIgnore
17 15 private Long ownerUserId;
18 16  
19 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 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 4 import io.swagger.v3.oas.annotations.media.Schema;
7 5 import lombok.Data;
8 6  
9 7 import java.math.BigDecimal;
10   -import java.time.LocalDate;
11 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 10 @Schema(description = "管理后台 - CRM 客户转化率分析 VO")
17 11 @Data
18 12 public class CrmStatisticsCustomerContractSummaryRespVO {
... ... @@ -29,31 +23,19 @@ public class CrmStatisticsCustomerContractSummaryRespVO {
29 23 @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1200.00")
30 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 32 @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
47   - @JsonIgnore
48 33 private Long ownerUserId;
49   -
50 34 @Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
51 35 private String ownerUserName;
52 36  
53 37 @Schema(description = "创建人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
54   - @JsonIgnore
55   - private String creatorUserId;
56   -
  38 + private String creator;
57 39 @Schema(description = "创建人", requiredMode = Schema.RequiredMode.REQUIRED, example = "源码")
58 40 private String creatorUserName;
59 41  
... ... @@ -61,7 +43,6 @@ public class CrmStatisticsCustomerContractSummaryRespVO {
61 43 private LocalDateTime createTime;
62 44  
63 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 11 private String time;
12 12  
13 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 8 public class CrmStatisticsCustomerDealCycleByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO {
9 9  
10 10 @Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0")
11   - private Double customerDealCycle = 0.0;
  11 + private Double customerDealCycle;
12 12  
13 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 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 34 private List<Long> userIds;
35 35  
36 36 @Schema(description = "时间间隔类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
37 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 49 * group by DATE_FORMAT(field, #{dateFormat})
50 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 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 11 private String time;
12 12  
13 13 @Schema(description = "新建客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
14   - private Integer customerCreateCount = 0;
  14 + private Integer customerCreateCount;
15 15  
16 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 10 public class CrmStatisticsCustomerSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO {
11 11  
12 12 @Schema(description = "新建客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
13   - private Integer customerCreateCount = 0;
  13 + private Integer customerCreateCount;
14 14  
15 15 @Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
16   - private Integer customerDealCount = 0;
  16 + private Integer customerDealCount;
17 17  
18 18 @Schema(description = "合同总金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
19   - private BigDecimal contractPrice = BigDecimal.ZERO;
  19 + private BigDecimal contractPrice;
20 20  
21 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 5  
6 6 @Schema(description = "管理后台 - CRM 跟进次数分析(按日期) VO")
7 7 @Data
8   -public class CrmStatisticsFollowupSummaryByDateRespVO {
  8 +public class CrmStatisticsFollowUpSummaryByDateRespVO {
9 9  
10 10 @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401")
11 11 private String time;
12 12  
13 13 @Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
14   - private Integer followupRecordCount = 0;
  14 + private Integer followUpRecordCount;
15 15  
16 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 6  
7 7 @Schema(description = "管理后台 - CRM 跟进次数分析(按类型) VO")
8 8 @Data
9   -public class CrmStatisticsFollowupSummaryByTypeRespVO {
  9 +public class CrmStatisticsFollowUpSummaryByTypeRespVO {
10 10  
11 11 @Schema(description = "跟进类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
12   - private String followupType;
  12 + private Integer followUpType;
13 13  
14 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 5  
6 6 @Schema(description = "管理后台 - CRM 跟进次数分析(按用户) VO")
7 7 @Data
8   -public class CrmStatisticsFollowupSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO {
  8 +public class CrmStatisticsFollowUpSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO {
9 9  
10 10 @Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
11   - private Integer followupRecordCount = 0;
  11 + private Integer followUpRecordCount;
12 12  
13 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 17 @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
18 18 private String deptName;
19 19  
  20 + // TODO @芋艿:需要改下,金额是 bigdecimal
20 21 /**
21 22 * 数量是个特别“抽象”的概念,在不同排行下,代表不同含义
22 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 13 @Mapper
14 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 36 List<CrmStatisticsCustomerContractSummaryRespVO> selectContractSummary(CrmStatisticsCustomerReqVO reqVO);
37 37  
38   - List<CrmStatisticsFollowupSummaryByTypeRespVO> selectFollowupRecordCountGroupByType(CrmStatisticsCustomerReqVO reqVO);
  38 + List<CrmStatisticsFollowUpSummaryByTypeRespVO> selectFollowUpRecordCountGroupByType(CrmStatisticsCustomerReqVO reqVO);
39 39  
40 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 27 */
28 28 List<CrmStatisticsCustomerSummaryByUserRespVO> getCustomerSummaryByUser(CrmStatisticsCustomerReqVO reqVO);
29 29  
30   -
31 30 /**
32 31 * 跟进次数分析(按日期)
33 32 *
34 33 * @param reqVO 请求参数
35 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 41 * @param reqVO 请求参数
43 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 49 * @param reqVO 请求参数
51 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 1 package cn.iocoder.yudao.module.crm.service.statistics;
2 2  
3 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 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 6 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
11 7 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*;
12 8 import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper;
13 9 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
14 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 11 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
18 12 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
19 13 import jakarta.annotation.Resource;
... ... @@ -27,10 +21,8 @@ import java.util.List;
27 21 import java.util.Map;
28 22 import java.util.stream.Stream;
29 23  
30   -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
31 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 28 * CRM 客户分析 Service 实现类
... ... @@ -41,13 +33,6 @@ import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.STATISTICS_CU
41 33 @Validated
42 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 36 @Resource
52 37 private CrmStatisticsCustomerMapper customerMapper;
53 38  
... ... @@ -55,283 +40,211 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
55 40 private AdminUserApi adminUserApi;
56 41 @Resource
57 42 private DeptApi deptApi;
58   - @Resource
59   - private DictDataApi dictDataApi;
60 43  
61 44 @Override
62 45 public List<CrmStatisticsCustomerSummaryByDateRespVO> getCustomerSummaryByDate(CrmStatisticsCustomerReqVO reqVO) {
63 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 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 71 @Override
93 72 public List<CrmStatisticsCustomerSummaryByUserRespVO> getCustomerSummaryByUser(CrmStatisticsCustomerReqVO reqVO) {
94 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 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 104 @Override
139   - public List<CrmStatisticsFollowupSummaryByDateRespVO> getFollowupSummaryByDate(CrmStatisticsCustomerReqVO reqVO) {
  105 + public List<CrmStatisticsFollowUpSummaryByDateRespVO> getFollowUpSummaryByDate(CrmStatisticsCustomerReqVO reqVO) {
140 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 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 131 @Override
170   - public List<CrmStatisticsFollowupSummaryByUserRespVO> getFollowupSummaryByUser(CrmStatisticsCustomerReqVO reqVO) {
  132 + public List<CrmStatisticsFollowUpSummaryByUserRespVO> getFollowUpSummaryByUser(CrmStatisticsCustomerReqVO reqVO) {
171 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 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 157 @Override
205   - public List<CrmStatisticsFollowupSummaryByTypeRespVO> getFollowupSummaryByType(CrmStatisticsCustomerReqVO reqVO) {
  158 + public List<CrmStatisticsFollowUpSummaryByTypeRespVO> getFollowUpSummaryByType(CrmStatisticsCustomerReqVO reqVO) {
206 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 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 169 @Override
229 170 public List<CrmStatisticsCustomerContractSummaryRespVO> getContractSummary(CrmStatisticsCustomerReqVO reqVO) {
230 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 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 190 @Override
264 191 public List<CrmStatisticsCustomerDealCycleByDateRespVO> getCustomerDealCycleByDate(CrmStatisticsCustomerReqVO reqVO) {
265 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 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 213 @Override
290 214 public List<CrmStatisticsCustomerDealCycleByUserRespVO> getCustomerDealCycleByUser(CrmStatisticsCustomerReqVO reqVO) {
291 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 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 260 }
348 261 // 情况二:选中某个部门
349 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 265 // 2.2 获得用户编号
354 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 5 <select id="selectCustomerCreateCountGroupByDate"
6 6 resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByDateRespVO">
7 7 SELECT
8   - DATE_FORMAT( create_time, #{sqlDateFormat} ) AS time,
  8 + DATE_FORMAT(create_time, '%Y-%m-%d') AS time,
9 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 18 </select>
19 19  
20 20 <select id="selectCustomerDealCountGroupByDate"
21 21 resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByDateRespVO">
22 22 SELECT
23   - DATE_FORMAT( order_date, #{sqlDateFormat} ) AS time,
  23 + DATE_FORMAT( order_date, '%Y-%m-%d' ) AS time,
24 24 COUNT( DISTINCT customer_id ) AS customerDealCount
25   - FROM crm_contract
  25 + FROM crm_contract
26 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 33 GROUP BY time
34 34 </select>
35 35  
... ... @@ -38,13 +38,13 @@
38 38 SELECT
39 39 owner_user_id,
40 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 48 GROUP BY owner_user_id
49 49 </select>
50 50  
... ... @@ -53,16 +53,16 @@
53 53 SELECT
54 54 customer.owner_user_id,
55 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 66 </select>
67 67  
68 68 <select id="selectContractPriceGroupByUser"
... ... @@ -70,15 +70,15 @@
70 70 SELECT
71 71 owner_user_id,
72 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 82 </select>
83 83  
84 84 <select id="selectReceivablePriceGroupByUser"
... ... @@ -86,132 +86,132 @@
86 86 SELECT
87 87 owner_user_id,
88 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 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 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 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 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 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 132 SELECT
133 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 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 147 SELECT
148 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 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 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 173 </select>
174 174  
175 175 <select id="selectContractSummary"
176 176 resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerContractSummaryRespVO">
177 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 180 contract.total_price,
181 181 IFNULL( receivable.price, 0 ) AS receivable_price,
182 182 customer.industry_id,
183 183 customer.source,
184 184 customer.owner_user_id,
185   - customer.creator AS creator_user_id,
  185 + customer.creator,
186 186 customer.create_time,
187 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 198 </select>
199 199  
200 200 <select id="selectCustomerDealCycleGroupByDate"
201 201 resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerDealCycleByDateRespVO">
202 202 SELECT
203   - DATE_FORMAT( contract.order_date, #{sqlDateFormat} ) AS time,
  203 + DATE_FORMAT( contract.order_date, '%Y-%m-%d' ) AS time,
204 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 215 </select>
216 216  
217 217 <select id="selectCustomerDealCycleGroupByUser"
... ... @@ -219,16 +219,16 @@
219 219 SELECT
220 220 customer.owner_user_id,
221 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 232 </select>
233 233  
234 234 </mapper>
... ...