!909 feat: [CRM-客户分析]增加[时间间隔]选项

Merge pull request !909 from dhb52/develop
This commit is contained in:
芋道源码 2024-03-28 11:13:18 +00:00 committed by Gitee
commit eab9627253
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
7 changed files with 411 additions and 330 deletions

View File

@ -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;
}
}

View File

@ -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, "自定义时间间隔,必须输入时间区间");
}

View File

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

View File

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

View File

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

View File

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

View File

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