如何在/movies/{id}接口返回的Movie DTO中追加登录用户评分?
Got it, let's walk through how to add the logged-in user's rating to your /movies/{id} response—just like IMDB does. There are a few practical approaches you can pick from, depending on your project's structure and long-term needs:
This is the cleanest, most type-safe approach. You'll keep your original Movie class untouched, and create a new DTO that wraps it along with the user's rating:
// 原有的Movie类保持不变,专注于电影本身的核心信息 public class Movie { private final String title; private final MovieType type; private final List<LanguageType> languages; private final List<CountryType> countries; // 构造器、getters等方法 } // 新的扩展DTO,专门用于返回带用户评分的电影信息 public class MovieWithUserRating { private final Movie movie; private final Integer userRating; // 用Integer可以兼容"未评分"的null场景 public MovieWithUserRating(Movie movie, Integer userRating) { this.movie = movie; this.userRating = userRating; } // 对应的getter方法 }
Then in your controller, fetch the movie, query the user's rating, and assemble the response:
@GetMapping("/movies/{id}") public ResponseEntity<MovieWithUserRating> getMovieWithUserRating( @PathVariable Long id, @AuthenticationPrincipal UserDetails currentUser) { // 用Spring Security获取当前登录用户 // 1. 获取电影基础信息 Movie movie = movieService.findById(id); if (movie == null) { return ResponseEntity.notFound().build(); } // 2. 查询当前用户对该电影的评分(需要你实现对应的评分查询逻辑) Integer userRating = ratingService.getUserRating(currentUser.getUsername(), id); // 3. 组装并返回结果 return ResponseEntity.ok(new MovieWithUserRating(movie, userRating)); }
Pros: 完全解耦电影核心数据和用户个性化数据,类型安全,后续扩展用户相关字段(比如是否收藏)非常方便。
If you don't want to create a new DTO class, you can use Jackson's annotations to dynamically add the rating field without modifying your core Movie class too much:
public class Movie { private final String title; private final MovieType type; private final List<LanguageType> languages; private final List<CountryType> countries; private Integer userRating; // 新增字段,注意调整构造器或添加setter // 原有构造器、getters @JsonAnyGetter public Map<String, Object> getAdditionalUserFields() { Map<String, Object> fields = new HashMap<>(); if (userRating != null) { fields.put("userRating", userRating); } return fields; } }
Then in your controller:
@GetMapping("/movies/{id}") public ResponseEntity<Movie> getMovieWithUserRating( @PathVariable Long id, @AuthenticationPrincipal UserDetails currentUser) { Movie movie = movieService.findById(id); if (movie == null) { return ResponseEntity.notFound().build(); } Integer userRating = ratingService.getUserRating(currentUser.getUsername(), id); movie.setUserRating(userRating); // 假设添加了setter方法 return ResponseEntity.ok(movie); }
Pros: 不用新增类,快速实现需求;Cons: 把用户个性化数据耦合到了核心电影实体里,长期维护会让Movie类职责变杂。
If your project uses a standardized API response format, you can wrap the movie data and user rating in a generic response object:
// 通用API响应包装类 public class ApiResponse<T> { private T data; private Map<String, Object> metadata; // 存放用户评分这类元数据 // 构造器、getters、setters }
Controller implementation:
@GetMapping("/movies/{id}") public ResponseEntity<ApiResponse<Movie>> getMovieWithUserRating( @PathVariable Long id, @AuthenticationPrincipal UserDetails currentUser) { Movie movie = movieService.findById(id); if (movie == null) { return ResponseEntity.notFound().build(); } Integer userRating = ratingService.getUserRating(currentUser.getUsername(), id); Map<String, Object> metadata = new HashMap<>(); metadata.put("userRating", userRating); ApiResponse<Movie> response = new ApiResponse<>(); response.setData(movie); response.setMetadata(metadata); return ResponseEntity.ok(response); }
Pros: 符合统一响应规范,把业务数据和用户元数据分离,结构清晰。
- 获取当前登录用户:如果用Spring Security,
@AuthenticationPrincipal是最便捷的方式;如果是JWT认证,你可以从请求头解析token获取用户标识。 - 性能优化:如果查询电影和评分是两次独立的DB请求,建议用JOIN查询或者批量查询来避免N+1性能问题。
- 未评分场景:如果用户还没给电影评分,返回
null或者约定的默认值(比如0),前端可以根据这个值展示"未评分"的状态。
内容的提问来源于stack exchange,提问作者JONKI

