package com.vibevault.service; import com.vibevault.dto.PlaylistDTO; import com.vibevault.dto.SongCreateDTO; import com.vibevault.dto.SongDTO; import com.vibevault.exception.ResourceNotFoundException; import com.vibevault.exception.UnauthorizedException; import com.vibevault.model.Playlist; import com.vibevault.model.Song; import com.vibevault.model.User; import com.vibevault.repository.PlaylistRepository; import com.vibevault.repository.UserRepository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; /** * 歌单服务实现 * * 需要实现: * - 所有 PlaylistService 接口中定义的方法 * - 将实体转换为 DTO 返回给调用者 * - 资源不存在时抛出 ResourceNotFoundException * - [Challenge] 检查用户是否有权限操作歌单(所有权检查) */ @Service public class PlaylistServiceImpl implements PlaylistService { private final PlaylistRepository playlistRepository; private final UserRepository userRepository; public PlaylistServiceImpl(PlaylistRepository playlistRepository, UserRepository userRepository) { this.playlistRepository = playlistRepository; this.userRepository = userRepository; } @Override public List getAllPlaylists() { return playlistRepository.findAll() .stream() .map(this::toDTO) .toList(); } @Override public PlaylistDTO getPlaylistById(Long id) { Playlist playlist = playlistRepository.findById(id) .orElseThrow(() -> new ResourceNotFoundException("Playlist not found with id: " + id)); return toDTO(playlist); } @Override @Transactional public PlaylistDTO createPlaylist(String name, String ownerUsername) { User owner = userRepository.findByUsername(ownerUsername) .orElseThrow(() -> new ResourceNotFoundException("User not found: " + ownerUsername)); Playlist playlist = new Playlist(name, owner); Playlist savedPlaylist = playlistRepository.save(playlist); return toDTO(savedPlaylist); } @Override @Transactional public PlaylistDTO addSongToPlaylist(Long playlistId, SongCreateDTO song, String username) { Playlist playlist = playlistRepository.findById(playlistId) .orElseThrow(() -> new ResourceNotFoundException("Playlist not found with id: " + playlistId)); // [Challenge] 检查用户是否有权限操作此歌单 checkPermission(playlist, username); Song newSong = new Song(song.title(), song.artist(), song.durationInSeconds()); playlist.addSong(newSong); Playlist updatedPlaylist = playlistRepository.save(playlist); return toDTO(updatedPlaylist); } @Override @Transactional public void removeSongFromPlaylist(Long playlistId, Long songId, String username) { Playlist playlist = playlistRepository.findById(playlistId) .orElseThrow(() -> new ResourceNotFoundException("Playlist not found with id: " + playlistId)); // [Challenge] 检查用户是否有权限操作此歌单 checkPermission(playlist, username); Song song = playlist.getSongs().stream() .filter(s -> s.getId().equals(songId)) .findFirst() .orElseThrow(() -> new ResourceNotFoundException("Song not found with id: " + songId)); playlist.removeSong(song); playlistRepository.save(playlist); } @Override @Transactional public void deletePlaylist(Long playlistId, String username) { Playlist playlist = playlistRepository.findById(playlistId) .orElseThrow(() -> new ResourceNotFoundException("Playlist not found with id: " + playlistId)); // [Challenge] 检查用户是否有权限操作此歌单 checkPermission(playlist, username); playlistRepository.delete(playlist); } // ========== Advanced 方法 ========== @Override @Transactional(readOnly = true) public List searchPlaylists(String keyword) { return playlistRepository.findByNameContainingIgnoreCase(keyword) .stream() .map(this::toDTO) .toList(); } @Override @Transactional public PlaylistDTO copyPlaylist(Long playlistId, String newName, String username) { // 获取要复制的歌单 Playlist originalPlaylist = playlistRepository.findById(playlistId) .orElseThrow(() -> new ResourceNotFoundException("Playlist not found with id: " + playlistId)); // 获取当前用户(新歌单的所有者) User currentUser = userRepository.findByUsername(username) .orElseThrow(() -> new ResourceNotFoundException("User not found: " + username)); // 创建新歌单 Playlist copiedPlaylist = new Playlist(newName, currentUser); // 复制原歌单中的所有歌曲 originalPlaylist.getSongs().forEach(originalSong -> { Song copiedSong = new Song( originalSong.getTitle(), originalSong.getArtist(), originalSong.getDurationInSeconds() ); copiedPlaylist.addSong(copiedSong); }); // 保存新歌单 Playlist savedPlaylist = playlistRepository.save(copiedPlaylist); return toDTO(savedPlaylist); } // ========== 辅助方法 ========== /** * 将 Playlist 实体转换为 DTO */ private PlaylistDTO toDTO(Playlist playlist) { List songDTOs = playlist.getSongs() .stream() .map(this::toSongDTO) .toList(); return new PlaylistDTO( playlist.getId(), playlist.getName(), playlist.getOwner().getUsername(), songDTOs ); } /** * 将 Song 实体转换为 DTO */ private SongDTO toSongDTO(Song song) { return new SongDTO( song.getId(), song.getTitle(), song.getArtist(), song.getDurationInSeconds() ); } /** * [Challenge] 检查用户是否有权限操作指定歌单 * 规则:歌单所有者或管理员可以操作 */ private void checkPermission(Playlist playlist, String username) { User currentUser = userRepository.findByUsername(username) .orElseThrow(() -> new ResourceNotFoundException("User not found: " + username)); // 检查是否是歌单所有者或管理员 if (!playlist.getOwner().getUsername().equals(username) && !currentUser.getRole().equals("ROLE_ADMIN")) { throw new UnauthorizedException("You don't have permission to modify this playlist"); } } }