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

This commit is contained in:
YunaiV 2024-03-30 09:22:54 +08:00
parent eab9627253
commit 90f82b157d
21 changed files with 483 additions and 600 deletions

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.framework.common.enums; package cn.iocoder.yudao.framework.common.enums;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable; import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
@ -7,7 +8,7 @@ import lombok.Getter;
import java.util.Arrays; import java.util.Arrays;
/** /**
* 时间间隔类型枚举 * 时间间隔枚举
* *
* @author dhb52 * @author dhb52
*/ */
@ -15,26 +16,19 @@ import java.util.Arrays;
@AllArgsConstructor @AllArgsConstructor
public enum DateIntervalEnum implements IntArrayValuable { public enum DateIntervalEnum implements IntArrayValuable {
TODAY(1, "今天"), DAY(1, ""),
YESTERDAY(2, "昨天"), WEEK(2, ""),
THIS_WEEK(3, "本周"), MONTH(3, ""),
LAST_WEEK(4, "上周"), QUARTER(4, "季度"),
THIS_MONTH(5, "本月"), YEAR(5, "")
LAST_MONTH(6, "上月"),
THIS_QUARTER(7, "本季度"),
LAST_QUARTER(8, "上季度"),
THIS_YEAR(9, "本年"),
LAST_YEAR(10, "去年"),
CUSTOMER(11, "自定义"),
; ;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(DateIntervalEnum::getType).toArray(); public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(DateIntervalEnum::getInterval).toArray();
/** /**
* 类型 * 类型
*/ */
private final Integer type; private final Integer interval;
/** /**
* 名称 * 名称
*/ */
@ -45,4 +39,8 @@ public enum DateIntervalEnum implements IntArrayValuable {
return ARRAYS; return ARRAYS;
} }
public static DateIntervalEnum valueOf(Integer interval) {
return ArrayUtil.firstMatch(item -> item.getInterval().equals(interval), DateIntervalEnum.values());
}
} }

View File

@ -65,19 +65,11 @@ public class DateUtils {
return new Date(System.currentTimeMillis() + duration.toMillis()); return new Date(System.currentTimeMillis() + duration.toMillis());
} }
public static boolean isExpired(Date time) {
return System.currentTimeMillis() > time.getTime();
}
public static boolean isExpired(LocalDateTime time) { public static boolean isExpired(LocalDateTime time) {
LocalDateTime now = LocalDateTime.now(); LocalDateTime now = LocalDateTime.now();
return now.isAfter(time); return now.isAfter(time);
} }
public static long diff(Date endTime, Date startTime) {
return endTime.getTime() - startTime.getTime();
}
/** /**
* 创建指定时间 * 创建指定时间
* *
@ -134,37 +126,6 @@ public class DateUtils {
return a.isAfter(b) ? a : b; return a.isAfter(b) ? a : b;
} }
/**
* 计算当期时间相差的日期
*
* @param field 日历字段.<br/>eg:Calendar.MONTH,Calendar.DAY_OF_MONTH,<br/>Calendar.HOUR_OF_DAY等.
* @param amount 相差的数值
* @return 计算后的日志
*/
public static Date addDate(int field, int amount) {
return addDate(null, field, amount);
}
/**
* 计算当期时间相差的日期
*
* @param date 设置时间
* @param field 日历字段 例如说{@link Calendar#DAY_OF_MONTH}
* @param amount 相差的数值
* @return 计算后的日志
*/
public static Date addDate(Date date, int field, int amount) {
if (amount == 0) {
return date;
}
Calendar c = Calendar.getInstance();
if (date != null) {
c.setTime(date);
}
c.add(field, amount);
return c.getTime();
}
/** /**
* 是否今天 * 是否今天
* *

View File

@ -1,13 +1,18 @@
package cn.iocoder.yudao.framework.common.util.date; package cn.iocoder.yudao.framework.common.util.date;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum;
import java.time.Duration; import java.time.*;
import java.time.LocalDate; import java.time.format.DateTimeParseException;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters; import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.List;
/** /**
* 时间工具类用于 {@link java.time.LocalDateTime} * 时间工具类用于 {@link java.time.LocalDateTime}
@ -21,6 +26,22 @@ public class LocalDateTimeUtils {
*/ */
public static LocalDateTime EMPTY = buildTime(1970, 1, 1); public static LocalDateTime EMPTY = buildTime(1970, 1, 1);
/**
* 解析时间
*
* 相比 {@link LocalDateTimeUtil#parse(CharSequence)} 方法来说会尽量去解析直到成功
*
* @param time 时间
* @return 时间字符串
*/
public static LocalDateTime parse(String time) {
try {
return LocalDateTimeUtil.parse(time, DatePattern.NORM_DATE_PATTERN);
} catch (DateTimeParseException e) {
return LocalDateTimeUtil.parse(time);
}
}
public static LocalDateTime addTime(Duration duration) { public static LocalDateTime addTime(Duration duration) {
return LocalDateTime.now().plus(duration); return LocalDateTime.now().plus(duration);
} }
@ -54,6 +75,21 @@ public class LocalDateTimeUtils {
return new LocalDateTime[]{buildTime(year1, mouth1, day1), buildTime(year2, mouth2, day2)}; return new LocalDateTime[]{buildTime(year1, mouth1, day1), buildTime(year2, mouth2, day2)};
} }
/**
* 判指定断时间是否在该时间范围内
*
* @param startTime 开始时间
* @param endTime 结束时间
* @param time 指定时间
* @return 是否
*/
public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime, String time) {
if (startTime == null || endTime == null || time == null) {
return false;
}
return LocalDateTimeUtil.isIn(parse(time), startTime, endTime);
}
/** /**
* 判断当前时间是否在该时间范围内 * 判断当前时间是否在该时间范围内
* *
@ -122,6 +158,16 @@ public class LocalDateTimeUtils {
return date.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX); return date.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
} }
/**
* 获得指定日期所在季度
*
* @param date 日期
* @return 所在季度
*/
public static int getQuarterOfYear(LocalDateTime date) {
return (date.getMonthValue() - 1) / 3 + 1;
}
/** /**
* 获取指定日期到现在过了几天如果指定日期在当前日期之后获取结果为负 * 获取指定日期到现在过了几天如果指定日期在当前日期之后获取结果为负
* *
@ -168,4 +214,94 @@ public class LocalDateTimeUtils {
return LocalDateTime.now().with(TemporalAdjusters.firstDayOfYear()).with(LocalTime.MIN); return LocalDateTime.now().with(TemporalAdjusters.firstDayOfYear()).with(LocalTime.MIN);
} }
public static List<LocalDateTime[]> getDateRangeList(LocalDateTime startTime,
LocalDateTime endTime,
Integer interval) {
// 1.1 找到枚举
DateIntervalEnum intervalEnum = DateIntervalEnum.valueOf(interval);
Assert.notNull(intervalEnum, "interval({}} 找不到对应的枚举", interval);
// 1.2 将时间对齐
startTime = LocalDateTimeUtil.beginOfDay(startTime);
endTime = LocalDateTimeUtil.endOfDay(endTime);
// 2. 循环生成时间范围
List<LocalDateTime[]> timeRanges = new ArrayList<>();
switch (intervalEnum) {
case DateIntervalEnum.DAY:
while (startTime.isBefore(endTime)) {
timeRanges.add(new LocalDateTime[]{startTime, startTime.plusDays(1).minusNanos(1)});
startTime = startTime.plusDays(1);
}
break;
case DateIntervalEnum.WEEK:
while (startTime.isBefore(endTime)) {
LocalDateTime endOfWeek = startTime.with(DayOfWeek.SUNDAY).plusDays(1).minusNanos(1);
timeRanges.add(new LocalDateTime[]{startTime, endOfWeek});
startTime = endOfWeek.plusNanos(1);
}
break;
case DateIntervalEnum.MONTH:
while (startTime.isBefore(endTime)) {
LocalDateTime endOfMonth = startTime.with(TemporalAdjusters.lastDayOfMonth()).plusDays(1).minusNanos(1);
timeRanges.add(new LocalDateTime[]{startTime, endOfMonth});
startTime = endOfMonth.plusNanos(1);
}
break;
case DateIntervalEnum.QUARTER:
while (startTime.isBefore(endTime)) {
LocalDateTime quarterEnd = startTime.withMonth(getQuarterOfYear(startTime) * 3 + 1)
.withDayOfMonth(1).minusNanos(1);
timeRanges.add(new LocalDateTime[]{startTime, quarterEnd});
startTime = quarterEnd.plusNanos(1);
}
break;
case DateIntervalEnum.YEAR:
while (startTime.isBefore(endTime)) {
LocalDateTime endOfYear = startTime.with(TemporalAdjusters.lastDayOfYear()).plusDays(1).minusNanos(1);
timeRanges.add(new LocalDateTime[]{startTime, endOfYear});
startTime = endOfYear.plusNanos(1);
}
break;
default:
throw new IllegalArgumentException("Invalid interval: " + interval);
}
// 3. 兜底最后一个时间需要保持在 endTime 之前
LocalDateTime[] lastTimeRange = CollUtil.getLast(timeRanges);
if (lastTimeRange != null) {
lastTimeRange[1] = endTime;
}
return timeRanges;
}
/**
* 格式化时间范围
*
* @param startTime 开始时间
* @param endTime 结束时间
* @param interval 时间间隔
* @return 时间范围
*/
public static String formatDateRange(LocalDateTime startTime, LocalDateTime endTime, Integer interval) {
// 1. 找到枚举
DateIntervalEnum intervalEnum = DateIntervalEnum.valueOf(interval);
Assert.notNull(intervalEnum, "interval({}} 找不到对应的枚举", interval);
// 2. 循环生成时间范围
switch (intervalEnum) {
case DateIntervalEnum.DAY:
return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN);
case DateIntervalEnum.WEEK:
return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN)
+ StrUtil.format("(第 {} 周)", LocalDateTimeUtil.weekOfYear(startTime));
case DateIntervalEnum.MONTH:
return LocalDateTimeUtil.format(startTime, DatePattern.NORM_MONTH_PATTERN);
case DateIntervalEnum.QUARTER:
return StrUtil.format("{}-Q{}", startTime.getYear(), getQuarterOfYear(startTime));
case DateIntervalEnum.YEAR:
return LocalDateTimeUtil.format(startTime, DatePattern.NORM_YEAR_PATTERN);
default:
throw new IllegalArgumentException("Invalid interval: " + interval);
}
}
} }

View File

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

View File

@ -1,6 +1,6 @@
# == 1. 客户总量分析 == # == 1. 客户总量分析 ==
### 1.1 客户总量分析(按日) ### 1.1 客户总量分析(按日)
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 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
Authorization: Bearer {{token}} Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}} tenant-id: {{adminTenentId}}

View File

@ -40,25 +40,25 @@ public class CrmStatisticsCustomerController {
return success(customerService.getCustomerSummaryByUser(reqVO)); return success(customerService.getCustomerSummaryByUser(reqVO));
} }
@GetMapping("/get-followup-summary-by-date") @GetMapping("/get-follow-up-summary-by-date")
@Operation(summary = "获取客户跟进次数分析(按日期)") @Operation(summary = "获取客户跟进次数分析(按日期)")
@PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")
public CommonResult<List<CrmStatisticsFollowupSummaryByDateRespVO>> getFollowupSummaryByDate(@Valid CrmStatisticsCustomerReqVO reqVO) { public CommonResult<List<CrmStatisticsFollowUpSummaryByDateRespVO>> getFollowupSummaryByDate(@Valid CrmStatisticsCustomerReqVO reqVO) {
return success(customerService.getFollowupSummaryByDate(reqVO)); return success(customerService.getFollowUpSummaryByDate(reqVO));
} }
@GetMapping("/get-followup-summary-by-user") @GetMapping("/get-follow-up-summary-by-user")
@Operation(summary = "获取客户跟进次数分析(按用户)") @Operation(summary = "获取客户跟进次数分析(按用户)")
@PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")
public CommonResult<List<CrmStatisticsFollowupSummaryByUserRespVO>> getFollowupSummaryByUser(@Valid CrmStatisticsCustomerReqVO reqVO) { public CommonResult<List<CrmStatisticsFollowUpSummaryByUserRespVO>> getFollowUpSummaryByUser(@Valid CrmStatisticsCustomerReqVO reqVO) {
return success(customerService.getFollowupSummaryByUser(reqVO)); return success(customerService.getFollowUpSummaryByUser(reqVO));
} }
@GetMapping("/get-followup-summary-by-type") @GetMapping("/get-follow-up-summary-by-type")
@Operation(summary = "获取客户跟进次数分析(按类型)") @Operation(summary = "获取客户跟进次数分析(按类型)")
@PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")
public CommonResult<List<CrmStatisticsFollowupSummaryByTypeRespVO>> getFollowupSummaryByType(@Valid CrmStatisticsCustomerReqVO reqVO) { public CommonResult<List<CrmStatisticsFollowUpSummaryByTypeRespVO>> getFollowUpSummaryByType(@Valid CrmStatisticsCustomerReqVO reqVO) {
return success(customerService.getFollowupSummaryByType(reqVO)); return success(customerService.getFollowUpSummaryByType(reqVO));
} }
@GetMapping("/get-contract-summary") @GetMapping("/get-contract-summary")

View File

@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
@ -13,7 +12,6 @@ import lombok.Data;
public class CrmStatisticsCustomerByUserBaseRespVO { public class CrmStatisticsCustomerByUserBaseRespVO {
@Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@JsonIgnore
private Long ownerUserId; private Long ownerUserId;
@Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") @Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")

View File

@ -1,18 +1,12 @@
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;
@Schema(description = "管理后台 - CRM 客户转化率分析 VO") @Schema(description = "管理后台 - CRM 客户转化率分析 VO")
@Data @Data
public class CrmStatisticsCustomerContractSummaryRespVO { public class CrmStatisticsCustomerContractSummaryRespVO {
@ -29,31 +23,19 @@ public class CrmStatisticsCustomerContractSummaryRespVO {
@Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1200.00") @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1200.00")
private BigDecimal receivablePrice; private BigDecimal receivablePrice;
@Schema(description = "客户行业ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") @Schema(description = "客户行业编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@JsonIgnore private Integer industryId;
private String industryId;
@Schema(description = "客户行业", requiredMode = Schema.RequiredMode.REQUIRED, example = "金融") @Schema(description = "客户来源编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private String industryName; private Integer source;
@Schema(description = "客户来源ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@JsonIgnore
private String source;
@Schema(description = "客户来源", requiredMode = Schema.RequiredMode.REQUIRED, example = "外呼")
private String sourceName;
@Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@JsonIgnore
private Long ownerUserId; private Long ownerUserId;
@Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") @Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
private String ownerUserName; private String ownerUserName;
@Schema(description = "创建人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") @Schema(description = "创建人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@JsonIgnore private String creator;
private String creatorUserId;
@Schema(description = "创建人", requiredMode = Schema.RequiredMode.REQUIRED, example = "源码") @Schema(description = "创建人", requiredMode = Schema.RequiredMode.REQUIRED, example = "源码")
private String creatorUserName; private String creatorUserName;
@ -61,7 +43,6 @@ public class CrmStatisticsCustomerContractSummaryRespVO {
private LocalDateTime createTime; private LocalDateTime createTime;
@Schema(description = "下单日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02 00:00:00") @Schema(description = "下单日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02 00:00:00")
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY, timezone = TIME_ZONE_DEFAULT) private LocalDateTime orderDate;
private LocalDate orderDate;
} }

View File

@ -11,6 +11,6 @@ public class CrmStatisticsCustomerDealCycleByDateRespVO {
private String time; private String time;
@Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0") @Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0")
private Double customerDealCycle = 0.0; private Double customerDealCycle;
} }

View File

@ -8,9 +8,9 @@ import lombok.Data;
public class CrmStatisticsCustomerDealCycleByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO { public class CrmStatisticsCustomerDealCycleByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO {
@Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0") @Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0")
private Double customerDealCycle = 0.0; private Double customerDealCycle;
@Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer customerDealCount = 0; private Integer customerDealCount;
} }

View File

@ -30,12 +30,12 @@ public class CrmStatisticsCustomerReqVO {
* userIds 目前不用前端传递目前是方便后端通过 deptId 读取编号后设置回来 * userIds 目前不用前端传递目前是方便后端通过 deptId 读取编号后设置回来
* 后续可能会支持选择部分用户进行查询 * 后续可能会支持选择部分用户进行查询
*/ */
@Schema(description = "负责人用户 id 集合", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2") @Schema(description = "负责人用户 id 集合", hidden = true, example = "2")
private List<Long> userIds; private List<Long> userIds;
@Schema(description = "时间间隔类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "时间间隔类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@InEnum(value = DateIntervalEnum.class, message = "时间间隔类型,必须是 {value}") @InEnum(value = DateIntervalEnum.class, message = "时间间隔类型,必须是 {value}")
private Integer intervalType; private Integer interval;
/** /**
* 前端如果选择自定义时间, 那么前端传递起始-终止时间, 如果选择其他时间间隔类型, 则由后台计算起始-终止时间 * 前端如果选择自定义时间, 那么前端传递起始-终止时间, 如果选择其他时间间隔类型, 则由后台计算起始-终止时间
@ -49,7 +49,8 @@ public class CrmStatisticsCustomerReqVO {
* group by DATE_FORMAT(field, #{dateFormat}) * group by DATE_FORMAT(field, #{dateFormat})
* 非前端传递, 由Service计算后传递给Mapper的参数 * 非前端传递, 由Service计算后传递给Mapper的参数
*/ */
@Schema(description = "Group By 日期格式", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "%Y%m") @Deprecated
@Schema(description = "Group By 日期格式", hidden = true, example = "%Y%m")
private String sqlDateFormat; private String sqlDateFormat;
} }

View File

@ -11,9 +11,9 @@ public class CrmStatisticsCustomerSummaryByDateRespVO {
private String time; private String time;
@Schema(description = "新建客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "新建客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer customerCreateCount = 0; private Integer customerCreateCount;
@Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer customerDealCount = 0; private Integer customerDealCount;
} }

View File

@ -10,15 +10,15 @@ import java.math.BigDecimal;
public class CrmStatisticsCustomerSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO { public class CrmStatisticsCustomerSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO {
@Schema(description = "新建客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "新建客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer customerCreateCount = 0; private Integer customerCreateCount;
@Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer customerDealCount = 0; private Integer customerDealCount;
@Schema(description = "合同总金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") @Schema(description = "合同总金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
private BigDecimal contractPrice = BigDecimal.ZERO; private BigDecimal contractPrice;
@Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
private BigDecimal receivablePrice = BigDecimal.ZERO; private BigDecimal receivablePrice;
} }

View File

@ -5,15 +5,15 @@ import lombok.Data;
@Schema(description = "管理后台 - CRM 跟进次数分析(按日期) VO") @Schema(description = "管理后台 - CRM 跟进次数分析(按日期) VO")
@Data @Data
public class CrmStatisticsFollowupSummaryByDateRespVO { public class CrmStatisticsFollowUpSummaryByDateRespVO {
@Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401") @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401")
private String time; private String time;
@Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer followupRecordCount = 0; private Integer followUpRecordCount;
@Schema(description = "跟进客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "跟进客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer followupCustomerCount = 0; private Integer followUpCustomerCount;
} }

View File

@ -6,12 +6,12 @@ import lombok.Data;
@Schema(description = "管理后台 - CRM 跟进次数分析(按类型) VO") @Schema(description = "管理后台 - CRM 跟进次数分析(按类型) VO")
@Data @Data
public class CrmStatisticsFollowupSummaryByTypeRespVO { public class CrmStatisticsFollowUpSummaryByTypeRespVO {
@Schema(description = "跟进类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "跟进类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private String followupType; private Integer followUpType;
@Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer followupRecordCount = 0; private Integer followUpRecordCount;
} }

View File

@ -5,12 +5,12 @@ import lombok.Data;
@Schema(description = "管理后台 - CRM 跟进次数分析(按用户) VO") @Schema(description = "管理后台 - CRM 跟进次数分析(按用户) VO")
@Data @Data
public class CrmStatisticsFollowupSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO { public class CrmStatisticsFollowUpSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO {
@Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer followupRecordCount = 0; private Integer followUpRecordCount;
@Schema(description = "跟进客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "跟进客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer followupCustomerCount = 0; private Integer followUpCustomerCount;
} }

View File

@ -17,6 +17,7 @@ public class CrmStatisticsRankRespVO {
@Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private String deptName; private String deptName;
// TODO @芋艿需要改下金额是 bigdecimal
/** /**
* 数量是个特别抽象的概念在不同排行下代表不同含义 * 数量是个特别抽象的概念在不同排行下代表不同含义
* <p> * <p>

View File

@ -13,32 +13,32 @@ import java.util.List;
@Mapper @Mapper
public interface CrmStatisticsCustomerMapper { public interface CrmStatisticsCustomerMapper {
List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerCreateCountGroupByDate(CrmStatisticsCustomerReqVO reqVO); // 已经 review List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerCreateCountGroupByDate(CrmStatisticsCustomerReqVO reqVO);
List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerDealCountGroupByDate(CrmStatisticsCustomerReqVO reqVO); // 已经 review List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerDealCountGroupByDate(CrmStatisticsCustomerReqVO reqVO);
List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerCreateCountGroupByUser(CrmStatisticsCustomerReqVO reqVO); // 已经 review List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerCreateCountGroupByUser(CrmStatisticsCustomerReqVO reqVO);
List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerDealCountGroupByUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); // 已经 review List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerDealCountGroupByUser(CrmStatisticsCustomerReqVO reqVO);
List<CrmStatisticsCustomerSummaryByUserRespVO> selectContractPriceGroupByUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); // 已经 review List<CrmStatisticsCustomerSummaryByUserRespVO> selectContractPriceGroupByUser(CrmStatisticsCustomerReqVO reqVO);
List<CrmStatisticsCustomerSummaryByUserRespVO> selectReceivablePriceGroupByUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); // 已经 review List<CrmStatisticsCustomerSummaryByUserRespVO> selectReceivablePriceGroupByUser(CrmStatisticsCustomerReqVO reqVO);
List<CrmStatisticsFollowupSummaryByDateRespVO> selectFollowupRecordCountGroupByDate(CrmStatisticsCustomerReqVO reqVO); List<CrmStatisticsFollowUpSummaryByDateRespVO> selectFollowUpRecordCountGroupByDate(CrmStatisticsCustomerReqVO reqVO);
List<CrmStatisticsFollowupSummaryByDateRespVO> selectFollowupCustomerCountGroupByDate(CrmStatisticsCustomerReqVO reqVO); List<CrmStatisticsFollowUpSummaryByDateRespVO> selectFollowUpCustomerCountGroupByDate(CrmStatisticsCustomerReqVO reqVO);
List<CrmStatisticsFollowupSummaryByUserRespVO> selectFollowupRecordCountGroupByUser(CrmStatisticsCustomerReqVO reqVO); List<CrmStatisticsFollowUpSummaryByUserRespVO> selectFollowUpRecordCountGroupByUser(CrmStatisticsCustomerReqVO reqVO);
List<CrmStatisticsFollowupSummaryByUserRespVO> selectFollowupCustomerCountGroupByUser(CrmStatisticsCustomerReqVO reqVO); List<CrmStatisticsFollowUpSummaryByUserRespVO> selectFollowUpCustomerCountGroupByUser(CrmStatisticsCustomerReqVO reqVO);
List<CrmStatisticsCustomerContractSummaryRespVO> selectContractSummary(CrmStatisticsCustomerReqVO reqVO); List<CrmStatisticsCustomerContractSummaryRespVO> selectContractSummary(CrmStatisticsCustomerReqVO reqVO);
List<CrmStatisticsFollowupSummaryByTypeRespVO> selectFollowupRecordCountGroupByType(CrmStatisticsCustomerReqVO reqVO); List<CrmStatisticsFollowUpSummaryByTypeRespVO> selectFollowUpRecordCountGroupByType(CrmStatisticsCustomerReqVO reqVO);
List<CrmStatisticsCustomerDealCycleByDateRespVO> selectCustomerDealCycleGroupByDate(CrmStatisticsCustomerReqVO reqVO); List<CrmStatisticsCustomerDealCycleByDateRespVO> selectCustomerDealCycleGroupByDate(CrmStatisticsCustomerReqVO reqVO);
List<CrmStatisticsCustomerDealCycleByUserRespVO> selectCustomerDealCycleGroupByUser(CrmStatisticsCustomerReqVO reqVO); List<CrmStatisticsCustomerDealCycleByUserRespVO> selectCustomerDealCycleGroupByUser(CrmStatisticsCustomerReqVO reqVO); // TODO
} }

View File

@ -27,14 +27,13 @@ public interface CrmStatisticsCustomerService {
*/ */
List<CrmStatisticsCustomerSummaryByUserRespVO> getCustomerSummaryByUser(CrmStatisticsCustomerReqVO reqVO); List<CrmStatisticsCustomerSummaryByUserRespVO> getCustomerSummaryByUser(CrmStatisticsCustomerReqVO reqVO);
/** /**
* 跟进次数分析(按日期) * 跟进次数分析(按日期)
* *
* @param reqVO 请求参数 * @param reqVO 请求参数
* @return 统计数据 * @return 统计数据
*/ */
List<CrmStatisticsFollowupSummaryByDateRespVO> getFollowupSummaryByDate(CrmStatisticsCustomerReqVO reqVO); List<CrmStatisticsFollowUpSummaryByDateRespVO> getFollowUpSummaryByDate(CrmStatisticsCustomerReqVO reqVO);
/** /**
* 跟进次数分析(按用户) * 跟进次数分析(按用户)
@ -42,7 +41,7 @@ public interface CrmStatisticsCustomerService {
* @param reqVO 请求参数 * @param reqVO 请求参数
* @return 统计数据 * @return 统计数据
*/ */
List<CrmStatisticsFollowupSummaryByUserRespVO> getFollowupSummaryByUser(CrmStatisticsCustomerReqVO reqVO); List<CrmStatisticsFollowUpSummaryByUserRespVO> getFollowUpSummaryByUser(CrmStatisticsCustomerReqVO reqVO);
/** /**
* 客户跟进次数分析(按类型) * 客户跟进次数分析(按类型)
@ -50,8 +49,7 @@ public interface CrmStatisticsCustomerService {
* @param reqVO 请求参数 * @param reqVO 请求参数
* @return 统计数据 * @return 统计数据
*/ */
List<CrmStatisticsFollowupSummaryByTypeRespVO> getFollowupSummaryByType(CrmStatisticsCustomerReqVO reqVO); List<CrmStatisticsFollowUpSummaryByTypeRespVO> getFollowUpSummaryByType(CrmStatisticsCustomerReqVO reqVO);
/** /**
* 获取合同摘要信息(客户转化率页面) * 获取合同摘要信息(客户转化率页面)

View File

@ -1,19 +1,13 @@
package cn.iocoder.yudao.module.crm.service.statistics; package cn.iocoder.yudao.module.crm.service.statistics;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum; import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*;
import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper; import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper;
import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
@ -27,10 +21,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Stream; import java.util.stream.Stream;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*; import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.STATISTICS_CUSTOMER_TIMES_NOT_SET;
/** /**
* CRM 客户分析 Service 实现类 * CRM 客户分析 Service 实现类
@ -41,13 +33,6 @@ import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.STATISTICS_CU
@Validated @Validated
public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerService { public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerService {
private static final String SQL_DATE_FORMAT_BY_MONTH = "%Y%m";
private static final String SQL_DATE_FORMAT_BY_DAY = "%Y%m%d";
private static final String TIME_FORMAT_BY_MONTH = "yyyyMM";
private static final String TIME_FORMAT_BY_DAY = "yyyyMMdd";
@Resource @Resource
private CrmStatisticsCustomerMapper customerMapper; private CrmStatisticsCustomerMapper customerMapper;
@ -55,283 +40,211 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
private AdminUserApi adminUserApi; private AdminUserApi adminUserApi;
@Resource @Resource
private DeptApi deptApi; private DeptApi deptApi;
@Resource
private DictDataApi dictDataApi;
@Override @Override
public List<CrmStatisticsCustomerSummaryByDateRespVO> getCustomerSummaryByDate(CrmStatisticsCustomerReqVO reqVO) { public List<CrmStatisticsCustomerSummaryByDateRespVO> getCustomerSummaryByDate(CrmStatisticsCustomerReqVO reqVO) {
// 1. 获得用户编号数组 // 1. 获得用户编号数组
List<Long> userIds = getUserIds(reqVO); reqVO.setUserIds(getUserIds(reqVO));
if (CollUtil.isEmpty(userIds)) { if (CollUtil.isEmpty(reqVO.getUserIds())) {
return Collections.emptyList(); return Collections.emptyList();
} }
reqVO.setUserIds(userIds);
// 2. 获取分项统计数据 // 2. 按天统计获取分项统计数据
initParams(reqVO); List<CrmStatisticsCustomerSummaryByDateRespVO> customerCreateCountList = customerMapper.selectCustomerCreateCountGroupByDate(reqVO);
List<CrmStatisticsCustomerSummaryByDateRespVO> customerCreateCountVoList = customerMapper.selectCustomerCreateCountGroupByDate(reqVO); List<CrmStatisticsCustomerSummaryByDateRespVO> customerDealCountList = customerMapper.selectCustomerDealCountGroupByDate(reqVO);
List<CrmStatisticsCustomerSummaryByDateRespVO> customerDealCountVoList = customerMapper.selectCustomerDealCountGroupByDate(reqVO);
// 3. 合并数据 // 3. 按照日期间隔合并数据
List<String> times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]); List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval());
Map<String, Integer> customerCreateCountMap = convertMap(customerCreateCountVoList, return convertList(timeRanges, times -> {
CrmStatisticsCustomerSummaryByDateRespVO::getTime, Integer customerCreateCount = customerCreateCountList.stream()
CrmStatisticsCustomerSummaryByDateRespVO::getCustomerCreateCount); .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))
Map<String, Integer> customerDealCountMap = convertMap(customerDealCountVoList, .mapToInt(CrmStatisticsCustomerSummaryByDateRespVO::getCustomerCreateCount).sum();
CrmStatisticsCustomerSummaryByDateRespVO::getTime, Integer customerDealCount = customerDealCountList.stream()
CrmStatisticsCustomerSummaryByDateRespVO::getCustomerDealCount); .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))
List<CrmStatisticsCustomerSummaryByDateRespVO> respVoList = convertList(times, .mapToInt(CrmStatisticsCustomerSummaryByDateRespVO::getCustomerDealCount).sum();
time -> new CrmStatisticsCustomerSummaryByDateRespVO() return new CrmStatisticsCustomerSummaryByDateRespVO()
.setTime(time) .setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval()))
.setCustomerCreateCount(customerCreateCountMap.getOrDefault(time, 0)) .setCustomerCreateCount(customerCreateCount).setCustomerDealCount(customerDealCount);
.setCustomerDealCount(customerDealCountMap.getOrDefault(time, 0))); });
return respVoList;
} }
@Override @Override
public List<CrmStatisticsCustomerSummaryByUserRespVO> getCustomerSummaryByUser(CrmStatisticsCustomerReqVO reqVO) { public List<CrmStatisticsCustomerSummaryByUserRespVO> getCustomerSummaryByUser(CrmStatisticsCustomerReqVO reqVO) {
// 1. 获得用户编号数组 // 1. 获得用户编号数组
List<Long> userIds = getUserIds(reqVO); reqVO.setUserIds(getUserIds(reqVO));
if (CollUtil.isEmpty(userIds)) { if (CollUtil.isEmpty(reqVO.getUserIds())) {
return Collections.emptyList(); return Collections.emptyList();
} }
reqVO.setUserIds(userIds);
// 2. 获取分项统计数据 // 2. 按用户统计获取分项统计数据
initParams(reqVO); List<CrmStatisticsCustomerSummaryByUserRespVO> customerCreateCountList = customerMapper.selectCustomerCreateCountGroupByUser(reqVO);
List<CrmStatisticsCustomerSummaryByUserRespVO> customerCreateCount = customerMapper.selectCustomerCreateCountGroupByUser(reqVO); List<CrmStatisticsCustomerSummaryByUserRespVO> customerDealCountList = customerMapper.selectCustomerDealCountGroupByUser(reqVO);
List<CrmStatisticsCustomerSummaryByUserRespVO> customerDealCount = customerMapper.selectCustomerDealCountGroupByUser(reqVO); List<CrmStatisticsCustomerSummaryByUserRespVO> contractPriceList = customerMapper.selectContractPriceGroupByUser(reqVO);
List<CrmStatisticsCustomerSummaryByUserRespVO> contractPrice = customerMapper.selectContractPriceGroupByUser(reqVO); List<CrmStatisticsCustomerSummaryByUserRespVO> receivablePriceList = customerMapper.selectReceivablePriceGroupByUser(reqVO);
List<CrmStatisticsCustomerSummaryByUserRespVO> receivablePrice = customerMapper.selectReceivablePriceGroupByUser(reqVO);
// 3. 合并统计数据 // 3.1 按照用户合并统计数据
Map<Long, Integer> customerCreateCountMap = convertMap(customerCreateCount, List<CrmStatisticsCustomerSummaryByUserRespVO> summaryList = convertList(reqVO.getUserIds(), userId -> {
CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, Integer customerCreateCount = customerCreateCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))
CrmStatisticsCustomerSummaryByUserRespVO::getCustomerCreateCount); .mapToInt(CrmStatisticsCustomerSummaryByUserRespVO::getCustomerCreateCount).sum();
Map<Long, Integer> customerDealCountMap = convertMap(customerDealCount, Integer customerDealCount = customerDealCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))
CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, .mapToInt(CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount).sum();
CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount); BigDecimal contractPrice = contractPriceList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))
Map<Long, BigDecimal> contractPriceMap = convertMap(contractPrice, .reduce(BigDecimal.ZERO, (sum, vo) -> sum.add(vo.getContractPrice()), BigDecimal::add);
CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, BigDecimal receivablePrice = receivablePriceList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))
CrmStatisticsCustomerSummaryByUserRespVO::getContractPrice); .reduce(BigDecimal.ZERO, (sum, vo) -> sum.add(vo.getReceivablePrice()), BigDecimal::add);
Map<Long, BigDecimal> receivablePriceMap = convertMap(receivablePrice, return (CrmStatisticsCustomerSummaryByUserRespVO) new CrmStatisticsCustomerSummaryByUserRespVO()
CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, .setCustomerCreateCount(customerCreateCount).setCustomerDealCount(customerDealCount)
CrmStatisticsCustomerSummaryByUserRespVO::getReceivablePrice); .setContractPrice(contractPrice).setReceivablePrice(receivablePrice).setOwnerUserId(userId);
List<CrmStatisticsCustomerSummaryByUserRespVO> respVoList = convertList(userIds, userId -> {
CrmStatisticsCustomerSummaryByUserRespVO vo = new CrmStatisticsCustomerSummaryByUserRespVO();
// ownerUserId 为基类属性
vo.setOwnerUserId(userId);
vo.setCustomerCreateCount(customerCreateCountMap.getOrDefault(userId, 0))
.setCustomerDealCount(customerDealCountMap.getOrDefault(userId, 0))
.setContractPrice(contractPriceMap.getOrDefault(userId, BigDecimal.ZERO))
.setReceivablePrice(receivablePriceMap.getOrDefault(userId, BigDecimal.ZERO));
return vo;
}); });
// 3.2 拼接用户信息
// 4. 拼接用户信息 appendUserInfo(summaryList);
appendUserInfo(respVoList); return summaryList;
return respVoList;
} }
@Override @Override
public List<CrmStatisticsFollowupSummaryByDateRespVO> getFollowupSummaryByDate(CrmStatisticsCustomerReqVO reqVO) { public List<CrmStatisticsFollowUpSummaryByDateRespVO> getFollowUpSummaryByDate(CrmStatisticsCustomerReqVO reqVO) {
// 1. 获得用户编号数组 // 1. 获得用户编号数组
List<Long> userIds = getUserIds(reqVO); reqVO.setUserIds(getUserIds(reqVO));
if (CollUtil.isEmpty(userIds)) { if (CollUtil.isEmpty(reqVO.getUserIds())) {
return Collections.emptyList(); return Collections.emptyList();
} }
reqVO.setUserIds(userIds);
// 2. 获取分项统计数据 // 2. 按天统计获取分项统计数据
initParams(reqVO); List<CrmStatisticsFollowUpSummaryByDateRespVO> followUpRecordCountList = customerMapper.selectFollowUpRecordCountGroupByDate(reqVO);
List<CrmStatisticsFollowupSummaryByDateRespVO> followupRecordCount = customerMapper.selectFollowupRecordCountGroupByDate(reqVO); List<CrmStatisticsFollowUpSummaryByDateRespVO> followUpCustomerCountList = customerMapper.selectFollowUpCustomerCountGroupByDate(reqVO);
List<CrmStatisticsFollowupSummaryByDateRespVO> followupCustomerCount = customerMapper.selectFollowupCustomerCountGroupByDate(reqVO);
// 3. 合并统计数据 // 3. 按照时间间隔合并统计数据
List<String> times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]); List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval());
Map<String, Integer> followupRecordCountMap = convertMap(followupRecordCount, return convertList(timeRanges, times -> {
CrmStatisticsFollowupSummaryByDateRespVO::getTime, Integer followUpRecordCount = followUpRecordCountList.stream()
CrmStatisticsFollowupSummaryByDateRespVO::getFollowupRecordCount); .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))
Map<String, Integer> followupCustomerCountMap = convertMap(followupCustomerCount, .mapToInt(CrmStatisticsFollowUpSummaryByDateRespVO::getFollowUpRecordCount).sum();
CrmStatisticsFollowupSummaryByDateRespVO::getTime, Integer followUpCustomerCount = followUpCustomerCountList.stream()
CrmStatisticsFollowupSummaryByDateRespVO::getFollowupCustomerCount); .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))
List<CrmStatisticsFollowupSummaryByDateRespVO> respVoList = convertList(times, time -> .mapToInt(CrmStatisticsFollowUpSummaryByDateRespVO::getFollowUpCustomerCount).sum();
new CrmStatisticsFollowupSummaryByDateRespVO().setTime(time) return new CrmStatisticsFollowUpSummaryByDateRespVO()
.setFollowupRecordCount(followupRecordCountMap.getOrDefault(time, 0)) .setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval()))
.setFollowupCustomerCount(followupCustomerCountMap.getOrDefault(time, 0)) .setFollowUpCustomerCount(followUpRecordCount).setFollowUpRecordCount(followUpCustomerCount);
); });
return respVoList;
} }
@Override @Override
public List<CrmStatisticsFollowupSummaryByUserRespVO> getFollowupSummaryByUser(CrmStatisticsCustomerReqVO reqVO) { public List<CrmStatisticsFollowUpSummaryByUserRespVO> getFollowUpSummaryByUser(CrmStatisticsCustomerReqVO reqVO) {
// 1. 获得用户编号数组 // 1. 获得用户编号数组
List<Long> userIds = getUserIds(reqVO); reqVO.setUserIds(getUserIds(reqVO));
if (CollUtil.isEmpty(userIds)) { if (CollUtil.isEmpty(reqVO.getUserIds())) {
return Collections.emptyList(); return Collections.emptyList();
} }
reqVO.setUserIds(userIds);
// 2. 获取分项统计数据 // 2. 按用户统计获取分项统计数据
initParams(reqVO); List<CrmStatisticsFollowUpSummaryByUserRespVO> followUpRecordCountList = customerMapper.selectFollowUpRecordCountGroupByUser(reqVO);
List<CrmStatisticsFollowupSummaryByUserRespVO> followupRecordCount = customerMapper.selectFollowupRecordCountGroupByUser(reqVO); List<CrmStatisticsFollowUpSummaryByUserRespVO> followUpCustomerCountList = customerMapper.selectFollowUpCustomerCountGroupByUser(reqVO);
List<CrmStatisticsFollowupSummaryByUserRespVO> followupCustomerCount = customerMapper.selectFollowupCustomerCountGroupByUser(reqVO);
// 3. 合并统计数据 // 3.1 按照用户合并统计数据
Map<Long, Integer> followupRecordCountMap = convertMap(followupRecordCount, List<CrmStatisticsFollowUpSummaryByUserRespVO> summaryList = convertList(reqVO.getUserIds(), userId -> {
CrmStatisticsFollowupSummaryByUserRespVO::getOwnerUserId, Integer followUpRecordCount = followUpRecordCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))
CrmStatisticsFollowupSummaryByUserRespVO::getFollowupRecordCount); .mapToInt(CrmStatisticsFollowUpSummaryByUserRespVO::getFollowUpRecordCount).sum();
Map<Long, Integer> followupCustomerCountMap = convertMap(followupCustomerCount, Integer followUpCustomerCount = followUpCustomerCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))
CrmStatisticsFollowupSummaryByUserRespVO::getOwnerUserId, .mapToInt(CrmStatisticsFollowUpSummaryByUserRespVO::getFollowUpCustomerCount).sum();
CrmStatisticsFollowupSummaryByUserRespVO::getFollowupCustomerCount); return (CrmStatisticsFollowUpSummaryByUserRespVO) new CrmStatisticsFollowUpSummaryByUserRespVO()
List<CrmStatisticsFollowupSummaryByUserRespVO> respVoList = convertList(userIds, userId -> { .setFollowUpCustomerCount(followUpRecordCount).setFollowUpRecordCount(followUpCustomerCount).setOwnerUserId(userId);
CrmStatisticsFollowupSummaryByUserRespVO vo = new CrmStatisticsFollowupSummaryByUserRespVO()
.setFollowupRecordCount(followupRecordCountMap.getOrDefault(userId, 0))
.setFollowupCustomerCount(followupCustomerCountMap.getOrDefault(userId, 0));
// ownerUserId 为基类属性
vo.setOwnerUserId(userId);
return vo;
}); });
// 3.2 拼接用户信息
// 4. 拼接用户信息 appendUserInfo(summaryList);
appendUserInfo(respVoList); return summaryList;
return respVoList;
} }
@Override @Override
public List<CrmStatisticsFollowupSummaryByTypeRespVO> getFollowupSummaryByType(CrmStatisticsCustomerReqVO reqVO) { public List<CrmStatisticsFollowUpSummaryByTypeRespVO> getFollowUpSummaryByType(CrmStatisticsCustomerReqVO reqVO) {
// 1. 获得用户编号数组 // 1. 获得用户编号数组
List<Long> userIds = getUserIds(reqVO); reqVO.setUserIds(getUserIds(reqVO));
if (CollUtil.isEmpty(userIds)) { if (CollUtil.isEmpty(reqVO.getUserIds())) {
return Collections.emptyList(); return Collections.emptyList();
} }
reqVO.setUserIds(userIds);
// 2. 获得排行数据 // 2. 获得跟进数据
initParams(reqVO); return customerMapper.selectFollowUpRecordCountGroupByType(reqVO);
List<CrmStatisticsFollowupSummaryByTypeRespVO> respVoList = customerMapper.selectFollowupRecordCountGroupByType(reqVO);
// 3. 获取字典数据
List<DictDataRespDTO> followUpTypes = dictDataApi.getDictDataList(CRM_FOLLOW_UP_TYPE);
Map<String, String> followUpTypeMap = convertMap(followUpTypes,
DictDataRespDTO::getValue, DictDataRespDTO::getLabel);
respVoList.forEach(vo -> {
vo.setFollowupType(followUpTypeMap.get(vo.getFollowupType()));
});
return respVoList;
} }
@Override @Override
public List<CrmStatisticsCustomerContractSummaryRespVO> getContractSummary(CrmStatisticsCustomerReqVO reqVO) { public List<CrmStatisticsCustomerContractSummaryRespVO> getContractSummary(CrmStatisticsCustomerReqVO reqVO) {
// 1. 获得用户编号数组 // 1. 获得用户编号数组
List<Long> userIds = getUserIds(reqVO); reqVO.setUserIds(getUserIds(reqVO));
if (CollUtil.isEmpty(userIds)) { if (CollUtil.isEmpty(reqVO.getUserIds())) {
return Collections.emptyList(); return Collections.emptyList();
} }
reqVO.setUserIds(userIds);
// 2. 获取统计数据 // 2. 按用户统计获取统计数据
initParams(reqVO); List<CrmStatisticsCustomerContractSummaryRespVO> summaryList = customerMapper.selectContractSummary(reqVO);
List<CrmStatisticsCustomerContractSummaryRespVO> respVoList = customerMapper.selectContractSummary(reqVO);
// 3. 设置 创建人负责人行业来源 // 3. 拼接信息
// 3.1 获取客户所属行业 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
Map<String, String> industryMap = convertMap(dictDataApi.getDictDataList(CRM_CUSTOMER_INDUSTRY), convertSetByFlatMap(summaryList, vo -> Stream.of(NumberUtils.parseLong(vo.getCreator()), vo.getOwnerUserId())));
DictDataRespDTO::getValue, DictDataRespDTO::getLabel); summaryList.forEach(vo -> {
// 3.2 获取客户来源 findAndThen(userMap, NumberUtils.parseLong(vo.getCreator()), user -> vo.setCreatorUserName(user.getNickname()));
Map<String, String> sourceMap = convertMap(dictDataApi.getDictDataList(CRM_CUSTOMER_SOURCE), findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname()));
DictDataRespDTO::getValue, DictDataRespDTO::getLabel);
// 3.3 获取创建人负责人列表
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSetByFlatMap(respVoList,
vo -> Stream.of(NumberUtils.parseLong(vo.getCreatorUserId()), vo.getOwnerUserId())));
// 3.4 设置 创建人负责人行业来源
respVoList.forEach(vo -> {
MapUtils.findAndThen(industryMap, vo.getIndustryId(), vo::setIndustryName);
MapUtils.findAndThen(sourceMap, vo.getSource(), vo::setSourceName);
MapUtils.findAndThen(userMap, NumberUtils.parseLong(vo.getCreatorUserId()),
user -> vo.setCreatorUserName(user.getNickname()));
MapUtils.findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname()));
}); });
return summaryList;
return respVoList;
} }
@Override @Override
public List<CrmStatisticsCustomerDealCycleByDateRespVO> getCustomerDealCycleByDate(CrmStatisticsCustomerReqVO reqVO) { public List<CrmStatisticsCustomerDealCycleByDateRespVO> getCustomerDealCycleByDate(CrmStatisticsCustomerReqVO reqVO) {
// 1. 获得用户编号数组 // 1. 获得用户编号数组
List<Long> userIds = getUserIds(reqVO); reqVO.setUserIds(getUserIds(reqVO));
if (CollUtil.isEmpty(userIds)) { if (CollUtil.isEmpty(reqVO.getUserIds())) {
return Collections.emptyList(); return Collections.emptyList();
} }
reqVO.setUserIds(userIds);
// 2. 获取分项统计数据 // 2. 按天统计获取分项统计数据
initParams(reqVO); List<CrmStatisticsCustomerDealCycleByDateRespVO> customerDealCycleList = customerMapper.selectCustomerDealCycleGroupByDate(reqVO);
List<CrmStatisticsCustomerDealCycleByDateRespVO> customerDealCycle = customerMapper.selectCustomerDealCycleGroupByDate(reqVO);
// 3. 合并统计数据 // 3. 按照日期间隔合并统计数据
List<String> times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]); List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval());
Map<String, Double> customerDealCycleMap = convertMap(customerDealCycle, return convertList(timeRanges, times -> {
CrmStatisticsCustomerDealCycleByDateRespVO::getTime, Double customerDealCycle = customerDealCycleList.stream()
CrmStatisticsCustomerDealCycleByDateRespVO::getCustomerDealCycle); .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))
List<CrmStatisticsCustomerDealCycleByDateRespVO> respVoList = convertList(times, time -> .mapToDouble(CrmStatisticsCustomerDealCycleByDateRespVO::getCustomerDealCycle).sum();
new CrmStatisticsCustomerDealCycleByDateRespVO().setTime(time) return new CrmStatisticsCustomerDealCycleByDateRespVO()
.setCustomerDealCycle(customerDealCycleMap.getOrDefault(time, 0D)) .setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval()))
); .setCustomerDealCycle(customerDealCycle);
});
return respVoList;
} }
@Override @Override
public List<CrmStatisticsCustomerDealCycleByUserRespVO> getCustomerDealCycleByUser(CrmStatisticsCustomerReqVO reqVO) { public List<CrmStatisticsCustomerDealCycleByUserRespVO> getCustomerDealCycleByUser(CrmStatisticsCustomerReqVO reqVO) {
// 1. 获得用户编号数组 // 1. 获得用户编号数组
List<Long> userIds = getUserIds(reqVO); reqVO.setUserIds(getUserIds(reqVO));
if (CollUtil.isEmpty(userIds)) { if (CollUtil.isEmpty(reqVO.getUserIds())) {
return Collections.emptyList(); return Collections.emptyList();
} }
reqVO.setUserIds(userIds);
// 2. 获取分项统计数据 // 2. 按用户统计获取分项统计数据
initParams(reqVO); List<CrmStatisticsCustomerDealCycleByUserRespVO> customerDealCycleList = customerMapper.selectCustomerDealCycleGroupByUser(reqVO);
List<CrmStatisticsCustomerDealCycleByUserRespVO> customerDealCycle = customerMapper.selectCustomerDealCycleGroupByUser(reqVO); List<CrmStatisticsCustomerSummaryByUserRespVO> customerDealCountList = customerMapper.selectCustomerDealCountGroupByUser(reqVO);
List<CrmStatisticsCustomerSummaryByUserRespVO> customerDealCount = customerMapper.selectCustomerDealCountGroupByUser(reqVO);
// 3. 合并统计数据 // 3.1 按照用户合并统计数据
Map<Long, Double> customerDealCycleMap = convertMap(customerDealCycle, List<CrmStatisticsCustomerDealCycleByUserRespVO> summaryList = convertList(reqVO.getUserIds(), userId -> {
CrmStatisticsCustomerDealCycleByUserRespVO::getOwnerUserId, Double customerDealCycle = customerDealCycleList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))
CrmStatisticsCustomerDealCycleByUserRespVO::getCustomerDealCycle); .mapToDouble(CrmStatisticsCustomerDealCycleByUserRespVO::getCustomerDealCycle).sum();
Map<Long, Integer> customerDealCountMap = convertMap(customerDealCount, Integer customerDealCount = customerDealCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId()))
CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, .mapToInt(CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount).sum();
CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount); return (CrmStatisticsCustomerDealCycleByUserRespVO) new CrmStatisticsCustomerDealCycleByUserRespVO()
List<CrmStatisticsCustomerDealCycleByUserRespVO> respVoList = convertList(userIds, userId -> { .setCustomerDealCycle(customerDealCycle).setCustomerDealCount(customerDealCount).setOwnerUserId(userId);
CrmStatisticsCustomerDealCycleByUserRespVO vo = new CrmStatisticsCustomerDealCycleByUserRespVO()
.setCustomerDealCycle(customerDealCycleMap.getOrDefault(userId, 0.0))
.setCustomerDealCount(customerDealCountMap.getOrDefault(userId, 0));
// ownerUserId 为基类属性
vo.setOwnerUserId(userId);
return vo;
}); });
// 3.2 拼接用户信息
// 4. 拼接用户信息 appendUserInfo(summaryList);
appendUserInfo(respVoList); return summaryList;
return respVoList;
} }
/** /**
* 拼接用户信息昵称 * 拼接用户信息昵称
* *
* @param respVoList 统计数据 * @param voList 统计数据
*/ */
private <T extends CrmStatisticsCustomerByUserBaseRespVO> void appendUserInfo(List<T> respVoList) { private <T extends CrmStatisticsCustomerByUserBaseRespVO> void appendUserInfo(List<T> voList) {
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(respVoList, Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
CrmStatisticsCustomerByUserBaseRespVO::getOwnerUserId)); convertSet(voList, CrmStatisticsCustomerByUserBaseRespVO::getOwnerUserId));
respVoList.forEach(vo -> MapUtils.findAndThen(userMap, voList.forEach(vo -> findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname())));
vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname())));
} }
/** /**
@ -347,113 +260,10 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
} }
// 情况二选中某个部门 // 情况二选中某个部门
// 2.1 获得部门列表 // 2.1 获得部门列表
Long deptId = reqVO.getDeptId(); List<Long> deptIds = convertList(deptApi.getChildDeptList(reqVO.getDeptId()), DeptRespDTO::getId);
List<Long> deptIds = convertList(deptApi.getChildDeptList(deptId), DeptRespDTO::getId); deptIds.add(reqVO.getDeptId());
deptIds.add(deptId);
// 2.2 获得用户编号 // 2.2 获得用户编号
return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId); return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId);
} }
/**
* 判断是否按照 月粒度 统计
*
* @param startTime 开始时间
* @param endTime 结束时间
* @return , 按月粒度, 否则按天粒度统计
*/
private boolean queryByMonth(LocalDateTime startTime, LocalDateTime endTime) {
return LocalDateTimeUtil.between(startTime, endTime).toDays() > 31;
}
/**
* 生成时间序列
*
* @param startTime 开始时间
* @param endTime 结束时间
* @return 时间序列
*/
private List<String> generateTimeSeries(LocalDateTime startTime, LocalDateTime endTime) {
boolean byMonth = queryByMonth(startTime, endTime);
List<String> times = CollUtil.newArrayList();
while (!startTime.isAfter(endTime)) {
times.add(LocalDateTimeUtil.format(startTime, byMonth ? TIME_FORMAT_BY_MONTH : TIME_FORMAT_BY_DAY));
if (byMonth)
startTime = startTime.plusMonths(1);
else
startTime = startTime.plusDays(1);
}
return times;
}
/**
* 获取 SQL 查询 GROUP BY 的时间格式
*
* @param startTime 开始时间
* @param endTime 结束时间
* @return SQL 查询 GROUP BY 的时间格式
*/
private String getSqlDateFormat(LocalDateTime startTime, LocalDateTime endTime) {
return queryByMonth(startTime, endTime) ? SQL_DATE_FORMAT_BY_MONTH : SQL_DATE_FORMAT_BY_DAY;
}
private void initParams(CrmStatisticsCustomerReqVO reqVO) {
final Integer intervalType = reqVO.getIntervalType();
// 1. 自定义时间间隔必须输入起始日期-结束日期
if (DateIntervalEnum.CUSTOMER.getType().equals(intervalType)) {
if (ObjUtil.isEmpty(reqVO.getTimes()) || reqVO.getTimes().length != 2) {
throw exception(STATISTICS_CUSTOMER_TIMES_NOT_SET);
}
// 设置 mapper sqlDateFormat 参数
reqVO.setSqlDateFormat(getSqlDateFormat(reqVO.getTimes()[0], reqVO.getTimes()[1]));
// 自定义日期无需计算日期参数
return;
}
// 2. 根据时间区间类型计算时间段区间日期
DateTime beginDate = null;
DateTime endDate = null;
if (DateIntervalEnum.TODAY.getType().equals(intervalType)) {
beginDate = DateUtil.beginOfDay(DateUtil.date());
endDate = DateUtil.endOfDay(DateUtil.date());
} else if (DateIntervalEnum.YESTERDAY.getType().equals(intervalType)) {
beginDate = DateUtil.offsetDay(DateUtil.date(), -1);
endDate = DateUtil.offsetDay(DateUtil.date(), -1);
} else if (DateIntervalEnum.THIS_WEEK.getType().equals(intervalType)) {
beginDate = DateUtil.beginOfWeek(DateUtil.date());
endDate = DateUtil.endOfWeek(DateUtil.date());
} else if (DateIntervalEnum.LAST_WEEK.getType().equals(intervalType)) {
beginDate = DateUtil.beginOfWeek(DateUtil.offsetWeek(DateUtil.date(), -1));
endDate = DateUtil.endOfWeek(DateUtil.offsetWeek(DateUtil.date(), -1));
} else if (DateIntervalEnum.THIS_MONTH.getType().equals(intervalType)) {
beginDate = DateUtil.beginOfMonth(DateUtil.date());
endDate = DateUtil.endOfMonth(DateUtil.date());
} else if (DateIntervalEnum.LAST_MONTH.getType().equals(intervalType)) {
beginDate = DateUtil.beginOfMonth(DateUtil.offsetMonth(DateUtil.date(), -1));
endDate = DateUtil.endOfMonth(DateUtil.offsetMonth(DateUtil.date(), -1));
} else if (DateIntervalEnum.THIS_QUARTER.getType().equals(intervalType)) {
beginDate = DateUtil.beginOfQuarter(DateUtil.date());
endDate = DateUtil.endOfQuarter(DateUtil.date());
} else if (DateIntervalEnum.LAST_QUARTER.getType().equals(intervalType)) {
beginDate = DateUtil.beginOfQuarter(DateUtil.offsetMonth(DateUtil.date(), -3));
endDate = DateUtil.endOfQuarter(DateUtil.offsetMonth(DateUtil.date(), -3));
} else if (DateIntervalEnum.THIS_YEAR.getType().equals(intervalType)) {
beginDate = DateUtil.beginOfYear(DateUtil.date());
endDate = DateUtil.endOfYear(DateUtil.date());
} else if (DateIntervalEnum.LAST_YEAR.getType().equals(intervalType)) {
beginDate = DateUtil.beginOfYear(DateUtil.offsetMonth(DateUtil.date(), -12));
endDate = DateUtil.endOfYear(DateUtil.offsetMonth(DateUtil.date(), -12));
}
// 3. 计算开始结束日期时间并设置reqVo
LocalDateTime[] times = new LocalDateTime[2];
times[0] = LocalDateTimeUtil.beginOfDay(LocalDateTimeUtil.of(beginDate));
times[1] = LocalDateTimeUtil.endOfDay(LocalDateTimeUtil.of(endDate));
// 3.1 设置 mapper 时间区间 参数
reqVO.setTimes(times);
// 3.2 设置 mapper sqlDateFormat 参数
reqVO.setSqlDateFormat(getSqlDateFormat(times[0], times[1]));
}
} }

View File

@ -5,31 +5,31 @@
<select id="selectCustomerCreateCountGroupByDate" <select id="selectCustomerCreateCountGroupByDate"
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByDateRespVO"> resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByDateRespVO">
SELECT SELECT
DATE_FORMAT( create_time, #{sqlDateFormat} ) AS time, DATE_FORMAT(create_time, '%Y-%m-%d') AS time,
COUNT(*) AS customerCreateCount COUNT(*) AS customerCreateCount
FROM crm_customer FROM crm_customer
WHERE deleted = 0 WHERE deleted = 0
AND owner_user_id IN AND owner_user_id IN
<foreach collection="userIds" item="userId" open="(" close=")" separator=","> <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
#{userId} #{userId}
</foreach> </foreach>
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
GROUP BY time GROUP BY time
</select> </select>
<select id="selectCustomerDealCountGroupByDate" <select id="selectCustomerDealCountGroupByDate"
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByDateRespVO"> resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByDateRespVO">
SELECT SELECT
DATE_FORMAT( order_date, #{sqlDateFormat} ) AS time, DATE_FORMAT( order_date, '%Y-%m-%d' ) AS time,
COUNT( DISTINCT customer_id ) AS customerDealCount COUNT( DISTINCT customer_id ) AS customerDealCount
FROM crm_contract FROM crm_contract
WHERE deleted = 0 WHERE deleted = 0
AND audit_status = 20 AND audit_status = 20
AND owner_user_id IN AND owner_user_id IN
<foreach collection="userIds" item="userId" open="(" close=")" separator=","> <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
#{userId} #{userId}
</foreach> </foreach>
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
GROUP BY time GROUP BY time
</select> </select>
@ -38,13 +38,13 @@
SELECT SELECT
owner_user_id, owner_user_id,
COUNT(1) AS customer_create_count COUNT(1) AS customer_create_count
FROM crm_customer FROM crm_customer
WHERE deleted = 0 WHERE deleted = 0
AND owner_user_id in AND owner_user_id in
<foreach collection="userIds" item="userId" open="(" close=")" separator=","> <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
#{userId} #{userId}
</foreach> </foreach>
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
GROUP BY owner_user_id GROUP BY owner_user_id
</select> </select>
@ -53,16 +53,16 @@
SELECT SELECT
customer.owner_user_id, customer.owner_user_id,
COUNT( DISTINCT customer.id ) AS customer_deal_count COUNT( DISTINCT customer.id ) AS customer_deal_count
FROM crm_customer AS customer FROM crm_customer AS customer
LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id
WHERE customer.deleted = 0 AND contract.deleted = 0 WHERE customer.deleted = 0 AND contract.deleted = 0
AND contract.audit_status = 20 AND contract.audit_status = 20
AND customer.owner_user_id IN AND customer.owner_user_id IN
<foreach collection="userIds" item="userId" open="(" close=")" separator=","> <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
#{userId} #{userId}
</foreach> </foreach>
AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
GROUP BY customer.owner_user_id GROUP BY customer.owner_user_id
</select> </select>
<select id="selectContractPriceGroupByUser" <select id="selectContractPriceGroupByUser"
@ -70,15 +70,15 @@
SELECT SELECT
owner_user_id, owner_user_id,
IFNULL(SUM(total_price), 0) AS contract_price IFNULL(SUM(total_price), 0) AS contract_price
FROM crm_contract FROM crm_contract
WHERE deleted = 0 WHERE deleted = 0
AND audit_status = 20 AND audit_status = 20
AND owner_user_id in AND owner_user_id in
<foreach collection="userIds" item="userId" open="(" close=")" separator=","> <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
#{userId} #{userId}
</foreach> </foreach>
AND order_date BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} AND order_date BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
GROUP BY owner_user_id GROUP BY owner_user_id
</select> </select>
<select id="selectReceivablePriceGroupByUser" <select id="selectReceivablePriceGroupByUser"
@ -86,132 +86,132 @@
SELECT SELECT
owner_user_id, owner_user_id,
IFNULL(SUM(price), 0) AS receivable_price IFNULL(SUM(price), 0) AS receivable_price
FROM crm_receivable FROM crm_receivable
WHERE deleted = 0 WHERE deleted = 0
AND audit_status = 20 AND audit_status = 20
AND owner_user_id IN AND owner_user_id IN
<foreach collection="userIds" item="userId" open="(" close=")" separator=","> <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
#{userId} #{userId}
</foreach> </foreach>
AND return_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} AND return_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
GROUP BY owner_user_id GROUP BY owner_user_id
</select> </select>
<select id="selectFollowupRecordCountGroupByDate" <select id="selectFollowUpRecordCountGroupByDate"
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowupSummaryByDateRespVO"> resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowUpSummaryByDateRespVO">
SELECT SELECT
DATE_FORMAT( create_time, #{sqlDateFormat} ) AS time, DATE_FORMAT( create_time, '%Y-%m-%d' ) AS time,
COUNT(*) AS followup_record_count COUNT(*) AS follow_up_record_count
FROM crm_follow_up_record FROM crm_follow_up_record
WHERE creator IN WHERE creator IN
<foreach collection="userIds" item="userId" open="(" close=")" separator=","> <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
#{userId} #{userId}
</foreach> </foreach>
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type} AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
GROUP BY time GROUP BY time
</select> </select>
<select id="selectFollowupCustomerCountGroupByDate" <select id="selectFollowUpCustomerCountGroupByDate"
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowupSummaryByDateRespVO"> resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowUpSummaryByDateRespVO">
SELECT SELECT
DATE_FORMAT( create_time, #{sqlDateFormat} ) AS time, DATE_FORMAT( create_time, '%Y-%m-%d' ) AS time,
COUNT(DISTINCT biz_id) AS followup_customer_count COUNT(DISTINCT biz_id) AS follow_up_customer_count
FROM crm_follow_up_record FROM crm_follow_up_record
WHERE creator IN WHERE creator IN
<foreach collection="userIds" item="userId" open="(" close=")" separator=","> <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
#{userId} #{userId}
</foreach> </foreach>
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type} AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
GROUP BY time GROUP BY time
</select> </select>
<select id="selectFollowupRecordCountGroupByUser" <select id="selectFollowUpRecordCountGroupByUser"
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowupSummaryByUserRespVO"> resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowUpSummaryByUserRespVO">
SELECT SELECT
creator as owner_user_id, creator as owner_user_id,
COUNT(*) AS followup_record_count COUNT(*) AS follow_up_record_count
FROM crm_follow_up_record FROM crm_follow_up_record
WHERE creator IN WHERE creator IN
<foreach collection="userIds" item="userId" open="(" close=")" separator=","> <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
#{userId} #{userId}
</foreach> </foreach>
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type} AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
GROUP BY creator GROUP BY creator
</select> </select>
<select id="selectFollowupCustomerCountGroupByUser" <select id="selectFollowUpCustomerCountGroupByUser"
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowupSummaryByUserRespVO"> resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowUpSummaryByUserRespVO">
SELECT SELECT
creator as owner_user_id, creator as owner_user_id,
COUNT(DISTINCT biz_id) AS followup_customer_count COUNT(DISTINCT biz_id) AS follow_up_customer_count
FROM crm_follow_up_record FROM crm_follow_up_record
WHERE creator IN WHERE creator IN
<foreach collection="userIds" item="userId" open="(" close=")" separator=","> <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
#{userId} #{userId}
</foreach> </foreach>
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type} AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
GROUP BY creator GROUP BY creator
</select> </select>
<select id="selectFollowupRecordCountGroupByType" <select id="selectFollowUpRecordCountGroupByType"
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowupSummaryByTypeRespVO"> resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowUpSummaryByTypeRespVO">
SELECT SELECT
type AS followupType, type AS follow_up_type,
COUNT(*) AS followup_record_count COUNT(*) AS follow_up_record_count
FROM crm_follow_up_record FROM crm_follow_up_record
WHERE creator IN WHERE creator IN
<foreach collection="userIds" item="userId" open="(" close=")" separator=","> <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
#{userId} #{userId}
</foreach> </foreach>
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type} AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
GROUP BY followupType GROUP BY follow_up_type
</select> </select>
<select id="selectContractSummary" <select id="selectContractSummary"
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerContractSummaryRespVO"> resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerContractSummaryRespVO">
SELECT SELECT
customer.`name` AS customer_name, customer.name AS customer_name,
contract.`name` AS contract_name, contract.name AS contract_name,
contract.total_price, contract.total_price,
IFNULL( receivable.price, 0 ) AS receivable_price, IFNULL( receivable.price, 0 ) AS receivable_price,
customer.industry_id, customer.industry_id,
customer.source, customer.source,
customer.owner_user_id, customer.owner_user_id,
customer.creator AS creator_user_id, customer.creator,
customer.create_time, customer.create_time,
contract.order_date contract.order_date
FROM crm_customer AS customer FROM crm_customer AS customer
INNER JOIN crm_contract AS contract ON customer.id = contract.customer_id INNER JOIN crm_contract AS contract ON customer.id = contract.customer_id
LEFT JOIN crm_receivable AS receivable ON contract.id = receivable.contract_id LEFT JOIN crm_receivable AS receivable ON contract.id = receivable.contract_id
WHERE customer.deleted = 0 AND contract.deleted = 0 AND receivable.deleted = 0 WHERE customer.deleted = 0 AND contract.deleted = 0 AND receivable.deleted = 0
AND contract.audit_status = 20 AND contract.audit_status = 20
AND customer.owner_user_id IN AND customer.owner_user_id IN
<foreach collection="userIds" item="userId" open="(" close=")" separator=","> <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
#{userId} #{userId}
</foreach> </foreach>
AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
</select> </select>
<select id="selectCustomerDealCycleGroupByDate" <select id="selectCustomerDealCycleGroupByDate"
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerDealCycleByDateRespVO"> resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerDealCycleByDateRespVO">
SELECT SELECT
DATE_FORMAT( contract.order_date, #{sqlDateFormat} ) AS time, DATE_FORMAT( contract.order_date, '%Y-%m-%d' ) AS time,
IFNULL( TRUNCATE ( AVG( TIMESTAMPDIFF( DAY, customer.create_time, contract.order_date )), 1 ), 0 ) AS customer_deal_cycle IFNULL( TRUNCATE ( AVG( TIMESTAMPDIFF( DAY, customer.create_time, contract.order_date )), 1 ), 0 ) AS customer_deal_cycle
FROM crm_customer AS customer FROM crm_customer AS customer
LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id
WHERE customer.deleted = 0 AND contract.deleted = 0 WHERE customer.deleted = 0 AND contract.deleted = 0
AND contract.audit_status = 20 AND contract.audit_status = 20
AND customer.owner_user_id IN AND customer.owner_user_id IN
<foreach collection="userIds" item="userId" open="(" close=")" separator=","> <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
#{userId} #{userId}
</foreach> </foreach>
AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
GROUP BY time GROUP BY time
</select> </select>
<select id="selectCustomerDealCycleGroupByUser" <select id="selectCustomerDealCycleGroupByUser"
@ -219,16 +219,16 @@
SELECT SELECT
customer.owner_user_id, customer.owner_user_id,
IFNULL( TRUNCATE ( AVG( TIMESTAMPDIFF( DAY, customer.create_time, contract.order_date )), 1 ), 0 ) AS customer_deal_cycle IFNULL( TRUNCATE ( AVG( TIMESTAMPDIFF( DAY, customer.create_time, contract.order_date )), 1 ), 0 ) AS customer_deal_cycle
FROM crm_customer AS customer FROM crm_customer AS customer
LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id
WHERE customer.deleted = 0 AND contract.deleted = 0 WHERE customer.deleted = 0 AND contract.deleted = 0
AND contract.audit_status = 20 AND contract.audit_status = 20
AND customer.owner_user_id IN AND customer.owner_user_id IN
<foreach collection="userIds" item="userId" open="(" close=")" separator=","> <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
#{userId} #{userId}
</foreach> </foreach>
AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
GROUP BY customer.owner_user_id GROUP BY customer.owner_user_id
</select> </select>
</mapper> </mapper>