Commit 34f68ce4c63876b02484ba30aab5b37364663232

Authored by dhb52
1 parent fbb624d8

feat: CRM/数据统计/员工客户分析 初稿

yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java
... ... @@ -55,4 +55,18 @@ public class CrmStatisticsCustomerController {
55 55 return success(customerService.getDistinctRecordCount(reqVO));
56 56 }
57 57  
  58 + @GetMapping("/get-record-type-count")
  59 + @Operation(summary = "获取客户跟进方式统计数")
  60 + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")
  61 + public CommonResult<List<CrmStatisticsCustomerCountVO>> getRecordTypeCount(@Valid CrmStatisticsCustomerReqVO reqVO) {
  62 + return success(customerService.getRecordTypeCount(reqVO));
  63 + }
  64 +
  65 + @GetMapping("/get-customer-cycle")
  66 + @Operation(summary = "获取客户成交周期")
  67 + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")
  68 + public CommonResult<List<CrmStatisticsCustomerCountVO>> getCustomerCycle(@Valid CrmStatisticsCustomerReqVO reqVO) {
  69 + return success(customerService.getCustomerCycle(reqVO));
  70 + }
  71 +
58 72 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerCountVO.java
... ... @@ -23,6 +23,12 @@ public class CrmStatisticsCustomerCountVO {
23 23 * 2. 个数:签约合同排行、产品销量排行、产品销量排行、新增客户数排行、新增联系人排行、跟进次数排行、跟进客户数排行
24 24 */
25 25 @Schema(description = "数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
26   - private Integer count;
  26 + private Integer count = 0;
  27 +
  28 + /**
  29 + * 成交周期(天)
  30 + */
  31 + @Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0")
  32 + private Double cycle = 0.0;
27 33  
28 34 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java
... ... @@ -22,4 +22,8 @@ public interface CrmStatisticsCustomerMapper {
22 22  
23 23 List<CrmStatisticsCustomerCountVO> selectDistinctRecordCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO);
24 24  
  25 + List<CrmStatisticsCustomerCountVO> selectRecordCountGroupbyType(CrmStatisticsCustomerReqVO reqVO);
  26 +
  27 + List<CrmStatisticsCustomerCountVO> selectCustomerCycleGroupbyDate(CrmStatisticsCustomerReqVO reqVO);
  28 +
25 29 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java
... ... @@ -45,4 +45,20 @@ public interface CrmStatisticsCustomerService {
45 45 */
46 46 List<CrmStatisticsCustomerCountVO> getDistinctRecordCount(CrmStatisticsCustomerReqVO reqVO);
47 47  
  48 + /**
  49 + * 获取客户跟进方式统计数
  50 + *
  51 + * @param reqVO 请求参数
  52 + * @return 客户跟进方式统计数
  53 + */
  54 + List<CrmStatisticsCustomerCountVO> getRecordTypeCount(CrmStatisticsCustomerReqVO reqVO);
  55 +
  56 + /**
  57 + * 获取客户成交周期
  58 + *
  59 + * @param reqVO 请求参数
  60 + * @return 客户成交周期
  61 + */
  62 + List<CrmStatisticsCustomerCountVO> getCustomerCycle(CrmStatisticsCustomerReqVO reqVO);
  63 +
48 64 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java
... ... @@ -3,12 +3,14 @@ package cn.iocoder.yudao.module.crm.service.statistics;
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.date.LocalDateTimeUtil;
5 5 import cn.hutool.core.util.ObjUtil;
6   -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO;
7 6 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO;
  7 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO;
8 8 import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper;
9 9 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
10 10 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
11 11 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
  12 +import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
  13 +import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO;
12 14 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
13 15 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
14 16 import jakarta.annotation.Resource;
... ... @@ -21,7 +23,8 @@ import java.util.List;
21 23 import java.util.Map;
22 24 import java.util.function.Function;
23 25  
24   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
  26 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  27 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
25 28  
26 29 /**
27 30 * CRM 数据统计 员工客户分析 Service 实现类
... ... @@ -39,6 +42,8 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
39 42 private AdminUserApi adminUserApi;
40 43 @Resource
41 44 private DeptApi deptApi;
  45 + @Resource
  46 + private DictDataApi dictDataApi;
42 47  
43 48 @Override
44 49 public List<CrmStatisticsCustomerCountVO> getTotalCustomerCount(CrmStatisticsCustomerReqVO reqVO) {
... ... @@ -62,6 +67,37 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
62 67 return getStat(reqVO, customerMapper::selectDistinctRecordCountGroupbyDate);
63 68 }
64 69  
  70 + @Override
  71 + public List<CrmStatisticsCustomerCountVO> getRecordTypeCount(CrmStatisticsCustomerReqVO reqVO) {
  72 + // 1. 获得用户编号数组: 如果用户编号为空, 则获得部门下的用户编号数组
  73 + if (ObjUtil.isNotNull(reqVO.getUserId())) {
  74 + reqVO.setUserIds(List.of(reqVO.getUserId()));
  75 + } else {
  76 + reqVO.setUserIds(getUserIds(reqVO.getDeptId()));
  77 + }
  78 + if (CollUtil.isEmpty(reqVO.getUserIds())) {
  79 + return Collections.emptyList();
  80 + }
  81 +
  82 + // 2. 获得排行数据
  83 + reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType());
  84 + List<CrmStatisticsCustomerCountVO> stats = customerMapper.selectRecordCountGroupbyType(reqVO);
  85 +
  86 + // 3. 获取字典数据
  87 + List<DictDataRespDTO> followUpTypes = dictDataApi.getDictDataList("crm_follow_up_type");
  88 + final Map<String, String> followUpTypeMap = convertMap(followUpTypes, DictDataRespDTO::getValue, DictDataRespDTO::getLabel);
  89 + stats.forEach(stat -> {
  90 + stat.setCategory(followUpTypeMap.get(stat.getCategory()));
  91 + });
  92 +
  93 + return stats;
  94 + }
  95 +
  96 + @Override
  97 + public List<CrmStatisticsCustomerCountVO> getCustomerCycle(CrmStatisticsCustomerReqVO reqVO) {
  98 + return getStat(reqVO, customerMapper::selectCustomerCycleGroupbyDate);
  99 + }
  100 +
65 101 /**
66 102 * 获得统计数据
67 103 *
... ... @@ -98,9 +134,9 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
98 134  
99 135 // 4. 生成时间序列
100 136 List<CrmStatisticsCustomerCountVO> result = CollUtil.newArrayList();
101   - while (startTime.compareTo(endTime) <= 0) {
  137 + while (!startTime.isAfter(endTime)) {
102 138 final String category = LocalDateTimeUtil.format(startTime, byMonth ? "yyyyMM" : "yyyyMMdd");
103   - result.add(new CrmStatisticsCustomerCountVO().setCategory(category).setCount(0));
  139 + result.add(new CrmStatisticsCustomerCountVO().setCategory(category));
104 140 if (byMonth)
105 141 startTime = startTime.plusMonths(1);
106 142 else
... ... @@ -108,12 +144,13 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
108 144 }
109 145  
110 146 // 5. 使用时间序列填充结果
111   - final Map<String, Integer> statMap = convertMap(stats,
112   - CrmStatisticsCustomerCountVO::getCategory,
113   - CrmStatisticsCustomerCountVO::getCount);
  147 + final Map<String, CrmStatisticsCustomerCountVO> statMap = convertMap(stats,
  148 + CrmStatisticsCustomerCountVO::getCategory,
  149 + Function.identity());
114 150 result.forEach(r -> {
115 151 if (statMap.containsKey(r.getCategory())) {
116   - r.setCount(statMap.get(r.getCategory()));
  152 + r.setCount(statMap.get(r.getCategory()).getCount())
  153 + .setCycle(statMap.get(r.getCategory()).getCycle());
117 154 }
118 155 });
119 156  
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml
... ... @@ -6,8 +6,8 @@
6 6 <select id="selectCustomerCountGroupbyDate"
7 7 resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO">
8 8 SELECT
9   - count(*) AS count,
10   - DATE_FORMAT( create_time, #{sqlDateFormat,javaType=java.lang.String} ) AS category
  9 + DATE_FORMAT( create_time, #{sqlDateFormat,javaType=java.lang.String} ) AS category,
  10 + count(*) AS count
11 11 FROM
12 12 crm_customer
13 13 WHERE
... ... @@ -18,15 +18,14 @@
18 18 </foreach>
19 19 AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
20 20 #{times[1],javaType=java.time.LocalDateTime}
21   - GROUP BY
22   - DATE_FORMAT( create_time, #{sqlDateFormat,javaType=java.lang.String} )
  21 + GROUP BY category
23 22 </select>
24 23  
25 24 <select id="selectDealCustomerCountGroupbyDate"
26 25 resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO">
27 26 SELECT
28   - count( DISTINCT a.id ) AS count,
29   - DATE_FORMAT( b.order_date, #{sqlDateFormat,javaType=java.lang.String} ) AS category
  27 + DATE_FORMAT( b.order_date, #{sqlDateFormat,javaType=java.lang.String} ) AS category,
  28 + count( DISTINCT a.id ) AS count
30 29 FROM
31 30 crm_customer AS a
32 31 LEFT JOIN crm_contract AS b ON b.customer_id = a.id
... ... @@ -39,8 +38,7 @@
39 38 </foreach>
40 39 AND b.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
41 40 #{times[1],javaType=java.time.LocalDateTime}
42   - GROUP BY
43   - DATE_FORMAT( b.order_date, #{sqlDateFormat,javaType=java.lang.String} )
  41 + GROUP BY category
44 42 </select>
45 43  
46 44 <select id="selectRecordCountGroupbyDate"
... ... @@ -58,8 +56,7 @@
58 56 AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
59 57 #{times[1],javaType=java.time.LocalDateTime}
60 58 AND biz_type = #{bizType,javaType=java.lang.Integer}
61   - GROUP BY
62   - DATE_FORMAT (create_time, #{sqlDateFormat,javaType=java.lang.String} )
  59 + GROUP BY category
63 60 </select>
64 61  
65 62 <select id="selectDistinctRecordCountGroupbyDate"
... ... @@ -77,7 +74,43 @@
77 74 AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
78 75 #{times[1],javaType=java.time.LocalDateTime}
79 76 AND biz_type = #{bizType,javaType=java.lang.Integer}
80   - GROUP BY DATE_FORMAT (create_time, #{sqlDateFormat,javaType=java.lang.String} )
  77 + GROUP BY category
  78 + </select>
  79 +
  80 + <select id="selectRecordCountGroupbyType"
  81 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO">
  82 + SELECT
  83 + type AS category,
  84 + count(*) AS count
  85 + FROM crm_follow_up_record
  86 + WHERE
  87 + creator IN
  88 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  89 + #{userId}
  90 + </foreach>
  91 + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
  92 + #{times[1],javaType=java.time.LocalDateTime}
  93 + AND biz_type = #{bizType,javaType=java.lang.Integer}
  94 + GROUP BY category
  95 + </select>
  96 +
  97 + <select id="selectCustomerCycleGroupbyDate"
  98 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO">
  99 + SELECT
  100 + DATE_FORMAT( b.order_date, #{sqlDateFormat,javaType=java.lang.String} ) AS category,
  101 + IFNULL( TRUNCATE ( AVG( TIMESTAMPDIFF( DAY, a.create_time, b.order_date )), 1 ), 0 ) AS cycle
  102 + FROM crm_customer AS a
  103 + LEFT JOIN crm_contract AS b ON b.customer_id = a.id
  104 + WHERE
  105 + a.deleted = 0 AND b.deleted = 0
  106 + AND b.audit_status = 20
  107 + AND a.owner_user_id IN
  108 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  109 + #{userId}
  110 + </foreach>
  111 + AND b.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
  112 + #{times[1],javaType=java.time.LocalDateTime}
  113 + GROUP BY category
81 114 </select>
82 115  
83 116 </mapper>
... ...