CRM:code review 客户总量统计

This commit is contained in:
YunaiV
2024-03-09 16:32:18 +08:00
parent 85817a2426
commit 8dcc12f215
8 changed files with 63 additions and 46 deletions

View File

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

View File

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

View File

@ -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}
*/

View File

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

View File

@ -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 @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,
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();

View File

@ -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,