指标数据查看的细节分析(下)
在上一篇章节中,讲到了再查询数据时,需要有 数据获取执行器,这里使用到了策略模式,对于数据获取执行器有三种策略:
- VideoReportDataFetchExecutor: 视频报表数据获取执行器
- VideoTypeReportDataFetchExecutor: 视频类型报表数据获取执行器
- ParentVideoTypeReportDataFetchExecutor: 父视频类型报表数据获取执行器
详细详解了 视频报表数据获取执行器 的执行流程,而在核心的流程中用到了模板模式,所以核心其实是在父类AbstractReportDataFetchExecutor
中了。
执行总览(从 execute 开始)
org.javaup.handler.core.queryfetch.AbstractReportDataFetchExecutor
入口方法:execute(totalParamTransfers)
- 获取查询入参
QueryDataTransfers
- 通过实现类先查“维度数据”(视频列表)
- 基于维度数据查询指标数据
- 过滤掉配置为“不显示”的指标
- 按维度进行分组汇总,构建“维度值 → 指标值映射”
- 组装行数据(含第一行“累记”)
- 组装
ReportVo
并封装进RuleHandleOutput
返回
public RuleHandleOutput execute(TotalParamTransfers totalParamTransfers) {
RuleHandleOutput ruleHandleOutput = new RuleHandleOutput();
QueryDataTransfers queryDataTransfers = totalParamTransfers.getParamTransfers().getQueryDataTransfers();
//查询视频维度
List<T> dimensionDataList = fetchDimensionData(queryDataTransfers);
if (CollectionUtils.isEmpty(dimensionDataList)) {
return ruleHandleOutput;
}
//获取到维度数据
List<Map<String, Object>> metricDataList = getMetricDataList(queryDataTransfers, dimensionDataList);
if (CollectionUtils.isEmpty(metricDataList)) {
return ruleHandleOutput;
}
List<Metric> metricList = metricService.selectByRuleId(totalParamTransfers.getParamTransfers().getRuleId());
//metricShowStatusMap:指标名称与显示状态的映射
Map<String, Integer> metricShowStatusMap = metricList.stream().collect(Collectors.toMap(Metric::getMetricName,
Metric::getShowStatus));
//这里是过滤掉不显示的指标
List<Map<String, Object>> filterMetricDataList = new ArrayList<>(metricDataList.size());
for (Map<String, Object> metricDataMap : metricDataList) {
Map<String, Object> filterMetricDataMap = MapUtil.newHashMap();
for (Entry<String, Object> entry : metricDataMap.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
Integer showStatus = metricShowStatusMap.get(key);
//showStatus为空,或者显示状态为显示,那么就放入新的指标数据中
//如果showStatus为空,说明是分组字段,比如video_id、video_type_id等,这些字段都是要显示的
if (Objects.isNull(showStatus) || showStatus.equals(ShowStatus.YES.getCode())) {
filterMetricDataMap.put(key, value);
}
}
filterMetricDataList.add(filterMetricDataMap);
}
List<String> filterMetricNameList = queryDataTransfers.getQueryDataExecuteTransfers().getMetricList().stream()
.map(Metric::getMetricName)
.filter(metricName -> ShowStatus.YES.getCode().equals(metricShowStatusMap.get(metricName))).toList();
List<String> filterMetricDescribeList = queryDataTransfers.getQueryDataExecuteTransfers().getMetricList()
.stream()
.filter(metric -> ShowStatus.YES.getCode().equals(metricShowStatusMap.get(metric.getMetricName())))
.map(Metric::getMetricDescribe)
.toList();
//将指标数据进行分组
//如果是视频维度,那么按照视频id进行分组 key:视频id value:指标数据
//如果是视频维度,那么按照视频分类id进行分组 key:视频分类id value:指标数据
Map<Object, Map<String, Object>> metricMap = groupByMetricDataList(queryDataTransfers,filterMetricDataList);
//组装行的指标数据集合
List<RowDataVo> rowDataVoList = assembleRowDataList(dimensionDataList, metricMap, filterMetricNameList);
//设置报表数据
ReportVo reportVo = new ReportVo();
//规则信息
reportVo.setRuleDescribe(queryDataTransfers.getQueryDataExecuteTransfers().getRuleDescribe());
//报表的统计时间
reportVo.setDateTime(DateTimeFunc.getEntryName(queryDataTransfers.getDateType(), queryDataTransfers.getStartTime(),
queryDataTransfers.getEndTime()));
//报表的列名
List<String> columnNameList = new ArrayList<>();
//第一个列名是维度名称
columnNameList.add(queryDataTransfers.getQueryDataExecuteTransfers().getAccumulateColumnName());
//后面是指标列名
columnNameList.addAll(filterMetricDescribeList);
reportVo.setColumnNameList(columnNameList);
//行数据
reportVo.setRowDataVoList(rowDataVoList);
ruleHandleOutput.setReportVo(reportVo);
return ruleHandleOutput;
}
在上一章节中,讲到了获取到维度数据(getMetricDataList
)方法执行的流程,得到了metricDataList
实际的指标数据
metricDataList真实数据结构
[
{
"video_id": 7762202481762402600,
"watch_count": 123,
"watch_like_count": 3,
"watch_dislike_count": 0,
"watch_complete_count": 34,
"watch_like_rate": 0.02,
"watch_dislike_rate": 0,
"watch_complete_rate": 0.28
},
{
"video_id": 7762202481762402601,
"watch_count": 129,
"watch_like_count": 17,
"watch_dislike_count": 0,
"watch_complete_count": 23,
"watch_like_rate": 0.13,
"watch_dislike_rate": 0,
"watch_complete_rate": 0.18
}
]
指标过滤的双重处理机制
在获取到原始指标数据后、进行分组之前,有一个双重过滤机制来处理"哪些指标需要显示":
第一重过滤:过滤指标数据行中的列
这一步是对数据行进行过滤,即从数据库查询返回的 metricDataList
中,把每一行里"不需要显示的指标列"剔除掉。
List<Metric> metricList = metricService.selectByRuleId(totalParamTransfers.getParamTransfers().getRuleId());
//metricShowStatusMap:指标名称与显示状态的映射
Map<String, Integer> metricShowStatusMap = metricList.stream().collect(Collectors.toMap(Metric::getMetricName,
Metric::getShowStatus));
//这里是过滤掉不显示的指标
List<Map<String, Object>> filterMetricDataList = new ArrayList<>(metricDataList.size());
for (Map<String, Object> metricDataMap : metricDataList) {
Map<String, Object> filterMetricDataMap = MapUtil.newHashMap();
for (Entry<String, Object> entry : metricDataMap.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
Integer showStatus = metricShowStatusMap.get(key);
//showStatus为空,或者显示状态为显示,那么就放入新的指标数据中
//如果showStatus为空,说明是分组字段,比如video_id、video_type_id等,这些字段都是要显示的
if (Objects.isNull(showStatus) || showStatus.equals(ShowStatus.YES.getCode())) {
filterMetricDataMap.put(key, value);
}
}
filterMetricDataList.add(filterMetricDataMap);
}
1. 构建映射表 metricShowStatusMap
:
- 从数据库查询该规则下的所有指标配置
- 建立"指标名称 → 显示状态"的映射关系
- 例如:
{"watch_count": 123, "watch_like_count": 1,}
2. 遍历每行数据的每个键值对:
- 对于每个字段(key),去
metricShowStatusMap
中查找它的显示状态 - 如果
showStatus == null
:说明这个字段不是指标字段,而是分组维度字段(如video_id
、video_type_id
),必须保留 - 如果
showStatus == ShowStatus.YES
:说明这是需要显示的指标,保留 - 其他情况(showStatus 为 NO):不保留,过滤掉
3. 结果:
- 原始数据行可能包含 10 个字段,过滤后可能只剩 7 个(1 个维度字段 + 6 个需要显示的指标)
- 得到
filterMetricDataList
,每行只保留需要的字段
举例说明:
假设从数据库查到一行数据:
{
"video_id": 101, // 分组字段
"watch_count": 1000, // 指标,showStatus=YES
"watch_like_count": 50, // 指标,showStatus=YES
"internal_metric": 123, // 指标,showStatus=NO(不显示)
"watch_complete_count": 800 // 指标,showStatus=YES
}
过滤后: