mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-10-31 18:28:43 +08:00 
			
		
		
		
	CRM:code review 客户总量统计
This commit is contained in:
		| @@ -5,12 +5,14 @@ import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import lombok.Data; | ||||
|  | ||||
| /** | ||||
|  * 用户客户统计响应 Base VO | ||||
|  * 用户客户统计响应 Base Response VO | ||||
|  * | ||||
|  * 目的:可以统一拼接子 VO 的 ownerUserId、ownerUserName 属性 | ||||
|  */ | ||||
| @Data | ||||
| public class CrmStatisticsCustomerByUserBaseRespVO { | ||||
|  | ||||
|     @Schema(description = "负责人ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||||
|     @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||||
|     @JsonIgnore | ||||
|     private Long ownerUserId; | ||||
|  | ||||
|   | ||||
| @@ -5,13 +5,13 @@ import com.fasterxml.jackson.annotation.JsonFormat; | ||||
| import com.fasterxml.jackson.annotation.JsonIgnore; | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import lombok.Data; | ||||
| import org.springframework.format.annotation.DateTimeFormat; | ||||
|  | ||||
| import java.math.BigDecimal; | ||||
| import java.time.LocalDate; | ||||
| import java.time.LocalDateTime; | ||||
|  | ||||
| import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY; | ||||
| import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT; | ||||
|  | ||||
| @Schema(description = "管理后台 - CRM 客户转化率分析 VO") | ||||
| @Data | ||||
| @@ -43,14 +43,14 @@ public class CrmStatisticsCustomerContractSummaryRespVO { | ||||
|     @Schema(description = "客户来源", requiredMode = Schema.RequiredMode.REQUIRED, example = "外呼") | ||||
|     private String sourceName; | ||||
|  | ||||
|     @Schema(description = "负责人ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||||
|     @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||||
|     @JsonIgnore | ||||
|     private Long ownerUserId; | ||||
|  | ||||
|     @Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") | ||||
|     private String ownerUserName; | ||||
|  | ||||
|     @Schema(description = "创建人ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | ||||
|     @Schema(description = "创建人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | ||||
|     @JsonIgnore | ||||
|     private String creatorUserId; | ||||
|  | ||||
|   | ||||
| @@ -12,7 +12,7 @@ import java.util.List; | ||||
|  | ||||
| import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | ||||
|  | ||||
| @Schema(description = "管理后台 - CRM 数据统计 员工客户分析 Request VO") | ||||
| @Schema(description = "管理后台 - CRM 数据统计的员工客户分析 Request VO") | ||||
| @Data | ||||
| public class CrmStatisticsCustomerReqVO { | ||||
|  | ||||
| @@ -39,12 +39,16 @@ public class CrmStatisticsCustomerReqVO { | ||||
|     @NotEmpty(message = "时间范围不能为空") | ||||
|     private LocalDateTime[] times; | ||||
|  | ||||
|     // TODO @dhb52:这个时间间隔,建议前端传递;例如说:字段叫 interval,枚举有天、周、月、季度、年。因为一般分析类的系统,都是交给用户选择筛选时间间隔,而我们这里是默认根据日期选项,默认对应的 interval 而已 | ||||
|     // 然后实现上,可以在 common 包的 enums 加个 DateIntervalEnum,里面一个是 interval 字段,枚举过去,然后有个 pattern 字段,用于格式化时间格式; | ||||
|     // 这样的话,可以通过 interval 获取到 pattern,然后前端就可以根据 pattern 格式化时间,计算还是交给数据库 | ||||
|     /** | ||||
|      * group by DATE_FORMAT(field, #{dateFormat}) | ||||
|      */ | ||||
|     @Schema(description = "Group By 日期格式", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "%Y%m") | ||||
|     private String sqlDateFormat; | ||||
|  | ||||
|     // TODO @dhb52:这个字段,目前是不是没啥用呀? | ||||
|     /** | ||||
|      * 数据类型 {@link CrmBizTypeEnum} | ||||
|      */ | ||||
|   | ||||
| @@ -13,17 +13,18 @@ import java.util.List; | ||||
| @Mapper | ||||
| public interface CrmStatisticsCustomerMapper { | ||||
|  | ||||
|     List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerCreateCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); | ||||
|     // TODO @dhb52:拼写,GroupBy。一般 idea 如果出现绿色的警告,可能是单词拼写错误,建议是要修改的哈; | ||||
|     List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerCreateCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); // 已经 review | ||||
|  | ||||
|     List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerDealCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); | ||||
|     List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerDealCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); // 已经 review | ||||
|  | ||||
|     List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerCreateCountGroupbyUser(CrmStatisticsCustomerReqVO reqVO); | ||||
|     List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerCreateCountGroupbyUser(CrmStatisticsCustomerReqVO reqVO); // 已经 review | ||||
|  | ||||
|     List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerDealCountGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); | ||||
|     List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerDealCountGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); // 已经 review | ||||
|  | ||||
|     List<CrmStatisticsCustomerSummaryByUserRespVO> selectContractPriceGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); | ||||
|     List<CrmStatisticsCustomerSummaryByUserRespVO> selectContractPriceGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); // 已经 review | ||||
|  | ||||
|     List<CrmStatisticsCustomerSummaryByUserRespVO> selectReceivablePriceGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); | ||||
|     List<CrmStatisticsCustomerSummaryByUserRespVO> selectReceivablePriceGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO);  // 已经 review | ||||
|  | ||||
|     List<CrmStatisticsFollowupSummaryByDateRespVO> selectFollowupRecordCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); | ||||
|  | ||||
|   | ||||
| @@ -55,7 +55,6 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe | ||||
|     @Resource | ||||
|     private DictDataApi dictDataApi; | ||||
|  | ||||
|  | ||||
|     @Override | ||||
|     public List<CrmStatisticsCustomerSummaryByDateRespVO> getCustomerSummaryByDate(CrmStatisticsCustomerReqVO reqVO) { | ||||
|         // 1. 获得用户编号数组 | ||||
| @@ -66,14 +65,17 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe | ||||
|         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); | ||||
|  | ||||
|         // 3. 获取时间序列 | ||||
|         // TODO @dhb52:3 和 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, | ||||
|             CrmStatisticsCustomerSummaryByDateRespVO::getTime, | ||||
| @@ -86,7 +88,6 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe | ||||
|                 .setCustomerCreateCount(customerCreateCountMap.getOrDefault(time, 0)) | ||||
|                 .setCustomerDealCount(customerDealCountMap.getOrDefault(time, 0)) | ||||
|         )); | ||||
|  | ||||
|         return respVoList; | ||||
|     } | ||||
|  | ||||
| @@ -131,7 +132,6 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe | ||||
|  | ||||
|         // 4. 拼接用户信息 | ||||
|         appendUserInfo(respVoList); | ||||
|  | ||||
|         return respVoList; | ||||
|     } | ||||
|  | ||||
| @@ -202,7 +202,6 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe | ||||
|  | ||||
|         // 4. 拼接用户信息 | ||||
|         appendUserInfo(respVoList); | ||||
|  | ||||
|         return respVoList; | ||||
|     } | ||||
|  | ||||
| @@ -290,7 +289,6 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe | ||||
|             new CrmStatisticsCustomerDealCycleByDateRespVO().setTime(time) | ||||
|                 .setCustomerDealCycle(customerDealCycleMap.getOrDefault(time, 0D)) | ||||
|         )); | ||||
|  | ||||
|         return respVoList; | ||||
|     } | ||||
|  | ||||
| @@ -337,9 +335,8 @@ 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()))); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -349,16 +346,17 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe | ||||
|      * @return 用户编号数组 | ||||
|      */ | ||||
|     private List<Long> getUserIds(CrmStatisticsCustomerReqVO reqVO) { | ||||
|         // 情况一:选中某个用户 | ||||
|         if (ObjUtil.isNotNull(reqVO.getUserId())) { | ||||
|             return List.of(reqVO.getUserId()); | ||||
|         } else { | ||||
|             // 1. 获得部门列表 | ||||
|             final Long deptId = reqVO.getDeptId(); | ||||
|             List<Long> deptIds = convertList(deptApi.getChildDeptList(deptId), DeptRespDTO::getId); | ||||
|             deptIds.add(deptId); | ||||
|             // 2. 获得用户编号 | ||||
|             return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId); | ||||
|         } | ||||
|         // 情况二:选中某个部门 | ||||
|         // 2.1 获得部门列表 | ||||
|         final Long deptId = reqVO.getDeptId(); | ||||
|         List<Long> deptIds = convertList(deptApi.getChildDeptList(deptId), DeptRespDTO::getId); | ||||
|         deptIds.add(deptId); | ||||
|         // 2.2 获得用户编号 | ||||
|         return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId); | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -380,6 +378,7 @@ 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(); | ||||
|   | ||||
| @@ -2,11 +2,14 @@ | ||||
| <!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" | ||||
|             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 | ||||
|         COUNT(*) AS customerCreateCount | ||||
|         FROM crm_customer | ||||
|         WHERE deleted = 0 | ||||
|         AND owner_user_id IN | ||||
| @@ -14,6 +17,7 @@ | ||||
|             #{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 | ||||
|     </select> | ||||
| @@ -21,6 +25,8 @@ | ||||
|     <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 | ||||
| @@ -31,11 +37,13 @@ | ||||
|         <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} | ||||
|         GROUP BY time | ||||
|     </select> | ||||
|  | ||||
|     <!-- TODO @dhb52:根据上面建议,进行优化 --> | ||||
|     <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 | ||||
| @@ -50,6 +58,7 @@ | ||||
|         GROUP BY owner_user_id | ||||
|     </select> | ||||
|  | ||||
|     <!-- TODO @dhb52:根据上面建议,进行优化 --> | ||||
|     <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 | ||||
| @@ -66,6 +75,7 @@ | ||||
|         GROUP BY a.owner_user_id | ||||
|     </select> | ||||
|  | ||||
|     <!-- TODO @dhb52:根据上面建议,进行优化 --> | ||||
|     <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 | ||||
| @@ -82,6 +92,7 @@ | ||||
|     </select> | ||||
|  | ||||
|  | ||||
|     <!-- TODO @dhb52:根据上面建议,进行优化 --> | ||||
|     <select id="selectReceivablePriceGroupbyUser" | ||||
|             resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByUserRespVO"> | ||||
|         SELECT owner_user_id, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 YunaiV
					YunaiV