feat(music): 优化音乐数据加载和展示
- 优化音乐时长获取日志,增加文件名信息 - 添加歌手头像字段并处理相关逻辑 - 修改相关音乐推荐为热门音乐推荐- 优化相关音乐封面展示逻辑 - 使用 JOIN FETCH 优化音乐数据加载性能
This commit is contained in:
@@ -49,6 +49,7 @@ public class HotMusicController {
|
|||||||
* 主要用于测试或紧急情况。
|
* 主要用于测试或紧急情况。
|
||||||
* @return 响应结果
|
* @return 响应结果
|
||||||
*/
|
*/
|
||||||
|
@SaIgnore
|
||||||
@GetMapping("/refresh-cache")
|
@GetMapping("/refresh-cache")
|
||||||
public Result<String> refreshHotMusicCache() {
|
public Result<String> refreshHotMusicCache() {
|
||||||
// 异步执行,立即返回,不阻塞当前请求
|
// 异步执行,立即返回,不阻塞当前请求
|
||||||
|
|||||||
@@ -71,6 +71,11 @@ public class MusicDTO {
|
|||||||
*/
|
*/
|
||||||
private String singer;
|
private String singer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 歌手头像
|
||||||
|
*/
|
||||||
|
private String singerAvatar;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 音乐类型列表
|
* 音乐类型列表
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -113,4 +113,12 @@ public interface MusicRepository extends JpaRepository<Music, Long>, JpaSpecific
|
|||||||
*/
|
*/
|
||||||
@Query("SELECT t.name, COUNT(m) FROM Music m JOIN m.types t GROUP BY t.name ORDER BY COUNT(m) DESC")
|
@Query("SELECT t.name, COUNT(m) FROM Music m JOIN m.types t GROUP BY t.name ORDER BY COUNT(m) DESC")
|
||||||
List<Object[]> countByMusicType();
|
List<Object[]> countByMusicType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询所有音乐,并立即加载关联的歌手信息
|
||||||
|
*
|
||||||
|
* @return 音乐列表
|
||||||
|
*/
|
||||||
|
@Query("SELECT DISTINCT m FROM Music m LEFT JOIN FETCH m.singers")
|
||||||
|
List<Music> findAllWithSingers();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ public class FileServiceImpl implements FileService {
|
|||||||
int duration = audioFile.getAudioHeader().getTrackLength();
|
int duration = audioFile.getAudioHeader().getTrackLength();
|
||||||
return duration;
|
return duration;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("获取音乐时长失败", e);
|
logger.error("获取音乐 '{}' 时长失败", file.getOriginalFilename(), e);
|
||||||
return 0;
|
return 0;
|
||||||
} finally {
|
} finally {
|
||||||
if (tempFile != null) {
|
if (tempFile != null) {
|
||||||
|
|||||||
@@ -240,18 +240,8 @@ public class MusicServiceImpl implements MusicService {
|
|||||||
@Transactional(readOnly = true) // 使用只读事务,提高查询性能
|
@Transactional(readOnly = true) // 使用只读事务,提高查询性能
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<List<MusicDTO>> updateHotMusicCache() {
|
public CompletableFuture<List<MusicDTO>> updateHotMusicCache() {
|
||||||
// 为了处理大数据量,我们使用分页查询来代替一次性加载所有数据
|
// 使用 JOIN FETCH 一次性加载所有音乐及其关联的歌手信息
|
||||||
List<Music> allMusic = new ArrayList<>();
|
List<Music> allMusic = musicRepository.findAllWithSingers();
|
||||||
Page<Music> musicPage;
|
|
||||||
int pageNum = 0;
|
|
||||||
int pageSize = 500; // 每次查询500条
|
|
||||||
|
|
||||||
do {
|
|
||||||
Pageable pageable = PageRequest.of(pageNum, pageSize);
|
|
||||||
musicPage = musicRepository.findAll(pageable);
|
|
||||||
allMusic.addAll(musicPage.getContent());
|
|
||||||
pageNum++;
|
|
||||||
} while (musicPage.hasNext());
|
|
||||||
|
|
||||||
// 使用并行流来加速计算过程
|
// 使用并行流来加速计算过程
|
||||||
allMusic.parallelStream().forEach(music -> {
|
allMusic.parallelStream().forEach(music -> {
|
||||||
@@ -659,6 +649,11 @@ public class MusicServiceImpl implements MusicService {
|
|||||||
List<String> singerNames = music.getSingers().stream().map(Singer::getName).collect(Collectors.toList());
|
List<String> singerNames = music.getSingers().stream().map(Singer::getName).collect(Collectors.toList());
|
||||||
musicDTO.setSingerNames(singerNames);
|
musicDTO.setSingerNames(singerNames);
|
||||||
musicDTO.setSinger(String.join("、", singerNames));
|
musicDTO.setSinger(String.join("、", singerNames));
|
||||||
|
|
||||||
|
// 设置歌手头像
|
||||||
|
if (music.getSingers().size() == 1) {
|
||||||
|
musicDTO.setSingerAvatar(music.getSingers().iterator().next().getAvatar());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果传入了用户ID,则查询该用户的收藏和点赞状态
|
// 如果传入了用户ID,则查询该用户的收藏和点赞状态
|
||||||
|
|||||||
@@ -73,7 +73,19 @@ export function getHotMusic(limit = 10) {
|
|||||||
// 更新:调用新的热门音乐接口
|
// 更新:调用新的热门音乐接口
|
||||||
return api.get('/hot-music/list', {
|
return api.get('/hot-music/list', {
|
||||||
params: { limit }
|
params: { limit }
|
||||||
})
|
}).then(res => {
|
||||||
|
if (res.code === 200 && res.data && Array.isArray(res.data)) {
|
||||||
|
res.data.forEach(item => {
|
||||||
|
if (item.cover) {
|
||||||
|
item.cover = processMusicUrl(item.cover);
|
||||||
|
}
|
||||||
|
if (item.singerAvatar) {
|
||||||
|
item.singerAvatar = processMusicUrl(item.singerAvatar);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -170,7 +170,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="relatedMusic.length > 0 && activeTab === 'lyric'" class="related-music">
|
<div v-if="relatedMusic.length > 0 && activeTab === 'lyric'" class="related-music">
|
||||||
<h2>相关推荐</h2>
|
<h2>热门推荐</h2>
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col
|
<el-col
|
||||||
v-for="item in relatedMusic"
|
v-for="item in relatedMusic"
|
||||||
@@ -182,7 +182,7 @@
|
|||||||
>
|
>
|
||||||
<div class="music-card" @click="goToMusic(item.id)">
|
<div class="music-card" @click="goToMusic(item.id)">
|
||||||
<div class="card-cover">
|
<div class="card-cover">
|
||||||
<img :src="item.cover || '/default-cover.jpg'" alt="封面">
|
<img :src="item.cover || item.singerAvatar || '/default-cover.jpg'" alt="封面">
|
||||||
<div class="play-icon" @click.stop="playRelatedMusic(item)">
|
<div class="play-icon" @click.stop="playRelatedMusic(item)">
|
||||||
<el-icon><VideoPlay /></el-icon>
|
<el-icon><VideoPlay /></el-icon>
|
||||||
</div>
|
</div>
|
||||||
@@ -312,6 +312,7 @@ const fetchRelatedMusic = async () => {
|
|||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
// 过滤掉当前音乐
|
// 过滤掉当前音乐
|
||||||
relatedMusic.value = (res.data || []).filter(item => item.id !== music.value?.id)
|
relatedMusic.value = (res.data || []).filter(item => item.id !== music.value?.id)
|
||||||
|
console.log('相关音乐:', relatedMusic.value)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取相关音乐失败:', error)
|
console.error('获取相关音乐失败:', error)
|
||||||
|
|||||||
Reference in New Issue
Block a user