statistics_info
CREATE TABLE `statistics_info` (
`statistics_date` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '统计日期',
`user_id` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户ID',
`data_type` tinyint(1) NOT NULL COMMENT '数据统计类型',
`statistics_count` int DEFAULT NULL COMMENT '统计数量',
PRIMARY KEY (`statistics_date`,`user_id`,`data_type`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='数据统计';
定时任务统计数据
//每晚12点统计
@Scheduled(cron = "0 0 0 * * ?")
public void statisticsData() {
statisticsInfoService.statisticsData();
}
statisticsInfoService.statisticsData();
@Override
public void statisticsData() {
List<StatisticsInfo> statisticsInfoList = new ArrayList<>();
final String statisticsDate = DateUtil.getBeforeDayDate(1);
//统计播放量
Map<String, Integer> videoPlayCountMap = redisComponent.getVideoPlayCount(statisticsDate);
List<String> playVideoKeys = new ArrayList<>(videoPlayCountMap.keySet());
//去除前缀,得到视频id
//例如easylive:video:playcount:2024-11-23:41bMMoxp70
//转为41bMMoxp70
playVideoKeys = playVideoKeys.stream()
.map(item -> item.substring(item.lastIndexOf(":") + 1))
.collect(Collectors.toList());
//根据视频id列表查询视频列表
VideoInfoQuery videoInfoQuery = new VideoInfoQuery();
videoInfoQuery.setVideoIdArray(playVideoKeys.toArray(new String[playVideoKeys.size()]));
List<VideoInfo> videoInfoList = videoInfoMapper.selectList(videoInfoQuery);
//生成<userId,videoCount>Map集合
Map<String, Integer> videoCountMap = videoInfoList.stream()
.collect(Collectors.groupingBy(VideoInfo::getUserId,
Collectors.summingInt(item -> videoPlayCountMap.get(Constants.REDIS_KEY_VIDEO_PLAY_COUNT + statisticsDate + ":" + item.getVideoId()))));
videoCountMap.forEach((k, v) -> {
StatisticsInfo statisticsInfo = new StatisticsInfo();
statisticsInfo.setStatisticsDate(statisticsDate);
statisticsInfo.setUserId(k);
statisticsInfo.setDataType(StatisticsTypeEnum.PLAY.getType());
statisticsInfo.setStatisticsCount(v);
statisticsInfoList.add(statisticsInfo);
});
//统计粉丝量
List<StatisticsInfo> fansDataList = this.statisticsInfoMapper.selectStatisticsFans(statisticsDate);
for (StatisticsInfo statisticsInfo : fansDataList) {
statisticsInfo.setStatisticsDate(statisticsDate);
statisticsInfo.setDataType(StatisticsTypeEnum.FANS.getType());
}
statisticsInfoList.addAll(fansDataList);
//统计评论
List<StatisticsInfo> commentDataList = this.statisticsInfoMapper.selectStatisticsComment(statisticsDate);
for (StatisticsInfo statisticsInfo : commentDataList) {
statisticsInfo.setStatisticsDate(statisticsDate);
statisticsInfo.setDataType(StatisticsTypeEnum.COMMENT.getType());
}
statisticsInfoList.addAll(commentDataList);
//统计 弹幕、点赞、收藏、投币
List<StatisticsInfo> statisticsInfoOthers = this.statisticsInfoMapper.selectStatisticsInfo(statisticsDate,
new Integer[]{UserActionTypeEnum.VIDEO_LIKE.getType(), UserActionTypeEnum.VIDEO_COIN.getType(), UserActionTypeEnum.VIDEO_COLLECT.getType()});
for (StatisticsInfo statisticsInfo : statisticsInfoOthers) {
statisticsInfo.setStatisticsDate(statisticsDate);
if (UserActionTypeEnum.VIDEO_LIKE.getType().equals(statisticsInfo.getDataType())) {
statisticsInfo.setDataType(StatisticsTypeEnum.LIKE.getType());
} else if (UserActionTypeEnum.VIDEO_COLLECT.getType().equals(statisticsInfo.getDataType())) {
statisticsInfo.setDataType(StatisticsTypeEnum.COLLECTION.getType());
} else if (UserActionTypeEnum.VIDEO_COIN.getType().equals(statisticsInfo.getDataType())) {
statisticsInfo.setDataType(StatisticsTypeEnum.COIN.getType());
}
}
statisticsInfoList.addAll(statisticsInfoOthers);
this.statisticsInfoMapper.insertOrUpdateBatch(statisticsInfoList);
}
这段代码是一个定时任务,用于每天统计并记录用户在平台上的各种行为数据,主要包括播放量、粉丝量、评论量、点赞量、收藏量、投币量等。统计结果会被保存到statistics_info
表中。
1. 表结构分析 (statistics_info
)
表statistics_info
记录了用户的各类行为数据,包含以下字段:
- statistics_date:统计日期,格式为
yyyy-MM-dd
。 - user_id:用户ID。
- data_type:数据类型,使用数字表示不同的统计类型(如播放量、评论、点赞等)。
- statistics_count:统计数量,记录该类型行为发生的次数或数量。
表的主键是 statistics_date
+ user_id
+ data_type
,保证了每个用户在每一天每种数据类型的唯一性。
2. 定时任务 (@Scheduled(cron = "0 0 0 * * ?")
)
这段代码使用了Spring的定时任务注解@Scheduled
,使得statisticsData()
方法每天在午夜12点(即00:00)自动执行,进行数据统计。cron
表达式"0 0 0 * * ?"
表示每天的0点0分0秒执行。
3. statisticsData()
方法分析
(1) 获取统计日期
final String statisticsDate = DateUtil.getBeforeDayDate(1);
这里获取的是前一天的日期,并将其作为统计日期。方法DateUtil.getBeforeDayDate(1)
返回的是当前日期的前一天。
(2) 统计播放量
Map<String, Integer> videoPlayCountMap = redisComponent.getVideoPlayCount(statisticsDate);
从Redis中获取前一天所有视频的播放量数据。返回的是一个Map
,键是视频的Redis键(例如 easylive:video:playcount:2024-11-23:41bMMoxp70
),值是视频的播放次数。
List<String> playVideoKeys = new ArrayList<>(videoPlayCountMap.keySet());
playVideoKeys = playVideoKeys.stream()
.map(item -> item.substring(item.lastIndexOf(":") + 1))
.collect(Collectors.toList());
提取视频ID(即去掉Redis键的前缀部分),得到一个只包含视频ID的列表。
VideoInfoQuery videoInfoQuery = new VideoInfoQuery();
videoInfoQuery.setVideoIdArray(playVideoKeys.toArray(new String[0]));
List<VideoInfo> videoInfoList = videoInfoMapper.selectList(videoInfoQuery);
根据提取到的视频ID,查询数据库,获取对应的VideoInfo
对象。每个VideoInfo
包含视频的详细信息,其中最重要的是视频所属的用户ID。
Map<String, Integer> videoCountMap = videoInfoList.stream()
.collect(Collectors.groupingBy(VideoInfo::getUserId,
Collectors.summingInt(item -> videoPlayCountMap.get(Constants.REDIS_KEY_VIDEO_PLAY_COUNT + statisticsDate + ":" + item.getVideoId()))));
根据视频信息生成一个Map<String, Integer>
,其中键是用户ID,值是该用户视频的总播放量。groupingBy
按用户ID分组,并通过summingInt
将相同用户的视频的播放量累加起来。
videoCountMap.forEach((k, v) -> {
StatisticsInfo statisticsInfo = new StatisticsInfo();
statisticsInfo.setStatisticsDate(statisticsDate);
statisticsInfo.setUserId(k);
statisticsInfo.setDataType(StatisticsTypeEnum.PLAY.getType());
statisticsInfo.setStatisticsCount(v);
statisticsInfoList.add(statisticsInfo);
});
根据统计结果构造StatisticsInfo
对象,并将其加入到statisticsInfoList
中。这些对象记录了每个用户的播放量。
(3) 统计粉丝量
List<StatisticsInfo> fansDataList = this.statisticsInfoMapper.selectStatisticsFans(statisticsDate);
通过statisticsInfoMapper
查询出前一天所有用户的粉丝量。
for (StatisticsInfo statisticsInfo : fansDataList) {
statisticsInfo.setStatisticsDate(statisticsDate);
statisticsInfo.setDataType(StatisticsTypeEnum.FANS.getType());
}
statisticsInfoList.addAll(fansDataList);
设置统计日期为前一天,数据类型设置为粉丝量,然后将查询到的粉丝数据加入到统计列表。
(4) 统计评论量
List<StatisticsInfo> commentDataList = this.statisticsInfoMapper.selectStatisticsComment(statisticsDate);
查询出前一天的评论量数据。
for (StatisticsInfo statisticsInfo : commentDataList) {
statisticsInfo.setStatisticsDate(statisticsDate);
statisticsInfo.setDataType(StatisticsTypeEnum.COMMENT.getType());
}
statisticsInfoList.addAll(commentDataList);
设置数据类型为评论量,并将数据加入到统计列表。
(5) 统计弹幕、点赞、收藏、投币
List<StatisticsInfo> statisticsInfoOthers = this.statisticsInfoMapper.selectStatisticsInfo(statisticsDate,
new Integer[]{UserActionTypeEnum.VIDEO_LIKE.getType(), UserActionTypeEnum.VIDEO_COIN.getType(), UserActionTypeEnum.VIDEO_COLLECT.getType()});
查询出前一天的点赞、投币、收藏等行为的数据。
for (StatisticsInfo statisticsInfo : statisticsInfoOthers) {
statisticsInfo.setStatisticsDate(statisticsDate);
if (UserActionTypeEnum.VIDEO_LIKE.getType().equals(statisticsInfo.getDataType())) {
statisticsInfo.setDataType(StatisticsTypeEnum.LIKE.getType());
} else if (UserActionTypeEnum.VIDEO_COLLECT.getType().equals(statisticsInfo.getDataType())) {
statisticsInfo.setDataType(StatisticsTypeEnum.COLLECTION.getType());
} else if (UserActionTypeEnum.VIDEO_COIN.getType().equals(statisticsInfo.getDataType())) {
statisticsInfo.setDataType(StatisticsTypeEnum.COIN.getType());
}
}
statisticsInfoList.addAll(statisticsInfoOthers);
将查询到的行为数据类型根据不同的UserActionTypeEnum
做转换,设置数据类型为点赞、收藏或投币,并加入到统计列表中。
(6) 批量插入或更新数据
this.statisticsInfoMapper.insertOrUpdateBatch(statisticsInfoList);
最后,将收集到的所有统计数据(包括播放量、粉丝量、评论量、行为量等)批量插入或更新到statistics_info
表中。
总结:
这段代码通过定时任务每天统计用户的各类行为数据,包括播放量、粉丝量、评论量、点赞量、投币量等。统计数据从Redis中获取并结合数据库中的视频信息,生成统计结果,并最终存入数据库表中。这些数据可以用于后续的分析、展示或其他业务需求。
获取前一天的数据
controller
//获取前一天的数据
@RequestMapping("/getActualTimeStatisticsInfo")
@GlobalInterceptor
public ResponseVO getActualTimeStatisticsInfo() {
//获取前一天的日期
String preDate = DateUtil.getBeforeDayDate(1);
TokenUserInfoDto tokenUserInfoDto = getTokenUserInfoDto();
StatisticsInfoQuery param = new StatisticsInfoQuery();
param.setStatisticsDate(preDate);
param.setUserId(tokenUserInfoDto.getUserId());
List<StatisticsInfo> preDayData = statisticsInfoService.findListByParam(param);
//根据数据类型和数据对应的数量构建map
Map<Integer, Integer> preDayDataMap = preDayData.stream().collect(Collectors.toMap(StatisticsInfo::getDataType, StatisticsInfo::getStatisticsCount, (item1, item2) -> item2));
//web端查询粉丝数,后台查询总数量
Map<String, Integer> totalCountInfo = statisticsInfoService.getStatisticsInfoActualTime(tokenUserInfoDto.getUserId());
Map<String, Object> result = new HashMap<>();
result.put("preDayData", preDayDataMap);
result.put("totalCountInfo", totalCountInfo);
//测试定时任务使用
statisticsInfoService.statisticsData();
return getSuccessResponseVO(result);
}
Map<String, Integer> totalCountInfo = statisticsInfoService.getStatisticsInfoActualTime(tokenUserInfoDto.getUserId());
@Override
public Map<String, Integer> getStatisticsInfoActualTime(String userId) {
Map<String, Integer> result = statisticsInfoMapper.selectTotalCountInfo(userId);
if (!StringTools.isEmpty(userId)) {
//查询粉丝数
result.put("userCount", userFocusMapper.selectFansCount(userId));
} else {
//管理端查询用户总数
result.put("userCount", userInfoMapper.selectCount(new UserInfoQuery()));
}
return result;
}
获取一周的数据
controller
//获取一周的数据
@RequestMapping("/getWeekStatisticsInfo")
@GlobalInterceptor
public ResponseVO getWeekStatisticsInfo(Integer dataType) {
TokenUserInfoDto tokenUserInfoDto = getTokenUserInfoDto();
List<String> dateList = DateUtil.getBeforeDates(7);
StatisticsInfoQuery param = new StatisticsInfoQuery();
//设置数据类型
param.setDataType(dataType);
param.setUserId(tokenUserInfoDto.getUserId());
param.setStatisticsDateStart(dateList.get(0));
param.setStatisticsDateEnd(dateList.get(dateList.size() - 1));
param.setOrderBy("statistics_date asc");
List<StatisticsInfo> statisticsInfoList = statisticsInfoService.findListByParam(param);
//按照日期分组
Map<String, StatisticsInfo> dataMap =
statisticsInfoList.stream()
.collect(Collectors.toMap(item -> item.getStatisticsDate(), Function.identity(), (data1, data2) -> data2));
List<StatisticsInfo> resultDataList = new ArrayList<>();
for (String date : dateList) {
StatisticsInfo dataItem = dataMap.get(date);
//没有数据就设为默认0
if (dataItem == null) {
dataItem = new StatisticsInfo();
dataItem.setStatisticsCount(0);
dataItem.setStatisticsDate(date);
}
resultDataList.add(dataItem);
}
return getSuccessResponseVO(resultDataList);
}
后台获取实时数据
//获取实时数据
@RequestMapping("/getActualTimeStatisticsInfo")
public ResponseVO getActualTimeStatisticsInfo() {
String preDate = DateUtil.getBeforeDayDate(1);
StatisticsInfoQuery param = new StatisticsInfoQuery();
param.setStatisticsDate(preDate);
List<StatisticsInfo> preDayData = statisticsInfoService.findListTotalInfoByParam(param);
//查询用户总数,替换类型为粉丝的数量
Integer userCount = userInfoService.findCountByParam(new UserInfoQuery());
preDayData.forEach(item -> {
if (StatisticsTypeEnum.FANS.getType().equals(item.getDataType())) {
item.setStatisticsCount(userCount);
}
});
//根据数据类型和数据对应的数量构建map
Map<Integer, Integer> preDayDataMap = preDayData.stream()
.collect(Collectors.toMap(StatisticsInfo::getDataType, StatisticsInfo::getStatisticsCount, (item1,
item2) -> item2));
Map<String, Integer> totalCountInfo = statisticsInfoService.getStatisticsInfoActualTime(null);
Map<String, Object> result = new HashMap<>();
result.put("preDayData", preDayDataMap);
result.put("totalCountInfo", totalCountInfo);
return getSuccessResponseVO(result);
}
获取这一周的数据
controller
//获取这一周的数据
@RequestMapping("/getWeekStatisticsInfo")
public ResponseVO getWeekStatisticsInfo(Integer dataType) {
List<String> dateList = DateUtil.getBeforeDates(7);
List<StatisticsInfo> statisticsInfoList = new ArrayList<>();
StatisticsInfoQuery param = new StatisticsInfoQuery();
param.setDataType(dataType);
param.setStatisticsDateStart(dateList.get(0));
param.setStatisticsDateEnd(dateList.get(dateList.size() - 1));
param.setOrderBy("statistics_date asc");
if (!StatisticsTypeEnum.FANS.getType().equals(dataType)) {
statisticsInfoList = statisticsInfoService.findListTotalInfoByParam(param);
} else {
statisticsInfoList = statisticsInfoService.findUserCountTotalInfoByParam(param);
}
//根据数据类型进行分组
Map<String, StatisticsInfo> dataMap = statisticsInfoList.stream().collect(Collectors.toMap(item -> item.getStatisticsDate(), Function.identity(), (data1,
data2) -> data2));
List<StatisticsInfo> resultDataList = new ArrayList<>();
for (String date : dateList) {
StatisticsInfo dataItem = dataMap.get(date);
if (dataItem == null) {
dataItem = new StatisticsInfo();
dataItem.setStatisticsCount(0);
dataItem.setStatisticsDate(date);
}
resultDataList.add(dataItem);
}
return getSuccessResponseVO(resultDataList);
}
xml
<select id="selectListTotalInfoByParam" resultMap="base_result_map">
select ifnull(sum(statistics_count),0) statistics_count,statistics_date,data_type from statistics_info s group by data_type,statistics_date
</select>
<select id="selectUserCountTotalInfoByParam" resultMap="base_result_map">
select count(1) statistics_count,DATE_FORMAT(join_time,'%Y-%m-%d') statistics_date from user_info group by statistics_date order by statistics_date asc
</select>