generated from Java-2025Fall/final-vibevault-template
完成作业
This commit is contained in:
parent
0115a4bf31
commit
d5091273cd
@ -1,8 +1,11 @@
|
||||
package com.vibevault.controller;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@ -14,6 +17,7 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.vibevault.dto.CopyPlaylistRequest;
|
||||
import com.vibevault.dto.PlaylistCreateDTO;
|
||||
import com.vibevault.dto.PlaylistDTO;
|
||||
import com.vibevault.dto.SongCreateDTO;
|
||||
@ -76,6 +80,52 @@ public class PlaylistController {
|
||||
return playlistService.addSongToPlaylist(id, songCreateDTO, username);
|
||||
}
|
||||
|
||||
// POST /api/playlists/{id}/songs/batch - 批量添加歌曲(需认证)
|
||||
@PostMapping("/{id}/songs/batch")
|
||||
@PreAuthorize("hasRole('USER')")
|
||||
public ResponseEntity<PlaylistDTO> addSongsToPlaylist(@PathVariable Long id, @RequestBody List<SongCreateDTO> songs, Principal principal) {
|
||||
PlaylistDTO updatedPlaylist = playlistService.addSongsToPlaylist(id, songs, principal.getName());
|
||||
return ResponseEntity.ok(updatedPlaylist);
|
||||
}
|
||||
|
||||
/**
|
||||
* [Advanced] 复制歌单
|
||||
*
|
||||
* @param id 要复制的歌单ID
|
||||
* @param request 复制请求(包含新歌单名称)
|
||||
* @param principal 当前用户
|
||||
* @return 新创建的歌单DTO
|
||||
*/
|
||||
@PostMapping("/{id}/copy")
|
||||
@PreAuthorize("hasRole('USER')")
|
||||
public ResponseEntity<PlaylistDTO> copyPlaylist(@PathVariable Long id, @RequestBody CopyPlaylistRequest request, Principal principal) {
|
||||
PlaylistDTO copiedPlaylist = playlistService.copyPlaylist(id, request.getNewName(), principal.getName());
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(copiedPlaylist);
|
||||
}
|
||||
|
||||
/**
|
||||
* [Advanced] 按条件筛选歌单
|
||||
*
|
||||
* @param ownerName 歌单拥有者名称(可选)
|
||||
* @param nameKeyword 歌单名称关键词(可选)
|
||||
* @param sortBy 排序字段(name或createdAt)
|
||||
* @param sortDirection 排序方向(asc或desc)
|
||||
* @param principal 当前用户
|
||||
* @return 筛选后的歌单列表
|
||||
*/
|
||||
@GetMapping("/filter")
|
||||
@PreAuthorize("hasRole('USER')")
|
||||
public ResponseEntity<List<PlaylistDTO>> filterPlaylists(
|
||||
@RequestParam(required = false) String ownerName,
|
||||
@RequestParam(required = false) String nameKeyword,
|
||||
@RequestParam(required = false, defaultValue = "name") String sortBy,
|
||||
@RequestParam(required = false, defaultValue = "asc") String sortDirection,
|
||||
Principal principal) {
|
||||
List<PlaylistDTO> filteredPlaylists = playlistService.filterPlaylists(
|
||||
principal.getName(), ownerName, nameKeyword, sortBy, sortDirection);
|
||||
return ResponseEntity.ok(filteredPlaylists);
|
||||
}
|
||||
|
||||
// DELETE /api/playlists/{playlistId}/songs/{songId} - 移除歌曲(需认证)
|
||||
@DeleteMapping("/{playlistId}/songs/{songId}")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
@ -98,11 +148,5 @@ public class PlaylistController {
|
||||
return playlistService.searchPlaylists(keyword);
|
||||
}
|
||||
|
||||
// [Advanced] POST /api/playlists/{id}/copy?newName=xxx - 复制歌单
|
||||
@PostMapping("/{id}/copy")
|
||||
@ResponseStatus(HttpStatus.CREATED)
|
||||
public PlaylistDTO copyPlaylist(@PathVariable Long id, @RequestParam String newName, Authentication authentication) {
|
||||
String username = authentication.getName();
|
||||
return playlistService.copyPlaylist(id, newName, username);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
16
src/main/java/com/vibevault/dto/CopyPlaylistRequest.java
Normal file
16
src/main/java/com/vibevault/dto/CopyPlaylistRequest.java
Normal file
@ -0,0 +1,16 @@
|
||||
package com.vibevault.dto;
|
||||
|
||||
/**
|
||||
* 复制歌单请求DTO
|
||||
*/
|
||||
public class CopyPlaylistRequest {
|
||||
private String newName;
|
||||
|
||||
public String getNewName() {
|
||||
return newName;
|
||||
}
|
||||
|
||||
public void setNewName(String newName) {
|
||||
this.newName = newName;
|
||||
}
|
||||
}
|
||||
41
src/main/java/com/vibevault/exception/ErrorResponse.java
Normal file
41
src/main/java/com/vibevault/exception/ErrorResponse.java
Normal file
@ -0,0 +1,41 @@
|
||||
package com.vibevault.exception;
|
||||
|
||||
/**
|
||||
* 统一错误响应类
|
||||
* 用于封装API错误响应的结构
|
||||
*/
|
||||
public class ErrorResponse {
|
||||
private int status;
|
||||
private String error;
|
||||
private String message;
|
||||
|
||||
public ErrorResponse(int status, String error, String message) {
|
||||
this.status = status;
|
||||
this.error = error;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public int getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(int status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public void setError(String error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
@ -6,10 +6,6 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 全局异常处理器
|
||||
*
|
||||
@ -28,14 +24,8 @@ public class GlobalExceptionHandler {
|
||||
* @return 包含错误信息的 ResponseEntity
|
||||
*/
|
||||
@ExceptionHandler(ResourceNotFoundException.class)
|
||||
public ResponseEntity<Map<String, Object>> handleResourceNotFoundException(ResourceNotFoundException ex) {
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("timestamp", LocalDateTime.now());
|
||||
body.put("status", HttpStatus.NOT_FOUND.value());
|
||||
body.put("error", "Not Found");
|
||||
body.put("message", ex.getMessage());
|
||||
|
||||
return new ResponseEntity<>(body, HttpStatus.NOT_FOUND);
|
||||
public ResponseEntity<ErrorResponse> handleResourceNotFoundException(ResourceNotFoundException ex) {
|
||||
return buildErrorResponse(HttpStatus.NOT_FOUND.value(), "Not Found", ex.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -44,14 +34,18 @@ public class GlobalExceptionHandler {
|
||||
* @return 包含错误信息的 ResponseEntity
|
||||
*/
|
||||
@ExceptionHandler(UnauthorizedAccessException.class)
|
||||
public ResponseEntity<Map<String, Object>> handleUnauthorizedAccessException(UnauthorizedAccessException ex) {
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("timestamp", LocalDateTime.now());
|
||||
body.put("status", HttpStatus.FORBIDDEN.value());
|
||||
body.put("error", "Forbidden");
|
||||
body.put("message", ex.getMessage());
|
||||
public ResponseEntity<ErrorResponse> handleUnauthorizedAccessException(UnauthorizedAccessException ex) {
|
||||
return buildErrorResponse(HttpStatus.FORBIDDEN.value(), "Forbidden", ex.getMessage());
|
||||
}
|
||||
|
||||
return new ResponseEntity<>(body, HttpStatus.FORBIDDEN);
|
||||
/**
|
||||
* 处理 UnauthorizedException 异常
|
||||
* @param ex 异常对象
|
||||
* @return 包含错误信息的 ResponseEntity
|
||||
*/
|
||||
@ExceptionHandler(UnauthorizedException.class)
|
||||
public ResponseEntity<ErrorResponse> handleUnauthorizedException(UnauthorizedException ex) {
|
||||
return buildErrorResponse(HttpStatus.FORBIDDEN.value(), "Forbidden", ex.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -60,14 +54,8 @@ public class GlobalExceptionHandler {
|
||||
* @return 包含错误信息的 ResponseEntity
|
||||
*/
|
||||
@ExceptionHandler(ResponseStatusException.class)
|
||||
public ResponseEntity<Map<String, Object>> handleResponseStatusException(ResponseStatusException ex) {
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("timestamp", LocalDateTime.now());
|
||||
body.put("status", ex.getStatusCode().value());
|
||||
body.put("error", ex.getStatusCode().toString());
|
||||
body.put("message", ex.getMessage());
|
||||
|
||||
return new ResponseEntity<>(body, ex.getStatusCode());
|
||||
public ResponseEntity<ErrorResponse> handleResponseStatusException(ResponseStatusException ex) {
|
||||
return buildErrorResponse(ex.getStatusCode().value(), ex.getStatusCode().toString(), ex.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -76,16 +64,19 @@ public class GlobalExceptionHandler {
|
||||
* @return 包含错误信息的 ResponseEntity
|
||||
*/
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<Map<String, Object>> handleGeneralException(Exception ex) {
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("timestamp", LocalDateTime.now());
|
||||
body.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value());
|
||||
body.put("error", "Internal Server Error");
|
||||
body.put("message", "An unexpected error occurred");
|
||||
public ResponseEntity<ErrorResponse> handleGeneralException(Exception ex) {
|
||||
return buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Internal Server Error", "An unexpected error occurred");
|
||||
}
|
||||
|
||||
// 在开发环境中可以添加异常堆栈信息
|
||||
// body.put("stackTrace", Arrays.toString(ex.getStackTrace()));
|
||||
|
||||
return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
/**
|
||||
* 构建统一的错误响应格式
|
||||
* @param status 响应状态码
|
||||
* @param error 错误类型
|
||||
* @param message 错误消息
|
||||
* @return 包含错误信息的 ResponseEntity
|
||||
*/
|
||||
private ResponseEntity<ErrorResponse> buildErrorResponse(int statusCode, String error, String message) {
|
||||
ErrorResponse errorResponse = new ErrorResponse(statusCode, error, message);
|
||||
return ResponseEntity.status(statusCode).body(errorResponse);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
package com.vibevault.model;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
@ -44,6 +47,10 @@ public class Playlist {
|
||||
@OneToMany(mappedBy = "playlist", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private List<Song> songs = new ArrayList<>();
|
||||
|
||||
@Column(nullable = false, updatable = false)
|
||||
@CreationTimestamp
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
public Playlist() {
|
||||
}
|
||||
|
||||
@ -76,6 +83,10 @@ public class Playlist {
|
||||
return Collections.unmodifiableList(songs);
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* 向歌单添加歌曲
|
||||
* 提示:需要维护双向关系
|
||||
|
||||
@ -1,6 +1,17 @@
|
||||
package com.vibevault.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
/**
|
||||
* 歌曲实体类
|
||||
@ -31,6 +42,10 @@ public class Song {
|
||||
@JoinColumn(name = "playlist_id")
|
||||
private Playlist playlist;
|
||||
|
||||
@Column(nullable = false, updatable = false)
|
||||
@CreationTimestamp
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
public Song() {
|
||||
}
|
||||
|
||||
@ -75,4 +90,8 @@ public class Song {
|
||||
public void setDurationInSeconds(int durationInSeconds) {
|
||||
this.durationInSeconds = durationInSeconds;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
package com.vibevault.repository;
|
||||
|
||||
import com.vibevault.model.Playlist;
|
||||
import com.vibevault.model.User;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import com.vibevault.model.Playlist;
|
||||
import com.vibevault.model.User;
|
||||
|
||||
/**
|
||||
* 歌单仓库接口
|
||||
@ -21,6 +22,36 @@ public interface PlaylistRepository extends JpaRepository<Playlist, Long> {
|
||||
// 按所有者查询歌单
|
||||
List<Playlist> findByOwner(User owner);
|
||||
|
||||
// 按所有者查询歌单并按名称排序(升序)
|
||||
List<Playlist> findByOwnerOrderByNameAsc(User owner);
|
||||
|
||||
// 按所有者查询歌单并按名称排序(降序)
|
||||
List<Playlist> findByOwnerOrderByNameDesc(User owner);
|
||||
|
||||
// 按所有者查询歌单并按创建时间排序(升序)
|
||||
List<Playlist> findByOwnerOrderByCreatedAtAsc(User owner);
|
||||
|
||||
// 按所有者查询歌单并按创建时间排序(降序)
|
||||
List<Playlist> findByOwnerOrderByCreatedAtDesc(User owner);
|
||||
|
||||
// [Advanced]: 按名称模糊搜索歌单
|
||||
List<Playlist> findByNameContainingIgnoreCase(String keyword);
|
||||
|
||||
// 按名称模糊搜索歌单并按名称排序
|
||||
List<Playlist> findByNameContainingIgnoreCaseOrderByNameAsc(String keyword);
|
||||
|
||||
// 按所有者和名称模糊搜索歌单
|
||||
List<Playlist> findByOwnerAndNameContainingIgnoreCase(User owner, String keyword);
|
||||
|
||||
// 按所有者和名称模糊搜索歌单并按创建时间排序(升序)
|
||||
List<Playlist> findByOwnerAndNameContainingIgnoreCaseOrderByCreatedAtAsc(User owner, String keyword);
|
||||
|
||||
// 按所有者和名称模糊搜索歌单并按创建时间排序(降序)
|
||||
List<Playlist> findByOwnerAndNameContainingIgnoreCaseOrderByCreatedAtDesc(User owner, String keyword);
|
||||
|
||||
// 按所有者和名称模糊搜索歌单并按名称排序(升序)
|
||||
List<Playlist> findByOwnerAndNameContainingIgnoreCaseOrderByNameAsc(User owner, String keyword);
|
||||
|
||||
// 按所有者和名称模糊搜索歌单并按名称排序(降序)
|
||||
List<Playlist> findByOwnerAndNameContainingIgnoreCaseOrderByNameDesc(User owner, String keyword);
|
||||
}
|
||||
|
||||
@ -1,15 +1,35 @@
|
||||
package com.vibevault.repository;
|
||||
|
||||
import com.vibevault.model.Song;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.vibevault.model.Song;
|
||||
|
||||
/**
|
||||
* 歌曲仓库接口
|
||||
*
|
||||
* 基础功能由 JpaRepository 提供
|
||||
*
|
||||
* [Advanced] 已添加:
|
||||
* - 按标题关键字模糊搜索歌曲
|
||||
* - 按创建时间排序返回结果
|
||||
*/
|
||||
@Repository
|
||||
public interface SongRepository extends JpaRepository<Song, Long> {
|
||||
// 这里可以根据需要添加自定义查询方法
|
||||
// 按标题关键字模糊搜索歌曲
|
||||
List<Song> findByTitleContainingIgnoreCase(String keyword);
|
||||
|
||||
// 按标题关键字模糊搜索歌曲并按标题排序
|
||||
List<Song> findByTitleContainingIgnoreCaseOrderByTitleAsc(String keyword);
|
||||
|
||||
// 按歌单ID查询歌曲
|
||||
List<Song> findByPlaylistId(Long playlistId);
|
||||
|
||||
// 按歌单ID查询歌曲并按创建时间排序
|
||||
List<Song> findByPlaylistIdOrderByCreatedAtAsc(Long playlistId);
|
||||
|
||||
// 按歌单ID查询歌曲并按标题排序
|
||||
List<Song> findByPlaylistIdOrderByTitleAsc(Long playlistId);
|
||||
}
|
||||
@ -1,10 +1,10 @@
|
||||
package com.vibevault.service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.vibevault.dto.PlaylistDTO;
|
||||
import com.vibevault.dto.SongCreateDTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 歌单服务接口
|
||||
* 定义歌单相关的业务操作
|
||||
@ -45,6 +45,14 @@ public interface PlaylistService {
|
||||
*/
|
||||
void removeSongFromPlaylist(Long playlistId, Long songId, String username);
|
||||
|
||||
/**
|
||||
* 批量向歌单添加歌曲
|
||||
* @param playlistId 歌单 ID
|
||||
* @param songs 歌曲列表
|
||||
* @param username 当前用户名(用于权限检查)
|
||||
*/
|
||||
PlaylistDTO addSongsToPlaylist(Long playlistId, List<SongCreateDTO> songs, String username);
|
||||
|
||||
/**
|
||||
* 删除歌单
|
||||
* @param playlistId 歌单 ID
|
||||
@ -60,7 +68,24 @@ public interface PlaylistService {
|
||||
List<PlaylistDTO> searchPlaylists(String keyword);
|
||||
|
||||
/**
|
||||
* [Advanced] 复制歌单
|
||||
* [Advanced] 复制一个歌单为新的歌单
|
||||
*
|
||||
* @param playlistId 要复制的歌单ID
|
||||
* @param newName 新歌单名称
|
||||
* @param username 当前用户名
|
||||
* @return 新创建的歌单DTO
|
||||
*/
|
||||
PlaylistDTO copyPlaylist(Long playlistId, String newName, String username);
|
||||
|
||||
/**
|
||||
* [Advanced] 按条件筛选歌单
|
||||
*
|
||||
* @param username 用户名
|
||||
* @param ownerName 歌单拥有者名称(可选)
|
||||
* @param nameKeyword 歌单名称关键词(可选)
|
||||
* @param sortBy 排序字段(name或createdAt)
|
||||
* @param sortDirection 排序方向(asc或desc)
|
||||
* @return 筛选后的歌单列表
|
||||
*/
|
||||
List<PlaylistDTO> filterPlaylists(String username, String ownerName, String nameKeyword, String sortBy, String sortDirection);
|
||||
}
|
||||
|
||||
@ -91,6 +91,23 @@ public class PlaylistServiceImpl implements PlaylistService {
|
||||
playlistRepository.save(playlist);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public PlaylistDTO addSongsToPlaylist(Long playlistId, List<SongCreateDTO> songDTOs, String username) {
|
||||
Playlist playlist = getPlaylistWithOwnershipCheck(playlistId, username);
|
||||
|
||||
for (SongCreateDTO songDTO : songDTOs) {
|
||||
Song song = new Song();
|
||||
song.setTitle(songDTO.title());
|
||||
song.setArtist(songDTO.artist());
|
||||
song.setDurationInSeconds(songDTO.durationInSeconds());
|
||||
playlist.addSong(song);
|
||||
}
|
||||
|
||||
Playlist savedPlaylist = playlistRepository.save(playlist);
|
||||
return convertToDTO(savedPlaylist);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void deletePlaylist(Long playlistId, String username) {
|
||||
@ -110,27 +127,133 @@ public class PlaylistServiceImpl implements PlaylistService {
|
||||
@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));
|
||||
// 验证原歌单存在且用户有权限访问
|
||||
Playlist originalPlaylist = getPlaylistWithOwnershipCheck(playlistId, username);
|
||||
|
||||
User newOwner = userRepository.findByUsername(username)
|
||||
// 获取当前用户
|
||||
User user = userRepository.findByUsername(username)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("User not found with username: " + username));
|
||||
|
||||
Playlist copiedPlaylist = new Playlist();
|
||||
copiedPlaylist.setName(newName);
|
||||
copiedPlaylist.setOwner(newOwner);
|
||||
// 创建新歌单
|
||||
Playlist newPlaylist = new Playlist();
|
||||
newPlaylist.setName(newName);
|
||||
newPlaylist.setOwner(user);
|
||||
|
||||
// 复制歌曲
|
||||
for (Song originalSong : originalPlaylist.getSongs()) {
|
||||
Song copiedSong = new Song();
|
||||
copiedSong.setTitle(originalSong.getTitle());
|
||||
copiedSong.setArtist(originalSong.getArtist());
|
||||
copiedSong.setDurationInSeconds(originalSong.getDurationInSeconds());
|
||||
copiedPlaylist.addSong(copiedSong);
|
||||
// 保存新歌单
|
||||
Playlist savedPlaylist = playlistRepository.save(newPlaylist);
|
||||
|
||||
// 复制原歌单的所有歌曲
|
||||
List<Song> originalSongs = songRepository.findByPlaylistId(originalPlaylist.getId());
|
||||
for (Song originalSong : originalSongs) {
|
||||
Song newSong = new Song();
|
||||
newSong.setTitle(originalSong.getTitle());
|
||||
newSong.setArtist(originalSong.getArtist());
|
||||
newSong.setDurationInSeconds(originalSong.getDurationInSeconds());
|
||||
newSong.setPlaylist(savedPlaylist);
|
||||
songRepository.save(newSong);
|
||||
}
|
||||
|
||||
Playlist savedPlaylist = playlistRepository.save(copiedPlaylist);
|
||||
return convertToDTO(savedPlaylist);
|
||||
// 重新加载新歌单以包含所有歌曲
|
||||
Playlist updatedPlaylist = playlistRepository.findById(savedPlaylist.getId())
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Playlist not found with id: " + savedPlaylist.getId()));
|
||||
|
||||
return convertToDTO(updatedPlaylist);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PlaylistDTO> filterPlaylists(String username, String ownerName, String nameKeyword, String sortBy, String sortDirection) {
|
||||
List<Playlist> playlists;
|
||||
User user = userRepository.findByUsername(username)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("User not found with username: " + username));
|
||||
|
||||
// 如果提供了ownerName,则筛选特定用户的歌单
|
||||
if (ownerName != null) {
|
||||
User owner = userRepository.findByUsername(ownerName)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("User not found with username: " + ownerName));
|
||||
|
||||
// 根据是否提供关键词和排序条件选择不同的查询方法
|
||||
if (nameKeyword != null) {
|
||||
if (sortBy != null && sortBy.equals("createdAt")) {
|
||||
if (sortDirection != null && sortDirection.equals("desc")) {
|
||||
// 按名称关键词筛选并按创建时间降序排序
|
||||
playlists = playlistRepository.findByOwnerAndNameContainingIgnoreCaseOrderByCreatedAtDesc(owner, nameKeyword);
|
||||
} else {
|
||||
// 按名称关键词筛选并按创建时间升序排序
|
||||
playlists = playlistRepository.findByOwnerAndNameContainingIgnoreCaseOrderByCreatedAtAsc(owner, nameKeyword);
|
||||
}
|
||||
} else {
|
||||
if (sortDirection != null && sortDirection.equals("desc")) {
|
||||
// 按名称关键词筛选并按名称降序排序
|
||||
playlists = playlistRepository.findByOwnerAndNameContainingIgnoreCaseOrderByNameDesc(owner, nameKeyword);
|
||||
} else {
|
||||
// 按名称关键词筛选并按名称升序排序
|
||||
playlists = playlistRepository.findByOwnerAndNameContainingIgnoreCaseOrderByNameAsc(owner, nameKeyword);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (sortBy != null && sortBy.equals("createdAt")) {
|
||||
if (sortDirection != null && sortDirection.equals("desc")) {
|
||||
// 按创建时间降序排序
|
||||
playlists = playlistRepository.findByOwnerOrderByCreatedAtDesc(owner);
|
||||
} else {
|
||||
// 按创建时间升序排序
|
||||
playlists = playlistRepository.findByOwnerOrderByCreatedAtAsc(owner);
|
||||
}
|
||||
} else {
|
||||
if (sortDirection != null && sortDirection.equals("desc")) {
|
||||
// 按名称降序排序
|
||||
playlists = playlistRepository.findByOwnerOrderByNameDesc(owner);
|
||||
} else {
|
||||
// 按名称升序排序
|
||||
playlists = playlistRepository.findByOwnerOrderByNameAsc(owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 未提供ownerName,则筛选当前用户的歌单
|
||||
if (nameKeyword != null) {
|
||||
if (sortBy != null && sortBy.equals("createdAt")) {
|
||||
if (sortDirection != null && sortDirection.equals("desc")) {
|
||||
// 按名称关键词筛选并按创建时间降序排序
|
||||
playlists = playlistRepository.findByOwnerAndNameContainingIgnoreCaseOrderByCreatedAtDesc(user, nameKeyword);
|
||||
} else {
|
||||
// 按名称关键词筛选并按创建时间升序排序
|
||||
playlists = playlistRepository.findByOwnerAndNameContainingIgnoreCaseOrderByCreatedAtAsc(user, nameKeyword);
|
||||
}
|
||||
} else {
|
||||
if (sortDirection != null && sortDirection.equals("desc")) {
|
||||
// 按名称关键词筛选并按名称降序排序
|
||||
playlists = playlistRepository.findByOwnerAndNameContainingIgnoreCaseOrderByNameDesc(user, nameKeyword);
|
||||
} else {
|
||||
// 按名称关键词筛选并按名称升序排序
|
||||
playlists = playlistRepository.findByOwnerAndNameContainingIgnoreCaseOrderByNameAsc(user, nameKeyword);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (sortBy != null && sortBy.equals("createdAt")) {
|
||||
if (sortDirection != null && sortDirection.equals("desc")) {
|
||||
// 按创建时间降序排序
|
||||
playlists = playlistRepository.findByOwnerOrderByCreatedAtDesc(user);
|
||||
} else {
|
||||
// 按创建时间升序排序
|
||||
playlists = playlistRepository.findByOwnerOrderByCreatedAtAsc(user);
|
||||
}
|
||||
} else {
|
||||
if (sortDirection != null && sortDirection.equals("desc")) {
|
||||
// 按名称降序排序
|
||||
playlists = playlistRepository.findByOwnerOrderByNameDesc(user);
|
||||
} else {
|
||||
// 按名称升序排序
|
||||
playlists = playlistRepository.findByOwnerOrderByNameAsc(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 转换为DTO并返回
|
||||
return playlists.stream()
|
||||
.map(this::convertToDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
// 辅助方法:检查歌单所有权
|
||||
|
||||
Loading…
Reference in New Issue
Block a user