Commit 8dcc12f21560c6c3e6b617795227d87d4cb3de0c

Authored by YunaiV
1 parent 85817a24

CRM:code review 客户总量统计

... ... @@ -16,12 +16,12 @@
16 16 <module>yudao-module-system</module>
17 17 <module>yudao-module-infra</module>
18 18 <!-- <module>yudao-module-member</module>-->
19   -<!-- <module>yudao-module-bpm</module>-->
  19 + <module>yudao-module-bpm</module>
20 20 <!-- <module>yudao-module-report</module>-->
21 21 <!-- <module>yudao-module-mp</module>-->
22 22 <!-- <module>yudao-module-pay</module>-->
23 23 <!-- <module>yudao-module-mall</module>-->
24   -<!-- <module>yudao-module-crm</module>-->
  24 + <module>yudao-module-crm</module>
25 25 <!-- <module>yudao-module-erp</module>-->
26 26 <!-- 示例项目 -->
27 27 <!-- <module>yudao-example</module>-->
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerByUserBaseRespVO.java
... ... @@ -5,12 +5,14 @@ import io.swagger.v3.oas.annotations.media.Schema;
5 5 import lombok.Data;
6 6  
7 7 /**
8   - * 用户客户统计响应 Base VO
  8 + * 用户客户统计响应 Base Response VO
  9 + *
  10 + * 目的:可以统一拼接子 VO 的 ownerUserId、ownerUserName 属性
9 11 */
10 12 @Data
11 13 public class CrmStatisticsCustomerByUserBaseRespVO {
12 14  
13   - @Schema(description = "负责人ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  15 + @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
14 16 @JsonIgnore
15 17 private Long ownerUserId;
16 18  
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerContractSummaryRespVO.java
... ... @@ -5,13 +5,13 @@ import com.fasterxml.jackson.annotation.JsonFormat;
5 5 import com.fasterxml.jackson.annotation.JsonIgnore;
6 6 import io.swagger.v3.oas.annotations.media.Schema;
7 7 import lombok.Data;
8   -import org.springframework.format.annotation.DateTimeFormat;
9 8  
10 9 import java.math.BigDecimal;
11 10 import java.time.LocalDate;
12 11 import java.time.LocalDateTime;
13 12  
14   -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
  13 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
  14 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;
15 15  
16 16 @Schema(description = "管理后台 - CRM 客户转化率分析 VO")
17 17 @Data
... ... @@ -43,14 +43,14 @@ public class CrmStatisticsCustomerContractSummaryRespVO {
43 43 @Schema(description = "客户来源", requiredMode = Schema.RequiredMode.REQUIRED, example = "外呼")
44 44 private String sourceName;
45 45  
46   - @Schema(description = "负责人ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  46 + @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
47 47 @JsonIgnore
48 48 private Long ownerUserId;
49 49  
50 50 @Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
51 51 private String ownerUserName;
52 52  
53   - @Schema(description = "创建人ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  53 + @Schema(description = "创建人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
54 54 @JsonIgnore
55 55 private String creatorUserId;
56 56  
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java
... ... @@ -12,7 +12,7 @@ import java.util.List;
12 12  
13 13 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
14 14  
15   -@Schema(description = "管理后台 - CRM 数据统计 员工客户分析 Request VO")
  15 +@Schema(description = "管理后台 - CRM 数据统计的员工客户分析 Request VO")
16 16 @Data
17 17 public class CrmStatisticsCustomerReqVO {
18 18  
... ... @@ -39,12 +39,16 @@ public class CrmStatisticsCustomerReqVO {
39 39 @NotEmpty(message = "时间范围不能为空")
40 40 private LocalDateTime[] times;
41 41  
  42 + // TODO @dhb52:这个时间间隔,建议前端传递;例如说:字段叫 interval,枚举有天、周、月、季度、年。因为一般分析类的系统,都是交给用户选择筛选时间间隔,而我们这里是默认根据日期选项,默认对应的 interval 而已
  43 + // 然后实现上,可以在 common 包的 enums 加个 DateIntervalEnum,里面一个是 interval 字段,枚举过去,然后有个 pattern 字段,用于格式化时间格式;
  44 + // 这样的话,可以通过 interval 获取到 pattern,然后前端就可以根据 pattern 格式化时间,计算还是交给数据库
42 45 /**
43 46 * group by DATE_FORMAT(field, #{dateFormat})
44 47 */
45 48 @Schema(description = "Group By 日期格式", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "%Y%m")
46 49 private String sqlDateFormat;
47 50  
  51 + // TODO @dhb52:这个字段,目前是不是没啥用呀?
48 52 /**
49 53 * 数据类型 {@link CrmBizTypeEnum}
50 54 */
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java
... ... @@ -13,17 +13,18 @@ import java.util.List;
13 13 @Mapper
14 14 public interface CrmStatisticsCustomerMapper {
15 15  
16   - List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerCreateCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO);
  16 + // TODO @dhb52:拼写,GroupBy。一般 idea 如果出现绿色的警告,可能是单词拼写错误,建议是要修改的哈;
  17 + List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerCreateCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); // 已经 review
17 18  
18   - List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerDealCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO);
  19 + List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerDealCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); // 已经 review
19 20  
20   - List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerCreateCountGroupbyUser(CrmStatisticsCustomerReqVO reqVO);
  21 + List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerCreateCountGroupbyUser(CrmStatisticsCustomerReqVO reqVO); // 已经 review
21 22  
22   - List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerDealCountGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO);
  23 + List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerDealCountGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); // 已经 review
23 24  
24   - List<CrmStatisticsCustomerSummaryByUserRespVO> selectContractPriceGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO);
  25 + List<CrmStatisticsCustomerSummaryByUserRespVO> selectContractPriceGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); // 已经 review
25 26  
26   - List<CrmStatisticsCustomerSummaryByUserRespVO> selectReceivablePriceGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO);
  27 + List<CrmStatisticsCustomerSummaryByUserRespVO> selectReceivablePriceGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); // 已经 review
27 28  
28 29 List<CrmStatisticsFollowupSummaryByDateRespVO> selectFollowupRecordCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO);
29 30  
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java
... ... @@ -55,7 +55,6 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
55 55 @Resource
56 56 private DictDataApi dictDataApi;
57 57  
58   -
59 58 @Override
60 59 public List<CrmStatisticsCustomerSummaryByDateRespVO> getCustomerSummaryByDate(CrmStatisticsCustomerReqVO reqVO) {
61 60 // 1. 获得用户编号数组
... ... @@ -66,14 +65,17 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
66 65 reqVO.setUserIds(userIds);
67 66  
68 67 // 2. 获取分项统计数据
  68 + // TODO @dhb52:如果是 list 变量,要么 List 要么 s 后缀
69 69 reqVO.setSqlDateFormat(getSqlDateFormat(reqVO.getTimes()[0], reqVO.getTimes()[1]));
70 70 final List<CrmStatisticsCustomerSummaryByDateRespVO> customerCreateCount = customerMapper.selectCustomerCreateCountGroupbyDate(reqVO);
71 71 final List<CrmStatisticsCustomerSummaryByDateRespVO> customerDealCount = customerMapper.selectCustomerDealCountGroupbyDate(reqVO);
72 72  
73 73 // 3. 获取时间序列
  74 + // TODO @dhb52:3 和 4 其实做的是一类事情,所以可以考虑 3.1 获取时间序列、3.2 合并统计数据 这样注释;然后中间就不空行了;就是说,一般空行的目的,是让逻辑分片,看着整体性更好,但是不能让逻辑感觉碎碎的;
74 75 final List<String> times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]);
75 76  
76 77 // 4. 合并统计数据
  78 + // TODO @dhb52:这个是不是要 add 到 respVoList 里?或者还可以 convertList(times, time -> new CrmStatisticsCustomerDealCycleByDateRespVO()...)
77 79 List<CrmStatisticsCustomerSummaryByDateRespVO> respVoList = new ArrayList<>(times.size());
78 80 final Map<String, Integer> customerCreateCountMap = convertMap(customerCreateCount,
79 81 CrmStatisticsCustomerSummaryByDateRespVO::getTime,
... ... @@ -86,7 +88,6 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
86 88 .setCustomerCreateCount(customerCreateCountMap.getOrDefault(time, 0))
87 89 .setCustomerDealCount(customerDealCountMap.getOrDefault(time, 0))
88 90 ));
89   -
90 91 return respVoList;
91 92 }
92 93  
... ... @@ -131,7 +132,6 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
131 132  
132 133 // 4. 拼接用户信息
133 134 appendUserInfo(respVoList);
134   -
135 135 return respVoList;
136 136 }
137 137  
... ... @@ -202,7 +202,6 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
202 202  
203 203 // 4. 拼接用户信息
204 204 appendUserInfo(respVoList);
205   -
206 205 return respVoList;
207 206 }
208 207  
... ... @@ -290,7 +289,6 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
290 289 new CrmStatisticsCustomerDealCycleByDateRespVO().setTime(time)
291 290 .setCustomerDealCycle(customerDealCycleMap.getOrDefault(time, 0D))
292 291 ));
293   -
294 292 return respVoList;
295 293 }
296 294  
... ... @@ -337,9 +335,8 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
337 335 */
338 336 private <T extends CrmStatisticsCustomerByUserBaseRespVO> void appendUserInfo(List<T> respVoList) {
339 337 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(respVoList,
340   - CrmStatisticsCustomerByUserBaseRespVO::getOwnerUserId));
341   - respVoList.forEach(vo -> MapUtils.findAndThen(userMap,
342   - vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname())));
  338 + CrmStatisticsCustomerByUserBaseRespVO::getOwnerUserId));
  339 + respVoList.forEach(vo -> MapUtils.findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname())));
343 340 }
344 341  
345 342 /**
... ... @@ -349,16 +346,17 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
349 346 * @return 用户编号数组
350 347 */
351 348 private List<Long> getUserIds(CrmStatisticsCustomerReqVO reqVO) {
  349 + // 情况一:选中某个用户
352 350 if (ObjUtil.isNotNull(reqVO.getUserId())) {
353 351 return List.of(reqVO.getUserId());
354   - } else {
355   - // 1. 获得部门列表
356   - final Long deptId = reqVO.getDeptId();
357   - List<Long> deptIds = convertList(deptApi.getChildDeptList(deptId), DeptRespDTO::getId);
358   - deptIds.add(deptId);
359   - // 2. 获得用户编号
360   - return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId);
361 352 }
  353 + // 情况二:选中某个部门
  354 + // 2.1 获得部门列表
  355 + final Long deptId = reqVO.getDeptId();
  356 + List<Long> deptIds = convertList(deptApi.getChildDeptList(deptId), DeptRespDTO::getId);
  357 + deptIds.add(deptId);
  358 + // 2.2 获得用户编号
  359 + return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId);
362 360 }
363 361  
364 362  
... ... @@ -380,6 +378,7 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
380 378 * @param endTime 结束时间
381 379 * @return 时间序列
382 380 */
  381 + // TODO @dhb52:可以抽象到 DateUtils 里,开始时间、结束时间,事件间隔,然后返回这个哈;
383 382 private List<String> generateTimeSeries(LocalDateTime startTime, LocalDateTime endTime) {
384 383 boolean byMonth = queryByMonth(startTime, endTime);
385 384 List<String> times = CollUtil.newArrayList();
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml
... ... @@ -2,11 +2,14 @@
2 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3 3 <mapper namespace="cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper">
4 4  
  5 + <!-- TODO @dhb52:数据库的关键字,进行大写。例如说,COUNT -->
  6 +
5 7 <select id="selectCustomerCreateCountGroupbyDate"
6 8 resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByDateRespVO">
7 9 SELECT
  10 + <!-- TODO @dhb52:下面这个,缩进一个 tab;这样可读性更好哈 -->
8 11 DATE_FORMAT( create_time, #{sqlDateFormat} ) AS time,
9   - count(*) AS customerCreateCount
  12 + COUNT(*) AS customerCreateCount
10 13 FROM crm_customer
11 14 WHERE deleted = 0
12 15 AND owner_user_id IN
... ... @@ -14,6 +17,7 @@
14 17 #{userId}
15 18 </foreach>
16 19 AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
  20 + <!-- TODO @dhb52:这可以考虑不换行,直接跟在后面的 AND,更连贯哈; -->
17 21 #{times[1],javaType=java.time.LocalDateTime}
18 22 GROUP BY time
19 23 </select>
... ... @@ -21,6 +25,8 @@
21 25 <select id="selectCustomerDealCountGroupbyDate"
22 26 resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByDateRespVO">
23 27 SELECT
  28 + <!-- TODO @dhb52:下面这个,缩进一个 tab;这样可读性更好哈 -->
  29 + <!-- TODO @dhb52:表变量最好不要用 a、b;可以用 customer 和 constract;虽然长一点,但是一眼看的清楚哈 -->
24 30 DATE_FORMAT( b.order_date, #{sqlDateFormat} ) AS time,
25 31 count( DISTINCT a.id ) AS customerDealCount
26 32 FROM crm_customer AS a
... ... @@ -31,11 +37,13 @@
31 37 <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
32 38 #{userId}
33 39 </foreach>
  40 + <!-- TODO @dhb52:这个应该是 order_date 的范围哈;貌似如果改成这样,不需要查询 customer 表,只要 contract 表就 ok 拉 -->
34 41 AND b.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
35 42 #{times[1],javaType=java.time.LocalDateTime}
36 43 GROUP BY time
37 44 </select>
38 45  
  46 + <!-- TODO @dhb52:根据上面建议,进行优化 -->
39 47 <select id="selectCustomerCreateCountGroupbyUser"
40 48 resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByUserRespVO">
41 49 SELECT owner_user_id, COUNT(1) AS customer_create_count
... ... @@ -50,6 +58,7 @@
50 58 GROUP BY owner_user_id
51 59 </select>
52 60  
  61 + <!-- TODO @dhb52:根据上面建议,进行优化 -->
53 62 <select id="selectCustomerDealCountGroupbyUser"
54 63 resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByUserRespVO">
55 64 SELECT a.owner_user_id, count( DISTINCT a.id ) AS customer_deal_count
... ... @@ -66,6 +75,7 @@
66 75 GROUP BY a.owner_user_id
67 76 </select>
68 77  
  78 + <!-- TODO @dhb52:根据上面建议,进行优化 -->
69 79 <select id="selectContractPriceGroupbyUser"
70 80 resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByUserRespVO">
71 81 SELECT owner_user_id, IFNULL(SUM(total_price), 0) AS contract_price
... ... @@ -82,6 +92,7 @@
82 92 </select>
83 93  
84 94  
  95 + <!-- TODO @dhb52:根据上面建议,进行优化 -->
85 96 <select id="selectReceivablePriceGroupbyUser"
86 97 resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByUserRespVO">
87 98 SELECT owner_user_id,
... ...
yudao-server/pom.xml
... ... @@ -46,11 +46,11 @@
46 46 <!-- <version>${revision}</version>-->
47 47 <!-- </dependency>-->
48 48 <!-- 工作流。默认注释,保证编译速度 -->
49   -<!-- <dependency>-->
50   -<!-- <groupId>cn.iocoder.boot</groupId>-->
51   -<!-- <artifactId>yudao-module-bpm-biz</artifactId>-->
52   -<!-- <version>${revision}</version>-->
53   -<!-- </dependency>-->
  49 + <dependency>
  50 + <groupId>cn.iocoder.boot</groupId>
  51 + <artifactId>yudao-module-bpm-biz</artifactId>
  52 + <version>${revision}</version>
  53 + </dependency>
54 54 <!-- 支付服务。默认注释,保证编译速度 -->
55 55 <!-- <dependency>-->
56 56 <!-- <groupId>cn.iocoder.boot</groupId>-->
... ... @@ -88,18 +88,18 @@
88 88 <!-- </dependency>-->
89 89  
90 90 <!-- CRM 相关模块。默认注释,保证编译速度 -->
91   -<!-- <dependency>-->
92   -<!-- <groupId>cn.iocoder.boot</groupId>-->
93   -<!-- <artifactId>yudao-module-crm-biz</artifactId>-->
94   -<!-- <version>${revision}</version>-->
95   -<!-- </dependency>-->
  91 + <dependency>
  92 + <groupId>cn.iocoder.boot</groupId>
  93 + <artifactId>yudao-module-crm-biz</artifactId>
  94 + <version>${revision}</version>
  95 + </dependency>
96 96  
97 97 <!-- ERP 相关模块。默认注释,保证编译速度 -->
98   -<!-- <dependency>-->
99   -<!-- <groupId>cn.iocoder.boot</groupId>-->
100   -<!-- <artifactId>yudao-module-erp-biz</artifactId>-->
101   -<!-- <version>${revision}</version>-->
102   -<!-- </dependency>-->
  98 + <dependency>
  99 + <groupId>cn.iocoder.boot</groupId>
  100 + <artifactId>yudao-module-erp-biz</artifactId>
  101 + <version>${revision}</version>
  102 + </dependency>
103 103  
104 104 <!-- spring boot 配置所需依赖 -->
105 105 <dependency>
... ...