diff --git a/src/main/java/com/vibevault/controller/PlaylistController.java b/src/main/java/com/vibevault/controller/PlaylistController.java index f333a5c..fc3eba8 100644 --- a/src/main/java/com/vibevault/controller/PlaylistController.java +++ b/src/main/java/com/vibevault/controller/PlaylistController.java @@ -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 addSongsToPlaylist(@PathVariable Long id, @RequestBody List 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 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> 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 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); - } + } diff --git a/src/main/java/com/vibevault/dto/CopyPlaylistRequest.java b/src/main/java/com/vibevault/dto/CopyPlaylistRequest.java new file mode 100644 index 0000000..4d9ec01 --- /dev/null +++ b/src/main/java/com/vibevault/dto/CopyPlaylistRequest.java @@ -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; + } +} diff --git a/src/main/java/com/vibevault/exception/ErrorResponse.java b/src/main/java/com/vibevault/exception/ErrorResponse.java new file mode 100644 index 0000000..c78bca5 --- /dev/null +++ b/src/main/java/com/vibevault/exception/ErrorResponse.java @@ -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; + } +} diff --git a/src/main/java/com/vibevault/exception/GlobalExceptionHandler.java b/src/main/java/com/vibevault/exception/GlobalExceptionHandler.java index 344bcc1..7804b63 100644 --- a/src/main/java/com/vibevault/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/vibevault/exception/GlobalExceptionHandler.java @@ -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> handleResourceNotFoundException(ResourceNotFoundException ex) { - Map 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 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> handleUnauthorizedAccessException(UnauthorizedAccessException ex) { - Map body = new HashMap<>(); - body.put("timestamp", LocalDateTime.now()); - body.put("status", HttpStatus.FORBIDDEN.value()); - body.put("error", "Forbidden"); - body.put("message", ex.getMessage()); - - return new ResponseEntity<>(body, HttpStatus.FORBIDDEN); + public ResponseEntity handleUnauthorizedAccessException(UnauthorizedAccessException ex) { + return buildErrorResponse(HttpStatus.FORBIDDEN.value(), "Forbidden", ex.getMessage()); + } + + /** + * 处理 UnauthorizedException 异常 + * @param ex 异常对象 + * @return 包含错误信息的 ResponseEntity + */ + @ExceptionHandler(UnauthorizedException.class) + public ResponseEntity 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> handleResponseStatusException(ResponseStatusException ex) { - Map 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 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> handleGeneralException(Exception ex) { - Map 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"); - - // 在开发环境中可以添加异常堆栈信息 - // body.put("stackTrace", Arrays.toString(ex.getStackTrace())); - - return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR); + public ResponseEntity handleGeneralException(Exception ex) { + return buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Internal Server Error", "An unexpected error occurred"); + } + + /** + * 构建统一的错误响应格式 + * @param status 响应状态码 + * @param error 错误类型 + * @param message 错误消息 + * @return 包含错误信息的 ResponseEntity + */ + private ResponseEntity buildErrorResponse(int statusCode, String error, String message) { + ErrorResponse errorResponse = new ErrorResponse(statusCode, error, message); + return ResponseEntity.status(statusCode).body(errorResponse); } } diff --git a/src/main/java/com/vibevault/model/Playlist.java b/src/main/java/com/vibevault/model/Playlist.java index 20bf1b8..c7bf16a 100644 --- a/src/main/java/com/vibevault/model/Playlist.java +++ b/src/main/java/com/vibevault/model/Playlist.java @@ -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 songs = new ArrayList<>(); + @Column(nullable = false, updatable = false) + @CreationTimestamp + private LocalDateTime createdAt; + public Playlist() { } @@ -71,10 +78,14 @@ public class Playlist { public void setOwner(User owner) { this.owner = owner; } - + public List getSongs() { return Collections.unmodifiableList(songs); } + + public LocalDateTime getCreatedAt() { + return createdAt; + } /** * 向歌单添加歌曲 diff --git a/src/main/java/com/vibevault/model/Song.java b/src/main/java/com/vibevault/model/Song.java index f00437d..988ca46 100644 --- a/src/main/java/com/vibevault/model/Song.java +++ b/src/main/java/com/vibevault/model/Song.java @@ -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; + } } diff --git a/src/main/java/com/vibevault/repository/PlaylistRepository.java b/src/main/java/com/vibevault/repository/PlaylistRepository.java index 8268d7e..640b954 100644 --- a/src/main/java/com/vibevault/repository/PlaylistRepository.java +++ b/src/main/java/com/vibevault/repository/PlaylistRepository.java @@ -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 { // 按所有者查询歌单 List findByOwner(User owner); + // 按所有者查询歌单并按名称排序(升序) + List findByOwnerOrderByNameAsc(User owner); + + // 按所有者查询歌单并按名称排序(降序) + List findByOwnerOrderByNameDesc(User owner); + + // 按所有者查询歌单并按创建时间排序(升序) + List findByOwnerOrderByCreatedAtAsc(User owner); + + // 按所有者查询歌单并按创建时间排序(降序) + List findByOwnerOrderByCreatedAtDesc(User owner); + // [Advanced]: 按名称模糊搜索歌单 List findByNameContainingIgnoreCase(String keyword); + + // 按名称模糊搜索歌单并按名称排序 + List findByNameContainingIgnoreCaseOrderByNameAsc(String keyword); + + // 按所有者和名称模糊搜索歌单 + List findByOwnerAndNameContainingIgnoreCase(User owner, String keyword); + + // 按所有者和名称模糊搜索歌单并按创建时间排序(升序) + List findByOwnerAndNameContainingIgnoreCaseOrderByCreatedAtAsc(User owner, String keyword); + + // 按所有者和名称模糊搜索歌单并按创建时间排序(降序) + List findByOwnerAndNameContainingIgnoreCaseOrderByCreatedAtDesc(User owner, String keyword); + + // 按所有者和名称模糊搜索歌单并按名称排序(升序) + List findByOwnerAndNameContainingIgnoreCaseOrderByNameAsc(User owner, String keyword); + + // 按所有者和名称模糊搜索歌单并按名称排序(降序) + List findByOwnerAndNameContainingIgnoreCaseOrderByNameDesc(User owner, String keyword); } diff --git a/src/main/java/com/vibevault/repository/SongRepository.java b/src/main/java/com/vibevault/repository/SongRepository.java index 95c266a..f998a94 100644 --- a/src/main/java/com/vibevault/repository/SongRepository.java +++ b/src/main/java/com/vibevault/repository/SongRepository.java @@ -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 { - // 这里可以根据需要添加自定义查询方法 + // 按标题关键字模糊搜索歌曲 + List findByTitleContainingIgnoreCase(String keyword); + + // 按标题关键字模糊搜索歌曲并按标题排序 + List findByTitleContainingIgnoreCaseOrderByTitleAsc(String keyword); + + // 按歌单ID查询歌曲 + List findByPlaylistId(Long playlistId); + + // 按歌单ID查询歌曲并按创建时间排序 + List findByPlaylistIdOrderByCreatedAtAsc(Long playlistId); + + // 按歌单ID查询歌曲并按标题排序 + List findByPlaylistIdOrderByTitleAsc(Long playlistId); } \ No newline at end of file diff --git a/src/main/java/com/vibevault/service/PlaylistService.java b/src/main/java/com/vibevault/service/PlaylistService.java index 226d083..43b3416 100644 --- a/src/main/java/com/vibevault/service/PlaylistService.java +++ b/src/main/java/com/vibevault/service/PlaylistService.java @@ -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 songs, String username); + /** * 删除歌单 * @param playlistId 歌单 ID @@ -60,7 +68,24 @@ public interface PlaylistService { List 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 filterPlaylists(String username, String ownerName, String nameKeyword, String sortBy, String sortDirection); } diff --git a/src/main/java/com/vibevault/service/impl/PlaylistServiceImpl.java b/src/main/java/com/vibevault/service/impl/PlaylistServiceImpl.java index 4d3e577..e900490 100644 --- a/src/main/java/com/vibevault/service/impl/PlaylistServiceImpl.java +++ b/src/main/java/com/vibevault/service/impl/PlaylistServiceImpl.java @@ -91,6 +91,23 @@ public class PlaylistServiceImpl implements PlaylistService { playlistRepository.save(playlist); } + @Override + @Transactional + public PlaylistDTO addSongsToPlaylist(Long playlistId, List 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)); - - User newOwner = userRepository.findByUsername(username) + // 验证原歌单存在且用户有权限访问 + Playlist originalPlaylist = getPlaylistWithOwnershipCheck(playlistId, 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); - - // 复制歌曲 - 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 newPlaylist = new Playlist(); + newPlaylist.setName(newName); + newPlaylist.setOwner(user); + + // 保存新歌单 + Playlist savedPlaylist = playlistRepository.save(newPlaylist); + + // 复制原歌单的所有歌曲 + List 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 filterPlaylists(String username, String ownerName, String nameKeyword, String sortBy, String sortDirection) { + List 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()); } // 辅助方法:检查歌单所有权