diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DateIntervalEnum.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DateIntervalEnum.java new file mode 100644 index 000000000..9a614ddc9 --- /dev/null +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DateIntervalEnum.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.framework.common.enums; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 时间间隔类型枚举 + * + * @author dhb52 + */ +@Getter +@AllArgsConstructor +public enum DateIntervalEnum implements IntArrayValuable { + + TODAY(1, "今天"), + YESTERDAY(2, "昨天"), + THIS_WEEK(3, "本周"), + LAST_WEEK(4, "上周"), + THIS_MONTH(5, "本月"), + LAST_MONTH(6, "上月"), + THIS_QUARTER(7, "本季度"), + LAST_QUARTER(8, "上季度"), + THIS_YEAR(9, "本年"), + LAST_YEAR(10, "去年"), + CUSTOMER(11, "自定义"), + ; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(DateIntervalEnum::getType).toArray(); + + /** + * 类型 + */ + private final Integer type; + + /** + * 名称 + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } + +} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java index 4a0976bb1..d49f6068c 100644 --- a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java +++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java @@ -103,4 +103,7 @@ public interface ErrorCodeConstants { ErrorCode FOLLOW_UP_RECORD_NOT_EXISTS = new ErrorCode(1_020_013_000, "跟进记录不存在"); ErrorCode FOLLOW_UP_RECORD_DELETE_DENIED = new ErrorCode(1_020_013_001, "删除跟进记录失败,原因:没有权限"); + // ========== 数据统计 1_020_014_000 ========== + ErrorCode STATISTICS_CUSTOMER_TIMES_NOT_SET = new ErrorCode(1_020_014_000, "自定义时间间隔,必须输入时间区间"); + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http index 0c422f986..9d96e159a 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http @@ -1,40 +1,40 @@ # == 1. 客户总量分析 == ### 1.1 客户总量分析(按日) -GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-date?deptId=100×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59 +GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-date?deptId=100&intervalType=11×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} ### 1.2 客户总量分析(按月) -GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-date?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 +GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-date?deptId=100&intervalType=11×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} ### 1.3 客户总量统计(按用户) -GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-user?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 +GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-user?deptId=100&intervalType=11×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} # == 2. 客户跟进次数分析 == ### 2.1 客户跟进次数分析(按日) -GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-date?deptId=100×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59 +GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-date?deptId=100&intervalType=11×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} ### 2.2 客户跟进次数分析(按月) -GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-date?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 +GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-date?deptId=100&intervalType=11×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} ### 2.3 客户总量统计(按用户) -GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-user?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 +GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-user?deptId=100&intervalType=11×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} # == 3. 客户跟进方式分析 == ### 3.1 客户跟进方式分析 -GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-type?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 +GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-type?deptId=100&intervalType=11×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} tenant-id: {{adminTenentId}} @@ -42,7 +42,7 @@ tenant-id: {{adminTenentId}} # == 4. 客户成交周期 == ### 4.1 合同摘要信息(客户转化率页面) -GET {{baseUrl}}/crm/statistics-customer/get-contract-summary?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 +GET {{baseUrl}}/crm/statistics-customer/get-contract-summary?deptId=100&intervalType=11×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} tenant-id: {{adminTenentId}} @@ -50,19 +50,19 @@ tenant-id: {{adminTenentId}} # == 5. 客户成交周期 == ### 5.1 客户成交周期(按日) -GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-date?deptId=100×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59 +GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-date?deptId=100&intervalType=11×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} tenant-id: {{adminTenentId}} ### 5.2 客户成交周期(按月) -GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-date?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 +GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-date?deptId=100&intervalType=11×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} tenant-id: {{adminTenentId}} ### 5.3 获取客户成交周期(按用户) -GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-user?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 +GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-user?deptId=100&intervalType=11×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} tenant-id: {{adminTenentId}} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java index c5d5acbc3..38f15cf03 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java @@ -1,8 +1,8 @@ package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; -import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; @@ -28,31 +28,28 @@ public class CrmStatisticsCustomerReqVO { /** * userIds 目前不用前端传递,目前是方便后端通过 deptId 读取编号后,设置回来 - *

* 后续,可能会支持选择部分用户进行查询 */ @Schema(description = "负责人用户 id 集合", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2") private List userIds; - @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "时间间隔类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @InEnum(value = DateIntervalEnum.class, message = "时间间隔类型,必须是 {value}") + private Integer intervalType; + + /** + * 前端如果选择自定义时间, 那么前端传递起始-终止时间, 如果选择其他时间间隔类型, 则由后台计算起始-终止时间 + * 并作为参数传递给Mapper + */ + @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED) @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @NotEmpty(message = "时间范围不能为空") private LocalDateTime[] times; - // TODO @dhb52:这个时间间隔,建议前端传递;例如说:字段叫 interval,枚举有天、周、月、季度、年。因为一般分析类的系统,都是交给用户选择筛选时间间隔,而我们这里是默认根据日期选项,默认对应的 interval 而已 - // 然后实现上,可以在 common 包的 enums 加个 DateIntervalEnum,里面一个是 interval 字段,枚举过去,然后有个 pattern 字段,用于格式化时间格式; - // 这样的话,可以通过 interval 获取到 pattern,然后前端就可以根据 pattern 格式化时间,计算还是交给数据库 /** * group by DATE_FORMAT(field, #{dateFormat}) + * 非前端传递, 由Service计算后传递给Mapper的参数 */ @Schema(description = "Group By 日期格式", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "%Y%m") private String sqlDateFormat; - // TODO @dhb52:这个字段,目前是不是没啥用呀? - /** - * 数据类型 {@link CrmBizTypeEnum} - */ - @Schema(description = "数据类型", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2") - private Integer bizType; - } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java index 19bd76fc3..7c8ed7e93 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java @@ -13,33 +13,32 @@ import java.util.List; @Mapper public interface CrmStatisticsCustomerMapper { - // TODO @dhb52:拼写,GroupBy。一般 idea 如果出现绿色的警告,可能是单词拼写错误,建议是要修改的哈; - List selectCustomerCreateCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); // 已经 review + List selectCustomerCreateCountGroupByDate(CrmStatisticsCustomerReqVO reqVO); // 已经 review - List selectCustomerDealCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); // 已经 review + List selectCustomerDealCountGroupByDate(CrmStatisticsCustomerReqVO reqVO); // 已经 review - List selectCustomerCreateCountGroupbyUser(CrmStatisticsCustomerReqVO reqVO); // 已经 review + List selectCustomerCreateCountGroupByUser(CrmStatisticsCustomerReqVO reqVO); // 已经 review - List selectCustomerDealCountGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); // 已经 review + List selectCustomerDealCountGroupByUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); // 已经 review - List selectContractPriceGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); // 已经 review + List selectContractPriceGroupByUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); // 已经 review - List selectReceivablePriceGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); // 已经 review + List selectReceivablePriceGroupByUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); // 已经 review - List selectFollowupRecordCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); + List selectFollowupRecordCountGroupByDate(CrmStatisticsCustomerReqVO reqVO); - List selectFollowupCustomerCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); + List selectFollowupCustomerCountGroupByDate(CrmStatisticsCustomerReqVO reqVO); - List selectFollowupRecordCountGroupbyUser(CrmStatisticsCustomerReqVO reqVO); + List selectFollowupRecordCountGroupByUser(CrmStatisticsCustomerReqVO reqVO); - List selectFollowupCustomerCountGroupbyUser(CrmStatisticsCustomerReqVO reqVO); + List selectFollowupCustomerCountGroupByUser(CrmStatisticsCustomerReqVO reqVO); List selectContractSummary(CrmStatisticsCustomerReqVO reqVO); - List selectFollowupRecordCountGroupbyType(CrmStatisticsCustomerReqVO reqVO); + List selectFollowupRecordCountGroupByType(CrmStatisticsCustomerReqVO reqVO); - List selectCustomerDealCycleGroupbyDate(CrmStatisticsCustomerReqVO reqVO); + List selectCustomerDealCycleGroupByDate(CrmStatisticsCustomerReqVO reqVO); - List selectCustomerDealCycleGroupbyUser(CrmStatisticsCustomerReqVO reqVO); + List selectCustomerDealCycleGroupByUser(CrmStatisticsCustomerReqVO reqVO); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java index c9654a68e..f951e25f5 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java @@ -1,13 +1,15 @@ package cn.iocoder.yudao.module.crm.service.statistics; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper; -import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; import cn.iocoder.yudao.module.system.api.dict.DictDataApi; @@ -20,14 +22,15 @@ import org.springframework.validation.annotation.Validated; import java.math.BigDecimal; import java.time.LocalDateTime; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Stream; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.STATISTICS_CUSTOMER_TIMES_NOT_SET; /** * CRM 客户分析 Service 实现类 @@ -58,114 +61,107 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe @Override public List getCustomerSummaryByDate(CrmStatisticsCustomerReqVO reqVO) { // 1. 获得用户编号数组 - final List userIds = getUserIds(reqVO); + List userIds = getUserIds(reqVO); if (CollUtil.isEmpty(userIds)) { return Collections.emptyList(); } reqVO.setUserIds(userIds); // 2. 获取分项统计数据 - // TODO @dhb52:如果是 list 变量,要么 List 要么 s 后缀 - reqVO.setSqlDateFormat(getSqlDateFormat(reqVO.getTimes()[0], reqVO.getTimes()[1])); - final List customerCreateCount = customerMapper.selectCustomerCreateCountGroupbyDate(reqVO); - final List customerDealCount = customerMapper.selectCustomerDealCountGroupbyDate(reqVO); + initParams(reqVO); + List customerCreateCountVoList = customerMapper.selectCustomerCreateCountGroupByDate(reqVO); + List customerDealCountVoList = customerMapper.selectCustomerDealCountGroupByDate(reqVO); - // 3. 获取时间序列 - // TODO @dhb52:3 和 4 其实做的是一类事情,所以可以考虑 3.1 获取时间序列、3.2 合并统计数据 这样注释;然后中间就不空行了;就是说,一般空行的目的,是让逻辑分片,看着整体性更好,但是不能让逻辑感觉碎碎的; - final List times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]); - - // 4. 合并统计数据 - // TODO @dhb52:这个是不是要 add 到 respVoList 里?或者还可以 convertList(times, time -> new CrmStatisticsCustomerDealCycleByDateRespVO()...) - List respVoList = new ArrayList<>(times.size()); - final Map customerCreateCountMap = convertMap(customerCreateCount, + // 3. 合并数据 + List times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]); + Map customerCreateCountMap = convertMap(customerCreateCountVoList, CrmStatisticsCustomerSummaryByDateRespVO::getTime, CrmStatisticsCustomerSummaryByDateRespVO::getCustomerCreateCount); - final Map customerDealCountMap = convertMap(customerDealCount, + Map customerDealCountMap = convertMap(customerDealCountVoList, CrmStatisticsCustomerSummaryByDateRespVO::getTime, CrmStatisticsCustomerSummaryByDateRespVO::getCustomerDealCount); - times.forEach(time -> respVoList.add( - new CrmStatisticsCustomerSummaryByDateRespVO().setTime(time) + List respVoList = convertList(times, + time -> new CrmStatisticsCustomerSummaryByDateRespVO() + .setTime(time) .setCustomerCreateCount(customerCreateCountMap.getOrDefault(time, 0)) - .setCustomerDealCount(customerDealCountMap.getOrDefault(time, 0)) - )); + .setCustomerDealCount(customerDealCountMap.getOrDefault(time, 0))); + return respVoList; } @Override public List getCustomerSummaryByUser(CrmStatisticsCustomerReqVO reqVO) { // 1. 获得用户编号数组 - final List userIds = getUserIds(reqVO); + List userIds = getUserIds(reqVO); if (CollUtil.isEmpty(userIds)) { return Collections.emptyList(); } reqVO.setUserIds(userIds); // 2. 获取分项统计数据 - final List customerCreateCount = customerMapper.selectCustomerCreateCountGroupbyUser(reqVO); - final List customerDealCount = customerMapper.selectCustomerDealCountGroupbyUser(reqVO); - final List contractPrice = customerMapper.selectContractPriceGroupbyUser(reqVO); - final List receivablePrice = customerMapper.selectReceivablePriceGroupbyUser(reqVO); + initParams(reqVO); + List customerCreateCount = customerMapper.selectCustomerCreateCountGroupByUser(reqVO); + List customerDealCount = customerMapper.selectCustomerDealCountGroupByUser(reqVO); + List contractPrice = customerMapper.selectContractPriceGroupByUser(reqVO); + List receivablePrice = customerMapper.selectReceivablePriceGroupByUser(reqVO); // 3. 合并统计数据 - final Map customerCreateCountMap = convertMap(customerCreateCount, + Map customerCreateCountMap = convertMap(customerCreateCount, CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, CrmStatisticsCustomerSummaryByUserRespVO::getCustomerCreateCount); - final Map customerDealCountMap = convertMap(customerDealCount, + Map customerDealCountMap = convertMap(customerDealCount, CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount); - final Map contractPriceMap = convertMap(contractPrice, + Map contractPriceMap = convertMap(contractPrice, CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, CrmStatisticsCustomerSummaryByUserRespVO::getContractPrice); - final Map receivablePriceMap = convertMap(receivablePrice, + Map receivablePriceMap = convertMap(receivablePrice, CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, CrmStatisticsCustomerSummaryByUserRespVO::getReceivablePrice); - List respVoList = new ArrayList<>(userIds.size()); - userIds.forEach(userId -> { - final CrmStatisticsCustomerSummaryByUserRespVO vo = new CrmStatisticsCustomerSummaryByUserRespVO(); + List respVoList = convertList(userIds, userId -> { + CrmStatisticsCustomerSummaryByUserRespVO vo = new CrmStatisticsCustomerSummaryByUserRespVO(); + // ownerUserId 为基类属性 vo.setOwnerUserId(userId); vo.setCustomerCreateCount(customerCreateCountMap.getOrDefault(userId, 0)) .setCustomerDealCount(customerDealCountMap.getOrDefault(userId, 0)) .setContractPrice(contractPriceMap.getOrDefault(userId, BigDecimal.ZERO)) .setReceivablePrice(receivablePriceMap.getOrDefault(userId, BigDecimal.ZERO)); - respVoList.add(vo); + return vo; }); // 4. 拼接用户信息 appendUserInfo(respVoList); + return respVoList; } @Override public List getFollowupSummaryByDate(CrmStatisticsCustomerReqVO reqVO) { // 1. 获得用户编号数组 - final List userIds = getUserIds(reqVO); + List userIds = getUserIds(reqVO); if (CollUtil.isEmpty(userIds)) { return Collections.emptyList(); } reqVO.setUserIds(userIds); // 2. 获取分项统计数据 - reqVO.setSqlDateFormat(getSqlDateFormat(reqVO.getTimes()[0], reqVO.getTimes()[1])); - reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); - final List followupRecordCount = customerMapper.selectFollowupRecordCountGroupbyDate(reqVO); - final List followupCustomerCount = customerMapper.selectFollowupCustomerCountGroupbyDate(reqVO); + initParams(reqVO); + List followupRecordCount = customerMapper.selectFollowupRecordCountGroupByDate(reqVO); + List followupCustomerCount = customerMapper.selectFollowupCustomerCountGroupByDate(reqVO); - // 3. 获取时间序列 - final List times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]); - - // 4. 合并统计数据 - List respVoList = new ArrayList<>(times.size()); - final Map followupRecordCountMap = convertMap(followupRecordCount, + // 3. 合并统计数据 + List times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]); + Map followupRecordCountMap = convertMap(followupRecordCount, CrmStatisticsFollowupSummaryByDateRespVO::getTime, CrmStatisticsFollowupSummaryByDateRespVO::getFollowupRecordCount); - final Map followupCustomerCountMap = convertMap(followupCustomerCount, + Map followupCustomerCountMap = convertMap(followupCustomerCount, CrmStatisticsFollowupSummaryByDateRespVO::getTime, CrmStatisticsFollowupSummaryByDateRespVO::getFollowupCustomerCount); - times.forEach(time -> respVoList.add( + List respVoList = convertList(times, time -> new CrmStatisticsFollowupSummaryByDateRespVO().setTime(time) .setFollowupRecordCount(followupRecordCountMap.getOrDefault(time, 0)) .setFollowupCustomerCount(followupCustomerCountMap.getOrDefault(time, 0)) - )); + ); return respVoList; } @@ -173,31 +169,31 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe @Override public List getFollowupSummaryByUser(CrmStatisticsCustomerReqVO reqVO) { // 1. 获得用户编号数组 - final List userIds = getUserIds(reqVO); + List userIds = getUserIds(reqVO); if (CollUtil.isEmpty(userIds)) { return Collections.emptyList(); } reqVO.setUserIds(userIds); // 2. 获取分项统计数据 - reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); - final List followupRecordCount = customerMapper.selectFollowupRecordCountGroupbyUser(reqVO); - final List followupCustomerCount = customerMapper.selectFollowupCustomerCountGroupbyUser(reqVO); + initParams(reqVO); + List followupRecordCount = customerMapper.selectFollowupRecordCountGroupByUser(reqVO); + List followupCustomerCount = customerMapper.selectFollowupCustomerCountGroupByUser(reqVO); // 3. 合并统计数据 - final Map followupRecordCountMap = convertMap(followupRecordCount, + Map followupRecordCountMap = convertMap(followupRecordCount, CrmStatisticsFollowupSummaryByUserRespVO::getOwnerUserId, CrmStatisticsFollowupSummaryByUserRespVO::getFollowupRecordCount); - final Map followupCustomerCountMap = convertMap(followupCustomerCount, + Map followupCustomerCountMap = convertMap(followupCustomerCount, CrmStatisticsFollowupSummaryByUserRespVO::getOwnerUserId, CrmStatisticsFollowupSummaryByUserRespVO::getFollowupCustomerCount); - List respVoList = new ArrayList<>(userIds.size()); - userIds.forEach(userId -> { - final CrmStatisticsFollowupSummaryByUserRespVO vo = new CrmStatisticsFollowupSummaryByUserRespVO() + List respVoList = convertList(userIds, userId -> { + CrmStatisticsFollowupSummaryByUserRespVO vo = new CrmStatisticsFollowupSummaryByUserRespVO() .setFollowupRecordCount(followupRecordCountMap.getOrDefault(userId, 0)) .setFollowupCustomerCount(followupCustomerCountMap.getOrDefault(userId, 0)); + // ownerUserId 为基类属性 vo.setOwnerUserId(userId); - respVoList.add(vo); + return vo; }); // 4. 拼接用户信息 @@ -208,19 +204,19 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe @Override public List getFollowupSummaryByType(CrmStatisticsCustomerReqVO reqVO) { // 1. 获得用户编号数组 - final List userIds = getUserIds(reqVO); + List userIds = getUserIds(reqVO); if (CollUtil.isEmpty(userIds)) { return Collections.emptyList(); } reqVO.setUserIds(userIds); // 2. 获得排行数据 - reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); - List respVoList = customerMapper.selectFollowupRecordCountGroupbyType(reqVO); + initParams(reqVO); + List respVoList = customerMapper.selectFollowupRecordCountGroupByType(reqVO); // 3. 获取字典数据 List followUpTypes = dictDataApi.getDictDataList(CRM_FOLLOW_UP_TYPE); - final Map followUpTypeMap = convertMap(followUpTypes, + Map followUpTypeMap = convertMap(followUpTypes, DictDataRespDTO::getValue, DictDataRespDTO::getLabel); respVoList.forEach(vo -> { vo.setFollowupType(followUpTypeMap.get(vo.getFollowupType())); @@ -232,26 +228,27 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe @Override public List getContractSummary(CrmStatisticsCustomerReqVO reqVO) { // 1. 获得用户编号数组 - final List userIds = getUserIds(reqVO); + List userIds = getUserIds(reqVO); if (CollUtil.isEmpty(userIds)) { return Collections.emptyList(); } reqVO.setUserIds(userIds); // 2. 获取统计数据 + initParams(reqVO); List respVoList = customerMapper.selectContractSummary(reqVO); // 3. 设置 创建人、负责人、行业、来源 - // 获取客户所属行业 + // 3.1 获取客户所属行业 Map industryMap = convertMap(dictDataApi.getDictDataList(CRM_CUSTOMER_INDUSTRY), DictDataRespDTO::getValue, DictDataRespDTO::getLabel); - // 获取客户来源 + // 3.2 获取客户来源 Map sourceMap = convertMap(dictDataApi.getDictDataList(CRM_CUSTOMER_SOURCE), DictDataRespDTO::getValue, DictDataRespDTO::getLabel); - // 获取创建人、负责人列表 + // 3.3 获取创建人、负责人列表 Map userMap = adminUserApi.getUserMap(convertSetByFlatMap(respVoList, vo -> Stream.of(NumberUtils.parseLong(vo.getCreatorUserId()), vo.getOwnerUserId()))); - + // 3.4 设置 创建人、负责人、行业、来源 respVoList.forEach(vo -> { MapUtils.findAndThen(industryMap, vo.getIndustryId(), vo::setIndustryName); MapUtils.findAndThen(sourceMap, vo.getSource(), vo::setSourceName); @@ -266,60 +263,57 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe @Override public List getCustomerDealCycleByDate(CrmStatisticsCustomerReqVO reqVO) { // 1. 获得用户编号数组 - final List userIds = getUserIds(reqVO); + List userIds = getUserIds(reqVO); if (CollUtil.isEmpty(userIds)) { return Collections.emptyList(); } reqVO.setUserIds(userIds); // 2. 获取分项统计数据 - reqVO.setSqlDateFormat(getSqlDateFormat(reqVO.getTimes()[0], reqVO.getTimes()[1])); - reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); - final List customerDealCycle = customerMapper.selectCustomerDealCycleGroupbyDate(reqVO); + initParams(reqVO); + List customerDealCycle = customerMapper.selectCustomerDealCycleGroupByDate(reqVO); - // 3. 获取时间序列 - final List times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]); - - // 4. 合并统计数据 - List respVoList = new ArrayList<>(times.size()); - final Map customerDealCycleMap = convertMap(customerDealCycle, + // 3. 合并统计数据 + List times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]); + Map customerDealCycleMap = convertMap(customerDealCycle, CrmStatisticsCustomerDealCycleByDateRespVO::getTime, CrmStatisticsCustomerDealCycleByDateRespVO::getCustomerDealCycle); - times.forEach(time -> respVoList.add( + List respVoList = convertList(times, time -> new CrmStatisticsCustomerDealCycleByDateRespVO().setTime(time) .setCustomerDealCycle(customerDealCycleMap.getOrDefault(time, 0D)) - )); + ); + return respVoList; } @Override public List getCustomerDealCycleByUser(CrmStatisticsCustomerReqVO reqVO) { // 1. 获得用户编号数组 - final List userIds = getUserIds(reqVO); + List userIds = getUserIds(reqVO); if (CollUtil.isEmpty(userIds)) { return Collections.emptyList(); } reqVO.setUserIds(userIds); // 2. 获取分项统计数据 - reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); - final List customerDealCycle = customerMapper.selectCustomerDealCycleGroupbyUser(reqVO); - final List customerDealCount = customerMapper.selectCustomerDealCountGroupbyUser(reqVO); + initParams(reqVO); + List customerDealCycle = customerMapper.selectCustomerDealCycleGroupByUser(reqVO); + List customerDealCount = customerMapper.selectCustomerDealCountGroupByUser(reqVO); // 3. 合并统计数据 - final Map customerDealCycleMap = convertMap(customerDealCycle, + Map customerDealCycleMap = convertMap(customerDealCycle, CrmStatisticsCustomerDealCycleByUserRespVO::getOwnerUserId, CrmStatisticsCustomerDealCycleByUserRespVO::getCustomerDealCycle); - final Map customerDealCountMap = convertMap(customerDealCount, + Map customerDealCountMap = convertMap(customerDealCount, CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount); - List respVoList = new ArrayList<>(userIds.size()); - userIds.forEach(userId -> { - final CrmStatisticsCustomerDealCycleByUserRespVO vo = new CrmStatisticsCustomerDealCycleByUserRespVO() + List respVoList = convertList(userIds, userId -> { + CrmStatisticsCustomerDealCycleByUserRespVO vo = new CrmStatisticsCustomerDealCycleByUserRespVO() .setCustomerDealCycle(customerDealCycleMap.getOrDefault(userId, 0.0)) .setCustomerDealCount(customerDealCountMap.getOrDefault(userId, 0)); + // ownerUserId 为基类属性 vo.setOwnerUserId(userId); - respVoList.add(vo); + return vo; }); // 4. 拼接用户信息 @@ -335,8 +329,9 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe */ private void appendUserInfo(List respVoList) { Map userMap = adminUserApi.getUserMap(convertSet(respVoList, - CrmStatisticsCustomerByUserBaseRespVO::getOwnerUserId)); - respVoList.forEach(vo -> MapUtils.findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname()))); + CrmStatisticsCustomerByUserBaseRespVO::getOwnerUserId)); + respVoList.forEach(vo -> MapUtils.findAndThen(userMap, + vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname()))); } /** @@ -352,7 +347,7 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe } // 情况二:选中某个部门 // 2.1 获得部门列表 - final Long deptId = reqVO.getDeptId(); + Long deptId = reqVO.getDeptId(); List deptIds = convertList(deptApi.getChildDeptList(deptId), DeptRespDTO::getId); deptIds.add(deptId); // 2.2 获得用户编号 @@ -378,7 +373,6 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe * @param endTime 结束时间 * @return 时间序列 */ - // TODO @dhb52:可以抽象到 DateUtils 里,开始时间、结束时间,事件间隔,然后返回这个哈; private List generateTimeSeries(LocalDateTime startTime, LocalDateTime endTime) { boolean byMonth = queryByMonth(startTime, endTime); List times = CollUtil.newArrayList(); @@ -404,4 +398,62 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe return queryByMonth(startTime, endTime) ? SQL_DATE_FORMAT_BY_MONTH : SQL_DATE_FORMAT_BY_DAY; } + private void initParams(CrmStatisticsCustomerReqVO reqVO) { + final Integer intervalType = reqVO.getIntervalType(); + + // 1. 自定义时间间隔,必须输入起始日期-结束日期 + if (DateIntervalEnum.CUSTOMER.getType().equals(intervalType)) { + if (ObjUtil.isEmpty(reqVO.getTimes()) || reqVO.getTimes().length != 2) { + throw exception(STATISTICS_CUSTOMER_TIMES_NOT_SET); + } + // 设置 mapper sqlDateFormat 参数 + reqVO.setSqlDateFormat(getSqlDateFormat(reqVO.getTimes()[0], reqVO.getTimes()[1])); + // 自定义日期无需计算日期参数 + return; + } + + // 2. 根据时间区间类型计算时间段区间日期 + DateTime beginDate = null; + DateTime endDate = null; + if (DateIntervalEnum.TODAY.getType().equals(intervalType)) { + beginDate = DateUtil.beginOfDay(DateUtil.date()); + endDate = DateUtil.endOfDay(DateUtil.date()); + } else if (DateIntervalEnum.YESTERDAY.getType().equals(intervalType)) { + beginDate = DateUtil.offsetDay(DateUtil.date(), -1); + endDate = DateUtil.offsetDay(DateUtil.date(), -1); + } else if (DateIntervalEnum.THIS_WEEK.getType().equals(intervalType)) { + beginDate = DateUtil.beginOfWeek(DateUtil.date()); + endDate = DateUtil.endOfWeek(DateUtil.date()); + } else if (DateIntervalEnum.LAST_WEEK.getType().equals(intervalType)) { + beginDate = DateUtil.beginOfWeek(DateUtil.offsetWeek(DateUtil.date(), -1)); + endDate = DateUtil.endOfWeek(DateUtil.offsetWeek(DateUtil.date(), -1)); + } else if (DateIntervalEnum.THIS_MONTH.getType().equals(intervalType)) { + beginDate = DateUtil.beginOfMonth(DateUtil.date()); + endDate = DateUtil.endOfMonth(DateUtil.date()); + } else if (DateIntervalEnum.LAST_MONTH.getType().equals(intervalType)) { + beginDate = DateUtil.beginOfMonth(DateUtil.offsetMonth(DateUtil.date(), -1)); + endDate = DateUtil.endOfMonth(DateUtil.offsetMonth(DateUtil.date(), -1)); + } else if (DateIntervalEnum.THIS_QUARTER.getType().equals(intervalType)) { + beginDate = DateUtil.beginOfQuarter(DateUtil.date()); + endDate = DateUtil.endOfQuarter(DateUtil.date()); + } else if (DateIntervalEnum.LAST_QUARTER.getType().equals(intervalType)) { + beginDate = DateUtil.beginOfQuarter(DateUtil.offsetMonth(DateUtil.date(), -3)); + endDate = DateUtil.endOfQuarter(DateUtil.offsetMonth(DateUtil.date(), -3)); + } else if (DateIntervalEnum.THIS_YEAR.getType().equals(intervalType)) { + beginDate = DateUtil.beginOfYear(DateUtil.date()); + endDate = DateUtil.endOfYear(DateUtil.date()); + } else if (DateIntervalEnum.LAST_YEAR.getType().equals(intervalType)) { + beginDate = DateUtil.beginOfYear(DateUtil.offsetMonth(DateUtil.date(), -12)); + endDate = DateUtil.endOfYear(DateUtil.offsetMonth(DateUtil.date(), -12)); + } + + // 3. 计算开始、结束日期时间,并设置reqVo + LocalDateTime[] times = new LocalDateTime[2]; + times[0] = LocalDateTimeUtil.beginOfDay(LocalDateTimeUtil.of(beginDate)); + times[1] = LocalDateTimeUtil.endOfDay(LocalDateTimeUtil.of(endDate)); + // 3.1 设置 mapper 时间区间 参数 + reqVO.setTimes(times); + // 3.2 设置 mapper sqlDateFormat 参数 + reqVO.setSqlDateFormat(getSqlDateFormat(times[0], times[1])); + } } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml index fce255651..32a1d425e 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml @@ -2,251 +2,233 @@ - - - - - - - - - - - - - - - - - + + - - - -