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 0e4252e..f44098d 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 @@ -4,6 +4,7 @@ import cn.dev33.satoken.annotation.SaCheckLogin; import cn.dev33.satoken.annotation.SaCheckRole; import cn.dev33.satoken.stp.StpUtil; import com.test.musichouduan.dto.MusicDTO; +import com.test.musichouduan.dto.MusicStatusDTO; import com.test.musichouduan.dto.PageResult; import com.test.musichouduan.dto.Result; import com.test.musichouduan.service.MusicService; @@ -234,4 +235,17 @@ public class MusicController { String message = isLiked ? "点赞成功" : "取消点赞成功"; return Result.success(message, isLiked); } + + /** + * 获取音乐的收藏和点赞状态 + * + * @param id 音乐ID + * @return 响应结果 + */ + @GetMapping("/{id}/status") + public Result getMusicStatus(@PathVariable Long id) { + Long userId = StpUtil.getLoginIdDefaultNull() != null ? StpUtil.getLoginIdAsLong() : null; + MusicStatusDTO status = musicService.getMusicStatus(id, userId); + return Result.success(status); + } } diff --git a/music-houduan/src/main/java/com/test/musichouduan/entity/UserLikeMusic.java b/music-houduan/src/main/java/com/test/musichouduan/entity/UserLikeMusic.java index 12fe23b..aac0a06 100644 --- a/music-houduan/src/main/java/com/test/musichouduan/entity/UserLikeMusic.java +++ b/music-houduan/src/main/java/com/test/musichouduan/entity/UserLikeMusic.java @@ -5,7 +5,9 @@ import lombok.Data; import org.hibernate.annotations.CreationTimestamp; import java.time.LocalDateTime; - +/** + * 用户点赞音乐实体类 + */ @Data @Entity @Table(name = "user_like_music", uniqueConstraints = { diff --git a/music-houduan/src/main/java/com/test/musichouduan/service/MusicService.java b/music-houduan/src/main/java/com/test/musichouduan/service/MusicService.java index 1d9bdca..e42312c 100644 --- a/music-houduan/src/main/java/com/test/musichouduan/service/MusicService.java +++ b/music-houduan/src/main/java/com/test/musichouduan/service/MusicService.java @@ -1,6 +1,7 @@ package com.test.musichouduan.service; import com.test.musichouduan.dto.MusicDTO; +import com.test.musichouduan.dto.MusicStatusDTO; import com.test.musichouduan.dto.PageResult; import com.test.musichouduan.entity.Music; import org.springframework.web.multipart.MultipartFile; @@ -151,4 +152,13 @@ public interface MusicService { * @return 是否点赞成功 (true: 点赞, false: 取消点赞) */ boolean toggleLikeMusic(Long musicId, Long userId); + + /** + * 获取音乐的收藏和点赞状态 + * + * @param musicId 音乐ID + * @param userId 用户ID + * @return 音乐状态DTO + */ + MusicStatusDTO getMusicStatus(Long musicId, Long userId); } 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 cb098d6..e30547e 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 @@ -2,6 +2,7 @@ package com.test.musichouduan.service.impl; import cn.dev33.satoken.stp.StpUtil; import com.test.musichouduan.dto.MusicDTO; +import com.test.musichouduan.dto.MusicStatusDTO; import com.test.musichouduan.dto.PageResult; import com.test.musichouduan.entity.Music; import com.test.musichouduan.entity.MusicType; @@ -599,6 +600,17 @@ public class MusicServiceImpl implements MusicService { } } + @Override + public MusicStatusDTO getMusicStatus(Long musicId, Long userId) { + if (userId == null) { + // 用户未登录,返回默认状态 + return new MusicStatusDTO(false, false); + } + boolean isCollected = userCollectMusicRepository.existsByUserIdAndMusicId(userId, musicId); + boolean isLiked = userLikeMusicRepository.existsByUserIdAndMusicId(userId, musicId); + return new MusicStatusDTO(isCollected, isLiked); + } + /** * 将实体转换为DTO * diff --git a/music-qianduan/src/api/music.js b/music-qianduan/src/api/music.js index 165872d..69569cc 100644 --- a/music-qianduan/src/api/music.js +++ b/music-qianduan/src/api/music.js @@ -44,7 +44,12 @@ export function getMusicList(page = 1, size = 10, keyword = '', typeId = null) { * @returns {Promise} */ export function getMusicById(id) { - return api.get(`/music/${id}`).then(res => { + return api.get(`/music/${id}`, { + params: { + // 添加时间戳以防止缓存 + _t: new Date().getTime() + } + }).then(res => { if (res.code === 200 && res.data) { // 处理音乐URL if (res.data.url) { @@ -125,6 +130,15 @@ export function toggleLikeMusic(id) { return api.post(`/music/${id}/like`) } +/** + * 获取音乐的收藏和点赞状态 + * @param {number} id 音乐ID + * @returns {Promise} + */ +export function getMusicStatus(id) { + return api.get(`/music/${id}/status`); +} + /** * 获取收藏的音乐列表 * @param {number} page 页码 diff --git a/music-qianduan/src/components/MusicPlayer.vue b/music-qianduan/src/components/MusicPlayer.vue index 728ecee..945db0e 100644 --- a/music-qianduan/src/components/MusicPlayer.vue +++ b/music-qianduan/src/components/MusicPlayer.vue @@ -286,7 +286,7 @@ import { ref, computed, watch, nextTick } from 'vue' import { useRouter } from 'vue-router' import { usePlayerStore } from '../stores/player' import { useUserStore } from '../stores/user' -import { processMusicUrl, toggleCollectMusic } from '../api/music' +import { processMusicUrl, toggleCollectMusic, getMusicStatus } from '../api/music' import { ElMessage } from 'element-plus' const router = useRouter() @@ -490,9 +490,9 @@ const toggleCollect = async () => { try { const res = await toggleCollectMusic(currentMusic.value.id) if (res.code === 200) { - // 更新收藏状态 - currentMusic.value.isCollected = !currentMusic.value.isCollected - ElMessage.success(currentMusic.value.isCollected ? '收藏成功' : '已取消收藏') + // 直接使用后端返回的最新状态 + playerStore.updateCurrentMusicStatus({ collected: res.data }); + ElMessage.success(res.message || (res.data ? '收藏成功' : '已取消收藏')) } else { ElMessage.error(res.message || '操作失败') } @@ -637,9 +637,9 @@ const scrollToActiveLyric = async () => { } } -// 监听当前音乐变化,处理音乐URL +// 监听当前音乐变化,处理音乐URL和状态 watch(currentMusic, (newMusic) => { - if (newMusic && newMusic.url) { + if (newMusic && newMusic.id) { // 处理音乐URL const processedUrl = processMusicUrl(newMusic.url) if (processedUrl !== newMusic.url) { @@ -654,9 +654,21 @@ watch(currentMusic, (newMusic) => { } } - // 确保收藏状态字段存在 - if (newMusic.isCollected === undefined) { - newMusic.isCollected = newMusic.collected || false + // 主动获取最新的收藏和点赞状态 + if (userStore.isLoggedIn) { + getMusicStatus(newMusic.id).then(res => { + if (res.code === 200 && res.data) { + playerStore.updateCurrentMusicStatus({ + collected: res.data.collected, + liked: res.data.liked + }); + } + }).catch(err => { + console.error("获取播放器音乐状态失败:", err); + }); + } else { + // 未登录则重置状态 + playerStore.updateCurrentMusicStatus({ collected: false, liked: false }); } // 重置歌词索引和元素引用 diff --git a/music-qianduan/src/views/Music.vue b/music-qianduan/src/views/Music.vue index 49c88bd..bd1f816 100644 --- a/music-qianduan/src/views/Music.vue +++ b/music-qianduan/src/views/Music.vue @@ -2,12 +2,14 @@ import { ref, onMounted } from 'vue' import { useRouter } from 'vue-router' import { Search } from '@element-plus/icons-vue' -import { getMusicList, getHotMusic, getLatestMusic, processMusicUrl } from '../api/music' +import { getMusicList, getHotMusic, getLatestMusic, processMusicUrl, getMusicStatus } from '../api/music' import { getMusicTypeList } from '../api/musicType' import MusicList from '../components/MusicList.vue' import { ElMessage } from 'element-plus' +import { useUserStore } from '../stores/user' const router = useRouter() +const userStore = useUserStore() // 音乐列表数据 const musicList = ref([]) @@ -43,13 +45,8 @@ const fetchMusicList = async (params = {}) => { console.log('音乐列表数据:', res.data) - // 处理收藏状态 + // 确保音乐URL和封面URL正确处理 musicList.value.forEach(music => { - if (music.collected === undefined) { - music.collected = false - } - - // 确保音乐URL和封面URL正确处理 if (music.url) { music.url = processMusicUrl(music.url) } @@ -57,6 +54,9 @@ const fetchMusicList = async (params = {}) => { music.cover = processMusicUrl(music.cover) } }) + + // 更新音乐状态 + updateMusicStatuses(musicList.value) } else { ElMessage.error(res.message || '获取音乐列表失败') } @@ -98,6 +98,27 @@ const handleCollectChange = (music) => { console.log('收藏状态变化:', music) } +// 更新音乐状态 +const updateMusicStatuses = async (list) => { + if (!userStore.isLoggedIn || !list || list.length === 0) { + return + } + + // 为列表中的每首歌曲获取状态 + const promises = list.map(music => getMusicStatus(music.id)) + try { + const results = await Promise.all(promises) + results.forEach((res, index) => { + if (res.code === 200 && res.data) { + list[index].collected = res.data.collected + list[index].liked = res.data.liked + } + }) + } catch (error) { + console.error('批量获取音乐状态失败:', error) + } +} + // 获取热门音乐 const fetchHotMusic = async (params = {}) => { loadingHot.value = true @@ -108,13 +129,15 @@ const fetchHotMusic = async (params = {}) => { const allHotMusic = res.data || [] total.value = allHotMusic.length // 总数是50 - // 处理URL和收藏状态 + // 处理URL allHotMusic.forEach(music => { if (music.url) music.url = processMusicUrl(music.url) if (music.cover) music.cover = processMusicUrl(music.cover) - if (music.collected === undefined) music.collected = false }) + // 更新状态 + updateMusicStatuses(allHotMusic) + // 根据当前页码和每页数量进行前端分页 const page = params.page || currentPage.value const size = params.size || pageSize.value @@ -149,10 +172,10 @@ const fetchLatestMusic = async () => { if (music.cover) { music.cover = processMusicUrl(music.cover) } - if (music.collected === undefined) { - music.collected = false - } }) + + // 更新状态 + updateMusicStatuses(latestMusicList.value) } else { ElMessage.error(res.message || '获取最新音乐失败') } diff --git a/music-qianduan/src/views/MusicDetail.vue b/music-qianduan/src/views/MusicDetail.vue index fa07af6..e31d89d 100644 --- a/music-qianduan/src/views/MusicDetail.vue +++ b/music-qianduan/src/views/MusicDetail.vue @@ -78,7 +78,7 @@ @click="toggleLike" > - {{ music.liked ? '已点赞' : '点赞' }} ({{ music.likeCount || 0 }}) + {{ music.liked ? '已点赞' : '点赞' }} { if (res.code === 200) { if (res.data) { music.value = res.data + // 获取音乐状态 + fetchMusicStatus() } else { // 如果音乐不存在,显示提示并返回音乐列表页 ElMessage.warning('音乐不存在或已被删除') @@ -264,6 +266,22 @@ const fetchMusicDetail = async () => { } } +// 获取音乐状态 +const fetchMusicStatus = async () => { + if (!music.value || !userStore.isLoggedIn) { + return + } + try { + const res = await getMusicStatus(music.value.id) + if (res.code === 200 && res.data) { + music.value.collected = res.data.collected + music.value.liked = res.data.liked + } + } catch (error) { + console.error('获取音乐状态失败:', error) + } +} + // 获取相关音乐 const fetchRelatedMusic = async () => { try { diff --git a/music-qianduan/src/views/PlaylistDetail.vue b/music-qianduan/src/views/PlaylistDetail.vue index ea59adb..1cb4241 100644 --- a/music-qianduan/src/views/PlaylistDetail.vue +++ b/music-qianduan/src/views/PlaylistDetail.vue @@ -5,6 +5,7 @@ import { usePlayerStore } from '../stores/player' import { useUserStore } from '../stores/user' import { ElMessage, ElMessageBox } from 'element-plus' import { getPlaylistById, toggleCollectPlaylist, deletePlaylist as apiDeletePlaylist } from '../api/playlist' +import { getMusicStatus } from '../api/music' import { getCommentList, addComment, deleteComment as apiDeleteComment } from '../api/comment' import { connect, subscribe, unsubscribe, disconnect } from '../utils/websocket' // 导入 WebSocket 工具 import { processImageUrl } from '../utils/imageUtils' @@ -67,6 +68,9 @@ const fetchPlaylistDetail = async () => { // 获取收藏状态 isCollected.value = playlist.value.collected === true + + // 更新歌单内歌曲的状态 + updateMusicStatuses(playlist.value.songs) } else { ElMessage.error(res.message || '获取歌单详情失败') } @@ -202,6 +206,27 @@ const cancelReply = () => { replyContent.value = '' } +// 更新音乐状态 +const updateMusicStatuses = async (list) => { + if (!userStore.isLoggedIn || !list || list.length === 0) { + return + } + + // 为列表中的每首歌曲获取状态 + const promises = list.map(music => getMusicStatus(music.id)) + try { + const results = await Promise.all(promises) + results.forEach((res, index) => { + if (res.code === 200 && res.data) { + list[index].collected = res.data.collected + list[index].liked = res.data.liked + } + }) + } catch (error) { + console.error('批量获取音乐状态失败:', error) + } +} + // 提交评论 const submitComment = async (isReply = false) => { if (!userStore.isLoggedIn) { diff --git a/music-qianduan/src/views/Search.vue b/music-qianduan/src/views/Search.vue index e07b27f..befd6bc 100644 --- a/music-qianduan/src/views/Search.vue +++ b/music-qianduan/src/views/Search.vue @@ -153,7 +153,7 @@ import { useRoute, useRouter } from 'vue-router' import { useUserStore } from '../stores/user' import { ElMessage } from 'element-plus' import { Search } from '@element-plus/icons-vue' -import { searchMusic, toggleCollectMusic } from '../api/music' +import { searchMusic, toggleCollectMusic, getMusicStatus } from '../api/music' import { searchPlaylist, toggleCollectPlaylist } from '../api/playlist' import { searchSinger, toggleCollectSinger } from '../api/singer' import { processImageUrl } from '../utils/imageUtils' @@ -222,6 +222,9 @@ const fetchMusicList = async () => { cover: processMusicUrl(item.cover), url: processMusicUrl(item.url) })) + + // 更新音乐状态 + updateMusicStatuses(musicList.value) } else { ElMessage.error(res.message || '搜索音乐失败') } @@ -448,6 +451,27 @@ const formatFans = (count) => { } } +// 更新音乐状态 +const updateMusicStatuses = async (list) => { + if (!userStore.isLoggedIn || !list || list.length === 0) { + return + } + + // 为列表中的每首歌曲获取状态 + const promises = list.map(music => getMusicStatus(music.id)) + try { + const results = await Promise.all(promises) + results.forEach((res, index) => { + if (res.code === 200 && res.data) { + list[index].collected = res.data.collected + list[index].liked = res.data.liked + } + }) + } catch (error) { + console.error('批量获取音乐状态失败:', error) + } +} + // 监听关键词变化 watch(() => route.query.keyword, (newKeyword) => { searchKeyword.value = newKeyword diff --git a/music-qianduan/src/views/SingerDetail.vue b/music-qianduan/src/views/SingerDetail.vue index 2fa5e36..44446e4 100644 --- a/music-qianduan/src/views/SingerDetail.vue +++ b/music-qianduan/src/views/SingerDetail.vue @@ -7,7 +7,7 @@ import { ElMessage, ElMessageBox } from 'element-plus' import { getSingerById, toggleCollectSinger } from '../api/singer' import { getCommentList, addComment, deleteComment as apiDeleteComment } from '../api/comment' import { processImageUrl } from '../utils/imageUtils' -import { processMusicUrl, getMusicBySinger } from '../api/music' +import { processMusicUrl, getMusicBySinger, getMusicStatus } from '../api/music' import { connect, subscribe, unsubscribe, disconnect } from '../utils/websocket' // 导入 WebSocket 工具 const route = useRoute() @@ -92,6 +92,9 @@ const fetchSingerMusic = async () => { cover: processMusicUrl(song.cover), url: processMusicUrl(song.url) })) + + // 更新歌曲状态 + updateMusicStatuses(songs.value) } else { ElMessage.error(res.message || '获取歌手音乐失败') } @@ -221,6 +224,27 @@ const cancelReply = () => { replyContent.value = '' } +// 更新音乐状态 +const updateMusicStatuses = async (list) => { + if (!userStore.isLoggedIn || !list || list.length === 0) { + return + } + + // 为列表中的每首歌曲获取状态 + const promises = list.map(music => getMusicStatus(music.id)) + try { + const results = await Promise.all(promises) + results.forEach((res, index) => { + if (res.code === 200 && res.data) { + list[index].collected = res.data.collected + list[index].liked = res.data.liked + } + }) + } catch (error) { + console.error('批量获取音乐状态失败:', error) + } +} + // 提交评论 const submitComment = async (isReply = false) => { if (!userStore.isLoggedIn) {