libilibi项目总结(16)数据统计、查询

发布于:2024-12-18 ⋅ 阅读:(85) ⋅ 点赞:(0)

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>

网站公告

今日签到

点亮在社区的每一天
去签到