From aa1eabfeb7f87b1c7e42423acc726554db2405c0 Mon Sep 17 00:00:00 2001 From: ikmkj Date: Tue, 7 Apr 2026 21:26:35 +0800 Subject: [PATCH] =?UTF-8?q?refactor(comment):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E8=AF=84=E8=AE=BA=E5=8A=9F=E8=83=BD=E5=AE=9E=E7=8E=B0=E5=B9=B6?= =?UTF-8?q?=E6=94=B9=E8=BF=9B=E7=83=AD=E9=97=A8=E9=9F=B3=E4=B9=90=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E6=9B=B4=E6=96=B0=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除CommentController中多余的权限注解导入 - 在CommentController中注入MusicService用于缓存更新 - 添加评论和删除评论时触发热门音乐缓存更新 - 提取评论数更新逻辑到独立方法updateTargetCommentCount - 删除评论时在最后同步评论数避免统计偏大 - 优化HotScoreUtil中播放量权重算法采用对数缩放 - 在MusicController的播放、收藏、点赞操作后更新热门音乐缓存 - 改进MusicRepository查询排序避免数据重复 - 重构MusicServiceImpl中热门音乐排序逻辑增强稳定性 --- .../controller/CommentController.java | 13 +++- .../controller/MusicController.java | 6 ++ .../repository/MusicRepository.java | 4 +- .../service/impl/CommentServiceImpl.java | 77 +++++++------------ .../service/impl/MusicServiceImpl.java | 10 ++- .../test/musichouduan/util/HotScoreUtil.java | 11 ++- 6 files changed, 67 insertions(+), 54 deletions(-) diff --git a/music-houduan/src/main/java/com/test/musichouduan/controller/CommentController.java b/music-houduan/src/main/java/com/test/musichouduan/controller/CommentController.java index d18996d..25c97c7 100644 --- a/music-houduan/src/main/java/com/test/musichouduan/controller/CommentController.java +++ b/music-houduan/src/main/java/com/test/musichouduan/controller/CommentController.java @@ -1,13 +1,12 @@ package com.test.musichouduan.controller; import cn.dev33.satoken.annotation.SaCheckLogin; -import cn.dev33.satoken.annotation.SaCheckRole; -import cn.dev33.satoken.annotation.SaIgnore; import cn.dev33.satoken.stp.StpUtil; import com.test.musichouduan.dto.CommentDTO; import com.test.musichouduan.dto.PageResult; import com.test.musichouduan.dto.Result; import com.test.musichouduan.service.CommentService; +import com.test.musichouduan.service.MusicService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -21,6 +20,9 @@ public class CommentController { @Autowired private CommentService commentService; + @Autowired + private MusicService musicService; + /** * 根据ID获取评论 * @@ -62,6 +64,9 @@ public class CommentController { @PostMapping("/add") public Result addComment(@RequestBody CommentDTO commentDTO) { CommentDTO result = commentService.addComment(commentDTO); + if (commentDTO.getTargetType() != null && commentDTO.getTargetType() == 1) { + musicService.updateHotMusicCache(); + } return Result.success("评论成功", result); } @@ -74,7 +79,11 @@ public class CommentController { @SaCheckLogin @DeleteMapping("/{id}") public Result deleteComment(@PathVariable Long id) { + CommentDTO commentDTO = commentService.getCommentById(id); boolean result = commentService.deleteComment(id); + if (commentDTO.getTargetType() != null && commentDTO.getTargetType() == 1) { + musicService.updateHotMusicCache(); + } return Result.success("删除成功", result); } diff --git a/music-houduan/src/main/java/com/test/musichouduan/controller/MusicController.java b/music-houduan/src/main/java/com/test/musichouduan/controller/MusicController.java index 3c1f21b..8111533 100644 --- a/music-houduan/src/main/java/com/test/musichouduan/controller/MusicController.java +++ b/music-houduan/src/main/java/com/test/musichouduan/controller/MusicController.java @@ -166,6 +166,7 @@ public class MusicController { @RequestPart(value = "cover", required = false) MultipartFile cover, @RequestPart(value = "lyric", required = false) MultipartFile lyric) { MusicDTO result = musicService.updateMusic(id, musicDTO, file, cover, lyric); + musicService.updateHotMusicCache(); return Result.success("更新成功", result); } @@ -195,6 +196,7 @@ public class MusicController { // 获取当前登录用户ID Long userId = StpUtil.getLoginIdAsLong(); boolean isCollected = musicService.toggleCollectMusic(id, userId); + musicService.updateHotMusicCache(); String message = isCollected ? "收藏成功" : "取消收藏成功"; return Result.success(message, isCollected); } @@ -227,6 +229,9 @@ public class MusicController { @PostMapping("/{id}/play") public Result playMusic(@PathVariable Long id) { MusicDTO musicDTO = musicService.playMusic(id); + if (musicDTO != null) { + musicService.updateHotMusicCache(); + } return Result.success(musicDTO); } @@ -242,6 +247,7 @@ public class MusicController { // 获取当前登录用户ID Long userId = StpUtil.getLoginIdAsLong(); boolean isLiked = musicService.toggleLikeMusic(id, userId); + musicService.updateHotMusicCache(); String message = isLiked ? "点赞成功" : "取消点赞成功"; return Result.success(message, isLiked); } diff --git a/music-houduan/src/main/java/com/test/musichouduan/repository/MusicRepository.java b/music-houduan/src/main/java/com/test/musichouduan/repository/MusicRepository.java index be68b15..32bb0f1 100644 --- a/music-houduan/src/main/java/com/test/musichouduan/repository/MusicRepository.java +++ b/music-houduan/src/main/java/com/test/musichouduan/repository/MusicRepository.java @@ -78,7 +78,7 @@ public interface MusicRepository extends JpaRepository, JpaSpecific * @param pageable 分页 * @return 音乐列表 */ - @Query("SELECT m FROM Music m ORDER BY m.createTime DESC") + @Query("SELECT m FROM Music m ORDER BY m.createTime DESC, m.id DESC") List findLatest(Pageable pageable); /** @@ -87,7 +87,7 @@ public interface MusicRepository extends JpaRepository, JpaSpecific * @param pageable 分页 * @return 音乐列表 */ - @Query("SELECT m FROM Music m ORDER BY m.playCount DESC") + @Query("SELECT m FROM Music m ORDER BY m.playCount DESC, m.createTime DESC, m.id DESC") List findMostPlayed(Pageable pageable); /** diff --git a/music-houduan/src/main/java/com/test/musichouduan/service/impl/CommentServiceImpl.java b/music-houduan/src/main/java/com/test/musichouduan/service/impl/CommentServiceImpl.java index a19ab52..fd2cbd2 100644 --- a/music-houduan/src/main/java/com/test/musichouduan/service/impl/CommentServiceImpl.java +++ b/music-houduan/src/main/java/com/test/musichouduan/service/impl/CommentServiceImpl.java @@ -136,33 +136,7 @@ public class CommentServiceImpl implements CommentService { Comment savedComment = commentRepository.save(comment); - // 如果评论的是音乐 (targetType=1, 这个值需要与前端约定好) - // 通常可以定义一个枚举或常量类来管理这些类型值 - if (savedComment.getTargetType() == 1) { - musicRepository.findById(savedComment.getTargetId()).ifPresent(music -> { - // 首先判断类型是否等于1 - if (savedComment.getTargetType() == 1) { - // 查询评论表,使用COUNT(id)统计该音乐的评论数量 - long commentCount = commentRepository.countByTargetIdAndTargetType(savedComment.getTargetId(), 1); - // 设置到音乐中 - music.setCommentCount((int) commentCount); - // 保存音乐实体 - musicRepository.save(music); - } - }); - } else if (savedComment.getTargetType() == 2) { // 2 代表歌单 - playlistRepository.findById(savedComment.getTargetId()).ifPresent(playlist -> { - long commentCount = commentRepository.countByTargetIdAndTargetType(savedComment.getTargetId(), 2); - playlist.setCommentCount((int) commentCount); - playlistRepository.save(playlist); - }); - } else if (savedComment.getTargetType() == 3) { // 3 代表歌手 - singerRepository.findById(savedComment.getTargetId()).ifPresent(singer -> { - long commentCount = commentRepository.countByTargetIdAndTargetType(savedComment.getTargetId(), 3); - singer.setCommentCount((int) commentCount); - singerRepository.save(singer); - }); - } + updateTargetCommentCount(savedComment.getTargetType(), savedComment.getTargetId()); // 将新评论转换为DTO CommentDTO savedCommentDTO = convertToDTO(savedComment); @@ -194,27 +168,6 @@ public class CommentServiceImpl implements CommentService { // 删除评论 commentRepository.delete(comment); - // 更新相关实体的评论数 - if (comment.getTargetType() == 1) { - musicRepository.findById(comment.getTargetId()).ifPresent(music -> { - long commentCount = commentRepository.countByTargetIdAndTargetType(comment.getTargetId(), 1); - music.setCommentCount((int) commentCount); - musicRepository.save(music); - }); - } else if (comment.getTargetType() == 2) { - playlistRepository.findById(comment.getTargetId()).ifPresent(playlist -> { - long commentCount = commentRepository.countByTargetIdAndTargetType(comment.getTargetId(), 2); - playlist.setCommentCount((int) commentCount); - playlistRepository.save(playlist); - }); - } else if (comment.getTargetType() == 3) { - singerRepository.findById(comment.getTargetId()).ifPresent(singer -> { - long commentCount = commentRepository.countByTargetIdAndTargetType(comment.getTargetId(), 3); - singer.setCommentCount((int) commentCount); - singerRepository.save(singer); - }); - } - // 如果是父评论,同时删除子评论 if (comment.getParentId() == null) { List children = commentRepository.findByParentIdOrderByCreateTimeAsc(id); @@ -223,6 +176,9 @@ public class CommentServiceImpl implements CommentService { } } + // 删除完成后再同步评论数,避免父评论删除后子评论残留导致统计偏大 + updateTargetCommentCount(comment.getTargetType(), comment.getTargetId()); + // 通过 WebSocket 广播删除事件 String destination = String.format("/topic/comments/%d/%d", comment.getTargetType(), comment.getTargetId()); Map deleteEvent = new HashMap<>(); @@ -234,6 +190,31 @@ public class CommentServiceImpl implements CommentService { return true; } + private void updateTargetCommentCount(Integer targetType, Long targetId) { + if (targetType == null || targetId == null) { + return; + } + + long commentCount = commentRepository.countByTargetIdAndTargetType(targetId, targetType); + + if (targetType == 1) { + musicRepository.findById(targetId).ifPresent(music -> { + music.setCommentCount((int) commentCount); + musicRepository.save(music); + }); + } else if (targetType == 2) { + playlistRepository.findById(targetId).ifPresent(playlist -> { + playlist.setCommentCount((int) commentCount); + playlistRepository.save(playlist); + }); + } else if (targetType == 3) { + singerRepository.findById(targetId).ifPresent(singer -> { + singer.setCommentCount((int) commentCount); + singerRepository.save(singer); + }); + } + } + @Override public PageResult getUserCommentList(Long userId, Integer page, Integer size) { // 获取用户 diff --git a/music-houduan/src/main/java/com/test/musichouduan/service/impl/MusicServiceImpl.java b/music-houduan/src/main/java/com/test/musichouduan/service/impl/MusicServiceImpl.java index cb9f4f6..b126784 100644 --- a/music-houduan/src/main/java/com/test/musichouduan/service/impl/MusicServiceImpl.java +++ b/music-houduan/src/main/java/com/test/musichouduan/service/impl/MusicServiceImpl.java @@ -243,6 +243,14 @@ public class MusicServiceImpl implements MusicService { public CompletableFuture> updateHotMusicCache() { // 使用 JOIN FETCH 一次性加载所有音乐及其关联的歌手信息 List allMusic = musicRepository.findAllWithSingers(); + Comparator hotMusicComparator = Comparator + .comparing((Music music) -> music.getHotScore() != null ? music.getHotScore() : 0D, Comparator.reverseOrder()) + .thenComparing(music -> music.getPlayCount() != null ? music.getPlayCount() : 0L, Comparator.reverseOrder()) + .thenComparing(music -> music.getCollectCount() != null ? music.getCollectCount() : 0, Comparator.reverseOrder()) + .thenComparing(music -> music.getLikeCount() != null ? music.getLikeCount() : 0, Comparator.reverseOrder()) + .thenComparing(music -> music.getCommentCount() != null ? music.getCommentCount() : 0, Comparator.reverseOrder()) + .thenComparing(Music::getCreateTime, Comparator.reverseOrder()) + .thenComparing(Music::getId, Comparator.reverseOrder()); // 使用并行流来加速计算过程 allMusic.parallelStream().forEach(music -> { @@ -252,7 +260,7 @@ public class MusicServiceImpl implements MusicService { // 对所有音乐按计算出的热度分进行降序排序 List sortedHotMusic = allMusic.stream() - .sorted(Comparator.comparing(Music::getHotScore).reversed()) + .sorted(hotMusicComparator) .limit(HOT_MUSIC_LIMIT) // 取热度最高的前50 .map(this::convertToDTO) .collect(Collectors.toList()); diff --git a/music-houduan/src/main/java/com/test/musichouduan/util/HotScoreUtil.java b/music-houduan/src/main/java/com/test/musichouduan/util/HotScoreUtil.java index 3b1c3e5..f0ed38c 100644 --- a/music-houduan/src/main/java/com/test/musichouduan/util/HotScoreUtil.java +++ b/music-houduan/src/main/java/com/test/musichouduan/util/HotScoreUtil.java @@ -9,6 +9,8 @@ import java.time.LocalDateTime; */ public class HotScoreUtil { + // 播放量采用对数缩放,避免高播放量对热门榜产生碾压 + private static final double PLAY_WEIGHT = 1.2; // 定义各种交互的权重 private static final double LIKE_WEIGHT = 1.0; private static final double COLLECT_WEIGHT = 2.0; @@ -27,17 +29,24 @@ public class HotScoreUtil { public static double calculateHotScore(Music music) { // 1. 计算交互权重和 // 如果交互数据为 null,则默认为 0 + long playCount = music.getPlayCount() != null ? music.getPlayCount() : 0; long likeCount = music.getLikeCount() != null ? music.getLikeCount() : 0; long collectCount = music.getCollectCount() != null ? music.getCollectCount() : 0; long commentCount = music.getCommentCount() != null ? music.getCommentCount() : 0; - double interactionScore = (likeCount * LIKE_WEIGHT) + + double playScore = Math.log1p(playCount) * PLAY_WEIGHT; + + double interactionScore = playScore + + (likeCount * LIKE_WEIGHT) + (collectCount * COLLECT_WEIGHT) + (commentCount * COMMENT_WEIGHT); // 2. 计算时间衰减因子 // (当前时间 - 创建时间)的小时数 LocalDateTime createTime = music.getCreateTime(); + if (createTime == null) { + createTime = LocalDateTime.now(); + } LocalDateTime now = LocalDateTime.now(); long hoursElapsed = Duration.between(createTime, now).toHours();