diff --git a/src/main/java/com/bao/dating/controller/DataAnalysisController.java b/src/main/java/com/bao/dating/controller/DataAnalysisController.java new file mode 100644 index 0000000..2db90fc --- /dev/null +++ b/src/main/java/com/bao/dating/controller/DataAnalysisController.java @@ -0,0 +1,66 @@ +package com.bao.dating.controller; + +import com.bao.dating.common.Result; +import com.bao.dating.common.ResultCode; +import com.bao.dating.pojo.vo.DataAnalysisVO; +import com.bao.dating.util.DataAnalysisRedisService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.util.ArrayList; + +@RestController +@RequestMapping("/dataAnalysis") +public class DataAnalysisController { + @Resource + private DataAnalysisRedisService dataAnalysisRedisService; + + /** + * 统计动态 + * @param days 统计时常 + * @return result + */ + @GetMapping("/postTotal") + public Result postDataAnalysis(@RequestParam Integer days){ + if (checkParam(days)){ + return Result.error(ResultCode.PARAM_ERROR); + } + Long count = dataAnalysisRedisService.countPostTotalByDays(days); + String timePeriod = dataAnalysisRedisService.calculateTimeRanges(LocalDateTime.now(), days); + DataAnalysisVO dataAnalysisVO = new DataAnalysisVO(); + dataAnalysisVO.setTotal(count); + dataAnalysisVO.setTimePeriod(timePeriod); + return Result.success(ResultCode.SUCCESS,dataAnalysisVO); + } + + @GetMapping("/loginUserTotal") + public Result loginUserDataAnalysis(@RequestParam Integer days){ + if (checkParam(days)){ + return Result.error(ResultCode.PARAM_ERROR); + } + Long count = dataAnalysisRedisService.countLoginUserTotalByDays(days); + String timePeriod = dataAnalysisRedisService.calculateTimeRanges(LocalDateTime.now(), days); + DataAnalysisVO dataAnalysisVO = new DataAnalysisVO(); + dataAnalysisVO.setTotal(count); + dataAnalysisVO.setTimePeriod(timePeriod); + return Result.success(ResultCode.SUCCESS,dataAnalysisVO); + } + + /** + * 检验参数 + * @param days 查询天数 + * @return 参数是否合法 + */ + public Boolean checkParam(Integer days){ + ArrayList day = new ArrayList<>(); + day.add(7); + day.add(15); + day.add(30); + day.add(365); + return days != null && day.contains(days); + } +} diff --git a/src/main/java/com/bao/dating/controller/PostController.java b/src/main/java/com/bao/dating/controller/PostController.java index 799e9eb..0840c13 100644 --- a/src/main/java/com/bao/dating/controller/PostController.java +++ b/src/main/java/com/bao/dating/controller/PostController.java @@ -1,17 +1,17 @@ package com.bao.dating.controller; - -import com.bao.dating.anno.Log; import com.bao.dating.common.Result; import com.bao.dating.common.ResultCode; import com.bao.dating.pojo.dto.PostRequestDTO; import com.bao.dating.pojo.entity.Post; import com.bao.dating.pojo.vo.PostEditVO; import com.bao.dating.service.PostService; +import com.bao.dating.util.DataAnalysisRedisService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; +import javax.annotation.Resource; import java.util.List; /** @@ -26,6 +26,9 @@ public class PostController { @Autowired private PostService postService; + @Resource + private DataAnalysisRedisService dataAnalysisRedisService; + /** * 上传媒体文件接口 like * @param files 媒体文件数组 @@ -46,6 +49,9 @@ public class PostController { public Result createPostJson(@RequestBody PostRequestDTO postDTO) { // 调用 Service 层处理发布动态业务逻辑 Post result = postService.createPost(postDTO); + //这里将发布动态操作记录到redis中 + Long postId = result.getPostId(); + dataAnalysisRedisService.recordPostData(postId); return Result.success(ResultCode.SUCCESS, "动态发布成功,等待审核。", result); } diff --git a/src/main/java/com/bao/dating/controller/UserController.java b/src/main/java/com/bao/dating/controller/UserController.java index 13b709a..00899c1 100644 --- a/src/main/java/com/bao/dating/controller/UserController.java +++ b/src/main/java/com/bao/dating/controller/UserController.java @@ -9,11 +9,13 @@ import com.bao.dating.pojo.dto.UserLoginDTO; import com.bao.dating.pojo.vo.UserInfoVO; import com.bao.dating.pojo.vo.UserLoginVO; import com.bao.dating.service.UserService; +import com.bao.dating.util.DataAnalysisRedisService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.util.List; +import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.Map; @@ -29,6 +31,9 @@ public class UserController { @Autowired private UserService userService; + @Resource + private DataAnalysisRedisService dataAnalysisRedisService; + /** * 登录 * @param userLoginDTO 登录参数 @@ -36,6 +41,12 @@ public class UserController { @PostMapping("/login") public Result login(@RequestBody UserLoginDTO userLoginDTO) { UserLoginVO userloginVO = userService.userLogin(userLoginDTO); + //将用户登录记录到redis中 + if (userloginVO != null){ + Long userId = userloginVO.getUserId(); + dataAnalysisRedisService.recordLoginUserData(userId); + } + return Result.success(ResultCode.SUCCESS, "登录成功", userloginVO); } @@ -128,6 +139,11 @@ public class UserController { } //登录 UserLoginVO vo = userService.loginByPhone(phone); + //将用户登录操作记录到redis中 + if (vo != null){ + Long userId = vo.getUserId(); + dataAnalysisRedisService.recordLoginUserData(userId); + } return Result.success(ResultCode.SUCCESS, "登录成功", vo); } @@ -200,6 +216,9 @@ public class UserController { if (userLoginVO == null){ return Result.error(ResultCode.FAIL,"请先注册用户或添加邮箱"); } + //将用户登录操作记录到redis中 + Long userId = userLoginVO.getUserId(); + dataAnalysisRedisService.recordLoginUserData(userId); return Result.success(ResultCode.SUCCESS,"用户登录成功",userLoginVO); } diff --git a/src/main/java/com/bao/dating/pojo/vo/DataAnalysisVO.java b/src/main/java/com/bao/dating/pojo/vo/DataAnalysisVO.java new file mode 100644 index 0000000..c3e1d80 --- /dev/null +++ b/src/main/java/com/bao/dating/pojo/vo/DataAnalysisVO.java @@ -0,0 +1,11 @@ +package com.bao.dating.pojo.vo; + +import lombok.Data; + +@Data +public class DataAnalysisVO { + //所有总数 + private Long total; + //时间段 + private String timePeriod; +} diff --git a/src/main/java/com/bao/dating/util/DataAnalysisRedisService.java b/src/main/java/com/bao/dating/util/DataAnalysisRedisService.java new file mode 100644 index 0000000..1f4778c --- /dev/null +++ b/src/main/java/com/bao/dating/util/DataAnalysisRedisService.java @@ -0,0 +1,101 @@ +package com.bao.dating.util; + +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.util.concurrent.TimeUnit; + +/** + * 基于redis的数据分析工具 -统计动态总数和登录人数 + */ +@Component +public class DataAnalysisRedisService { + // Redis Key 定义 + //动态总数 + private static final String REDIS_KEY_POST_TOTAL = "data:post:total"; + //活跃人数 + private static final String REDIS_KEY_LOGIN_USER_TOTAL = "data:loginUser:total"; + + @Resource + private RedisTemplate redisTemplate; + + /** + * 记录动态向redis中添加数据 + * @param postId 动态id + */ + public void recordPostData(Long postId) { + // 获取当前毫秒级时间戳(作为Sorted Set的score) + long currentTimestamp = System.currentTimeMillis(); + // 1. 记录动态数据 + redisTemplate.opsForZSet().add(REDIS_KEY_POST_TOTAL, postId, currentTimestamp); + // 可选:设置Key的过期时间(1年+1天,避免数据无限累积) + setKeyExpire(REDIS_KEY_POST_TOTAL); + } + + /** + * 记录动态向redis中添加数据 + * @param userId 用户id + */ + public void recordLoginUserData(Long userId) { + // 获取当前毫秒级时间戳(作为Sorted Set的score) + long currentTimestamp = System.currentTimeMillis(); + // 1. 记录动态数据 + redisTemplate.opsForZSet().add(REDIS_KEY_LOGIN_USER_TOTAL, userId, currentTimestamp); + // 可选:设置Key的过期时间(1年+1天,避免数据无限累积) + setKeyExpire(REDIS_KEY_LOGIN_USER_TOTAL); + } + + /** + * 设置Redis Key的过期时间(1年+1天,确保1年内的数据都能被查询) + */ + private void setKeyExpire(String redisKey) { + if (Boolean.FALSE.equals(redisTemplate.hasKey(redisKey))) { + redisTemplate.expire(redisKey, 366, TimeUnit.DAYS); + } + } + + /** + * 统计指定时间段内发布的动态 + * @param days 指定时间段 + * @return 总数 + */ + public Long countPostTotalByDays(int days) { + // 1. 计算时间范围:结束时间=当前时间,起始时间=当前时间 - N天的毫秒数 + long endTimestamp = System.currentTimeMillis(); + long startTimestamp = endTimestamp - (long) days * 24 * 60 * 60 * 1000; + // 2. key min max - 统计指定score范围内的成员数量(高效,直接返回数量) + return redisTemplate.opsForZSet().count(REDIS_KEY_POST_TOTAL, startTimestamp, endTimestamp); + } + + public Long countLoginUserTotalByDays(int days) { + // 1. 计算时间范围:结束时间=当前时间,起始时间=当前时间 - N天的毫秒数 + long endTimestamp = System.currentTimeMillis(); + long startTimestamp = endTimestamp - (long) days * 24 * 60 * 60 * 1000; + // 2. key min max - 统计指定score范围内的成员数量(高效,直接返回数量) + return redisTemplate.opsForZSet().count(REDIS_KEY_LOGIN_USER_TOTAL, startTimestamp, endTimestamp); + } + + /** + * 核心方法:传入当前时间,返回各时间段的结果 + * @param currentDateTime 当前时间(LocalDateTime类型,可直接通过LocalDateTime.now()获取) + */ + public String calculateTimeRanges(LocalDateTime currentDateTime,int days) { + // 1. 计算各时间段的起始时间(当前时间往前推对应时长) + LocalDateTime sevenDaysAgo = currentDateTime.minusDays(7); + LocalDateTime fifteenDaysAgo = currentDateTime.minusDays(15); + LocalDateTime thirtyDaysAgo = currentDateTime.minusDays(30); + LocalDateTime oneYearAgo = currentDateTime.minusYears(1); + if (days == 7) + return sevenDaysAgo + " - " + currentDateTime; + if (days == 15) + return fifteenDaysAgo + " - " + currentDateTime; + if (days == 30) + return thirtyDaysAgo + " - " + currentDateTime; + if (days == 365) + return oneYearAgo + " - " + currentDateTime; + return null; + } + +}