完成作业
Some checks failed
autograde-final-vibevault / check-trigger (push) Successful in 13s
autograde-final-vibevault / grade (push) Failing after 46s

This commit is contained in:
VibeVault User 2025-12-14 01:48:04 +08:00
parent d6a4bd92e6
commit f87b60a8b9
10 changed files with 167 additions and 157 deletions

0
build-output.txt Normal file
View File

View 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);

View File

@ -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 - 添加歌曲需认证

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View 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> {
// 这里可以根据需要添加自定义查询方法
}

View File

@ -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
}
}

View File

@ -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
View File