generated from Java-2025Fall/final-vibevault-template
完成作业
This commit is contained in:
parent
d6a4bd92e6
commit
f87b60a8b9
0
build-output.txt
Normal file
0
build-output.txt
Normal file
@ -1,12 +1,17 @@
|
||||
package com.vibevault.controller;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import com.vibevault.model.User;
|
||||
import com.vibevault.repository.UserRepository;
|
||||
import com.vibevault.security.JwtService;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
/**
|
||||
* 认证控制器
|
||||
@ -46,9 +51,7 @@ public class AuthController {
|
||||
}
|
||||
|
||||
// 创建新用户,加密密码
|
||||
User user = new User();
|
||||
user.setUsername(request.username());
|
||||
user.setPassword(passwordEncoder.encode(request.password()));
|
||||
User user = new User(request.username(), passwordEncoder.encode(request.password()));
|
||||
|
||||
// 保存用户到数据库
|
||||
userRepository.save(user);
|
||||
|
||||
@ -1,14 +1,23 @@
|
||||
package com.vibevault.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
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.PlaylistCreateDTO;
|
||||
import com.vibevault.dto.PlaylistDTO;
|
||||
import com.vibevault.dto.SongCreateDTO;
|
||||
import com.vibevault.service.PlaylistService;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 歌单 REST 控制器
|
||||
@ -56,7 +65,7 @@ public class PlaylistController {
|
||||
@ResponseStatus(HttpStatus.CREATED)
|
||||
public PlaylistDTO createPlaylist(@RequestBody PlaylistCreateDTO playlistCreateDTO, Authentication authentication) {
|
||||
String username = authentication.getName();
|
||||
return playlistService.createPlaylist(playlistCreateDTO.getName(), username);
|
||||
return playlistService.createPlaylist(playlistCreateDTO.name(), username);
|
||||
}
|
||||
|
||||
// POST /api/playlists/{id}/songs - 添加歌曲(需认证)
|
||||
|
||||
@ -7,6 +7,7 @@ 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;
|
||||
|
||||
/**
|
||||
@ -21,11 +22,70 @@ import java.util.Map;
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
// TODO: 实现 ResourceNotFoundException 处理器 (返回 404)
|
||||
/**
|
||||
* 处理 ResourceNotFoundException 异常
|
||||
* @param ex 异常对象
|
||||
* @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);
|
||||
}
|
||||
|
||||
// TODO: 实现 UnauthorizedException 处理器 (返回 403)
|
||||
/**
|
||||
* 处理 UnauthorizedAccessException 异常
|
||||
* @param ex 异常对象
|
||||
* @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());
|
||||
|
||||
return new ResponseEntity<>(body, HttpStatus.FORBIDDEN);
|
||||
}
|
||||
|
||||
// TODO: 实现 ResponseStatusException 处理器
|
||||
/**
|
||||
* 处理 ResponseStatusException 异常
|
||||
* @param ex 异常对象
|
||||
* @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());
|
||||
}
|
||||
|
||||
// TODO [Advanced]: 实现通用异常处理器
|
||||
/**
|
||||
* 处理所有其他未捕获的异常
|
||||
* @param ex 异常对象
|
||||
* @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");
|
||||
|
||||
// 在开发环境中可以添加异常堆栈信息
|
||||
// body.put("stackTrace", Arrays.toString(ex.getStackTrace()));
|
||||
|
||||
return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,20 @@
|
||||
package com.vibevault.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
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.OneToMany;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
/**
|
||||
* 歌单实体类
|
||||
*
|
||||
@ -34,7 +44,7 @@ public class Playlist {
|
||||
@OneToMany(mappedBy = "playlist", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private List<Song> songs = new ArrayList<>();
|
||||
|
||||
protected Playlist() {
|
||||
public Playlist() {
|
||||
}
|
||||
|
||||
public Playlist(String name, User owner) {
|
||||
@ -58,6 +68,10 @@ public class Playlist {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(User owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public List<Song> getSongs() {
|
||||
return Collections.unmodifiableList(songs);
|
||||
}
|
||||
|
||||
@ -63,4 +63,16 @@ public class Song {
|
||||
public void setPlaylist(Playlist playlist) {
|
||||
this.playlist = playlist;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public void setArtist(String artist) {
|
||||
this.artist = artist;
|
||||
}
|
||||
|
||||
public void setDurationInSeconds(int durationInSeconds) {
|
||||
this.durationInSeconds = durationInSeconds;
|
||||
}
|
||||
}
|
||||
|
||||
15
src/main/java/com/vibevault/repository/SongRepository.java
Normal file
15
src/main/java/com/vibevault/repository/SongRepository.java
Normal file
@ -0,0 +1,15 @@
|
||||
package com.vibevault.repository;
|
||||
|
||||
import com.vibevault.model.Song;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
* 歌曲仓库接口
|
||||
*
|
||||
* 基础功能由 JpaRepository 提供
|
||||
*/
|
||||
@Repository
|
||||
public interface SongRepository extends JpaRepository<Song, Long> {
|
||||
// 这里可以根据需要添加自定义查询方法
|
||||
}
|
||||
@ -1,122 +0,0 @@
|
||||
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<PlaylistDTO> getAllPlaylists() {
|
||||
// TODO: 实现获取所有歌单
|
||||
throw new UnsupportedOperationException("待实现");
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlaylistDTO getPlaylistById(Long id) {
|
||||
// TODO: 实现根据 ID 获取歌单,不存在时抛出 ResourceNotFoundException
|
||||
throw new UnsupportedOperationException("待实现");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public PlaylistDTO createPlaylist(String name, String ownerUsername) {
|
||||
// TODO: 实现创建歌单
|
||||
throw new UnsupportedOperationException("待实现");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public PlaylistDTO addSongToPlaylist(Long playlistId, SongCreateDTO song, String username) {
|
||||
// TODO: 实现添加歌曲到歌单
|
||||
// [Challenge] 需要检查用户是否有权限操作此歌单
|
||||
throw new UnsupportedOperationException("待实现");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void removeSongFromPlaylist(Long playlistId, Long songId, String username) {
|
||||
// TODO: 实现从歌单移除歌曲
|
||||
// [Challenge] 需要检查用户是否有权限操作此歌单
|
||||
throw new UnsupportedOperationException("待实现");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void deletePlaylist(Long playlistId, String username) {
|
||||
// TODO: 实现删除歌单
|
||||
// [Challenge] 需要检查用户是否有权限操作此歌单
|
||||
throw new UnsupportedOperationException("待实现");
|
||||
}
|
||||
|
||||
// ========== Advanced 方法 ==========
|
||||
|
||||
@Override
|
||||
public List<PlaylistDTO> searchPlaylists(String keyword) {
|
||||
// TODO [Advanced]: 实现按关键字搜索歌单
|
||||
throw new UnsupportedOperationException("待实现");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public PlaylistDTO copyPlaylist(Long playlistId, String newName, String username) {
|
||||
// TODO [Advanced]: 实现复制歌单
|
||||
throw new UnsupportedOperationException("待实现");
|
||||
}
|
||||
|
||||
// ========== 辅助方法 ==========
|
||||
|
||||
/**
|
||||
* 将 Playlist 实体转换为 DTO
|
||||
*/
|
||||
private PlaylistDTO toDTO(Playlist playlist) {
|
||||
// TODO: 实现实体到 DTO 的转换
|
||||
throw new UnsupportedOperationException("待实现");
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Song 实体转换为 DTO
|
||||
*/
|
||||
private SongDTO toSongDTO(Song song) {
|
||||
// TODO: 实现实体到 DTO 的转换
|
||||
throw new UnsupportedOperationException("待实现");
|
||||
}
|
||||
|
||||
/**
|
||||
* [Challenge] 检查用户是否有权限操作指定歌单
|
||||
* 规则:歌单所有者或管理员可以操作
|
||||
*/
|
||||
private void checkPermission(Playlist playlist, String username) {
|
||||
// TODO [Challenge]: 实现权限检查
|
||||
// 如果无权限,抛出 UnauthorizedException
|
||||
}
|
||||
}
|
||||
@ -1,32 +1,37 @@
|
||||
package com.vibevault.service.impl;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.vibevault.dto.PlaylistDTO;
|
||||
import com.vibevault.dto.SongCreateDTO;
|
||||
import com.vibevault.dto.SongDTO;
|
||||
import com.vibevault.exception.ResourceNotFoundException;
|
||||
import com.vibevault.exception.UnauthorizedAccessException;
|
||||
import com.vibevault.model.Playlist;
|
||||
import com.vibevault.model.Song;
|
||||
import com.vibevault.model.User;
|
||||
import com.vibevault.repository.PlaylistRepository;
|
||||
import com.vibevault.repository.SongRepository;
|
||||
import com.vibevault.repository.UserRepository;
|
||||
import com.vibevault.service.PlaylistService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class PlaylistServiceImpl implements PlaylistService {
|
||||
|
||||
private final PlaylistRepository playlistRepository;
|
||||
private final UserRepository userRepository;
|
||||
private final SongRepository songRepository;
|
||||
|
||||
@Autowired
|
||||
public PlaylistServiceImpl(PlaylistRepository playlistRepository, UserRepository userRepository) {
|
||||
public PlaylistServiceImpl(PlaylistRepository playlistRepository, UserRepository userRepository, SongRepository songRepository) {
|
||||
this.playlistRepository = playlistRepository;
|
||||
this.userRepository = userRepository;
|
||||
this.songRepository = songRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -63,9 +68,9 @@ public class PlaylistServiceImpl implements PlaylistService {
|
||||
Playlist playlist = getPlaylistWithOwnershipCheck(playlistId, username);
|
||||
|
||||
Song song = new Song();
|
||||
song.setTitle(songDTO.getTitle());
|
||||
song.setArtist(songDTO.getArtist());
|
||||
song.setDurationInSeconds(songDTO.getDurationInSeconds());
|
||||
song.setTitle(songDTO.title());
|
||||
song.setArtist(songDTO.artist());
|
||||
song.setDurationInSeconds(songDTO.durationInSeconds());
|
||||
|
||||
playlist.addSong(song);
|
||||
Playlist savedPlaylist = playlistRepository.save(playlist);
|
||||
@ -143,11 +148,25 @@ public class PlaylistServiceImpl implements PlaylistService {
|
||||
|
||||
// 辅助方法:将 Playlist 转换为 PlaylistDTO
|
||||
private PlaylistDTO convertToDTO(Playlist playlist) {
|
||||
PlaylistDTO dto = new PlaylistDTO();
|
||||
dto.setId(playlist.getId());
|
||||
dto.setName(playlist.getName());
|
||||
dto.setOwnerUsername(playlist.getOwner().getUsername());
|
||||
dto.setSongCount(playlist.getSongs().size());
|
||||
return dto;
|
||||
List<SongDTO> songDTOs = playlist.getSongs().stream()
|
||||
.map(this::convertSongToDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new PlaylistDTO(
|
||||
playlist.getId(),
|
||||
playlist.getName(),
|
||||
playlist.getOwner().getUsername(),
|
||||
songDTOs
|
||||
);
|
||||
}
|
||||
|
||||
// 辅助方法:将 Song 转换为 SongDTO
|
||||
private SongDTO convertSongToDTO(Song song) {
|
||||
return new SongDTO(
|
||||
song.getId(),
|
||||
song.getTitle(),
|
||||
song.getArtist(),
|
||||
song.getDurationInSeconds()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
0
test-output.txt
Normal file
0
test-output.txt
Normal file
Loading…
Reference in New Issue
Block a user