generated from yandex-praktikum/java-filmorate
-
Notifications
You must be signed in to change notification settings - Fork 0
Переработана архитектура и добалена новая логика #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
e5a69d5
Переработана архитектура и добалена новая логика
e79da3c
Переработана архитектура и добалена новая логика
d2fe3d4
Переработана архитектура и добалена новая логика
9423c33
Переработана архитектура и добалена новая логика
61c698c
Переработана архитектура и добалена новая логика
ae7cc7e
Переработана архитектура и добалена новая логика
91d5c0c
Добавил логирование и аннатацию @Positive
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
134 changes: 93 additions & 41 deletions
134
src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,69 +1,121 @@ | ||
| package ru.yandex.practicum.filmorate.controller; | ||
|
|
||
| import org.springframework.web.bind.annotation.RestController; | ||
|
|
||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.beans.factory.annotation.Autowired; | ||
| import org.springframework.validation.annotation.Validated; | ||
| import org.springframework.web.bind.annotation.*; | ||
| import jakarta.validation.Valid; | ||
| import jakarta.validation.constraints.Positive; | ||
| import ru.yandex.practicum.filmorate.exception.ValidationException; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import ru.yandex.practicum.filmorate.exception.NotFoundException; | ||
| import ru.yandex.practicum.filmorate.model.Film; | ||
| import org.springframework.web.bind.annotation.*; | ||
| import ru.yandex.practicum.filmorate.service.FilmService; | ||
| import ru.yandex.practicum.filmorate.service.UserService; | ||
| import ru.yandex.practicum.filmorate.storage.InMemoryFilmStorage; | ||
| import ru.yandex.practicum.filmorate.storage.InMemoryUserStorage; | ||
|
|
||
| import java.time.LocalDate; | ||
| import java.util.ArrayList; | ||
| import java.util.HashMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
|
|
||
| @RestController | ||
| @RequestMapping("/films") | ||
| @Slf4j | ||
| @Validated | ||
| public class FilmController { | ||
| private final Map<Long, Film> films = new HashMap<>(); | ||
| private Long idCounter = 1L; | ||
| private final FilmService filmService; | ||
| private final UserService userService; | ||
|
|
||
| private void validateFilm(Film film) { | ||
| if (film.getName() == null || film.getName().isBlank()) { | ||
| log.warn("Название фильма не может быть пустым"); | ||
| throw new ValidationException("Название фильма не может быть пустым"); | ||
| } | ||
| if (film.getDescription() != null && film.getDescription().length() > 200) { | ||
| log.warn("Максимальная длина описания — 200 символов"); | ||
| throw new ValidationException("Максимальная длина описания — 200 символов"); | ||
| } | ||
| if (film.getReleaseDate() == null || film.getReleaseDate().isBefore(LocalDate.of(1895, 12, 28))) { | ||
| log.warn("Дата релиза должна быть не раньше 28 декабря 1895 года"); | ||
| throw new ValidationException("Дата релиза должна быть не раньше 28 декабря 1895 года"); | ||
| } | ||
| if (film.getDuration() <= 0) { | ||
| log.warn("Продолжительность фильма должна быть положительным числом"); | ||
| throw new ValidationException("Продолжительность фильма должна быть положительным числом"); | ||
| } | ||
| public FilmController() { | ||
| this.filmService = new FilmService(new InMemoryFilmStorage()); | ||
| this.userService = new UserService(new InMemoryUserStorage()); | ||
| } | ||
|
|
||
| @Autowired | ||
| public FilmController(FilmService filmService, UserService userService) { | ||
| this.filmService = filmService; | ||
| this.userService = userService; | ||
| } | ||
|
|
||
| @GetMapping | ||
| public List<Film> getAllFilms() { | ||
| log.info("Fetching all films"); | ||
| return filmService.getAllFilms(); | ||
| } | ||
|
|
||
| @GetMapping("/{id}") | ||
| public Film getFilm(@Positive @PathVariable Long id) { | ||
| log.info("Fetching film with id {}", id); | ||
| return filmService.getFilmById(id); | ||
| } | ||
|
|
||
| @PostMapping | ||
| public Film addFilm(@Valid @RequestBody Film film) { | ||
| log.info("Adding film: {}", film); | ||
| validateFilm(film); | ||
| film.setId(idCounter++); | ||
| films.put(film.getId(), film); | ||
| log.info("Добавлен фильм с ID {}: {}", film.getId(), film); | ||
| return film; | ||
| return filmService.addFilm(film); | ||
| } | ||
|
|
||
| @PutMapping | ||
| public Film updateFilm(@Valid @RequestBody Film film) { | ||
| if (film.getId() == null || !films.containsKey(film.getId())) { | ||
| log.warn("Фильм с ID {} не найден", film.getId()); | ||
| throw new ValidationException("Фильм с указанным ID не существует"); | ||
| } | ||
| log.info("Updating film: {}", film); | ||
| validateFilm(film); | ||
| films.put(film.getId(), film); | ||
| log.info("Обновлен фильм с ID {}: {}", film.getId(), film); | ||
| return film; | ||
| try { | ||
| return filmService.updateFilm(film); | ||
| } catch (NotFoundException e) { | ||
| log.warn("Film not found: {}", film.getId()); | ||
| throw new ValidationException("Film not found"); | ||
| } | ||
| } | ||
|
|
||
| @GetMapping | ||
| public List<Film> getAllFilms() { | ||
| log.info("Получен запрос всех фильмов. Текущее количество: {}", films.size()); | ||
| return new ArrayList<>(films.values()); | ||
| @DeleteMapping("/{id}") | ||
| public void deleteFilm(@Positive @PathVariable Long id) { | ||
| log.info("Deleting film with id {}", id); | ||
| filmService.removeFilm(id); | ||
| } | ||
|
|
||
| @PutMapping("/{id}/like/{userId}") | ||
| public void addLike(@Positive @PathVariable Long id, | ||
| @Positive @PathVariable Long userId) { | ||
| log.info("User {} likes film {}", userId, id); | ||
| userService.getUserById(userId); | ||
| filmService.addLike(id, userId); | ||
| } | ||
|
|
||
| @DeleteMapping("/{id}/like/{userId}") | ||
| public void removeLike(@Positive @PathVariable Long id, | ||
| @Positive @PathVariable Long userId) { | ||
| log.info("User {} removes like from film {}", userId, id); | ||
| userService.getUserById(userId); | ||
| filmService.removeLike(id, userId); | ||
| } | ||
|
|
||
| @GetMapping("/popular") | ||
| public List<Film> getPopularFilms(@RequestParam(defaultValue = "10") @Positive int count) { | ||
| log.info("Fetching top {} popular films", count); | ||
| return filmService.getPopularFilms(count); | ||
| } | ||
|
|
||
| private void validateFilm(Film film) { | ||
| if (film == null) { | ||
| throw new ValidationException("Film cannot be null"); | ||
| } | ||
| if (film.getName() == null || film.getName().isBlank()) { | ||
| log.warn("Film name is invalid"); | ||
| throw new ValidationException("Film name is invalid"); | ||
| } | ||
| if (film.getDescription() != null && film.getDescription().length() > 200) { | ||
| log.warn("Film description is too long"); | ||
| throw new ValidationException("Description length exceeds 200 characters"); | ||
| } | ||
| if (film.getReleaseDate() == null || film.getReleaseDate().isBefore(LocalDate.of(1895, 12, 28))) { | ||
| log.warn("Film release date is invalid"); | ||
| throw new ValidationException("Release date is invalid"); | ||
| } | ||
| if (film.getDuration() <= 0) { | ||
| log.warn("Film duration is invalid"); | ||
| throw new ValidationException("Duration must be positive"); | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
57 changes: 57 additions & 0 deletions
57
src/main/java/ru/yandex/practicum/filmorate/controller/GlobalExceptionHandler.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| package ru.yandex.practicum.filmorate.controller; | ||
|
|
||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.http.HttpStatus; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.web.bind.MethodArgumentNotValidException; | ||
| import org.springframework.web.bind.annotation.ExceptionHandler; | ||
| import org.springframework.web.bind.annotation.RestControllerAdvice; | ||
| import org.springframework.validation.FieldError; | ||
| import ru.yandex.practicum.filmorate.exception.NotFoundException; | ||
| import ru.yandex.practicum.filmorate.exception.ValidationException; | ||
|
|
||
| import java.util.Map; | ||
|
|
||
| @Slf4j | ||
| @RestControllerAdvice | ||
| public class GlobalExceptionHandler { | ||
|
|
||
| @ExceptionHandler(MethodArgumentNotValidException.class) | ||
| public ResponseEntity<Map<String, String>> handleMethodArgumentNotValid(MethodArgumentNotValidException ex) { | ||
| log.error("MethodArgumentNotValidException: {}", ex.getMessage(), ex); | ||
| String errorMsg = ex.getBindingResult() | ||
| .getFieldErrors() | ||
| .stream() | ||
| .findFirst() | ||
| .map(FieldError::getDefaultMessage) | ||
| .orElse("Validation failed"); | ||
| return ResponseEntity.status(HttpStatus.BAD_REQUEST) | ||
| .body(Map.of("error", errorMsg)); | ||
| } | ||
|
|
||
| @ExceptionHandler(ValidationException.class) | ||
| public ResponseEntity<Map<String, String>> handleValidation(ValidationException ex) { | ||
| log.error("ValidationException: {}", ex.getMessage(), ex); | ||
| String msg = ex.getMessage(); | ||
| if (msg != null && msg.toLowerCase().contains("not found")) { | ||
| return ResponseEntity.status(HttpStatus.NOT_FOUND) | ||
| .body(Map.of("error", msg)); | ||
| } | ||
| return ResponseEntity.status(HttpStatus.BAD_REQUEST) | ||
| .body(Map.of("error", msg)); | ||
| } | ||
|
|
||
| @ExceptionHandler(NotFoundException.class) | ||
| public ResponseEntity<Map<String, String>> handleNotFound(NotFoundException ex) { | ||
| log.error("NotFoundException: {}", ex.getMessage(), ex); | ||
| return ResponseEntity.status(HttpStatus.NOT_FOUND) | ||
| .body(Map.of("error", ex.getMessage())); | ||
| } | ||
|
|
||
| @ExceptionHandler(Exception.class) | ||
| public ResponseEntity<Map<String, String>> handleOther(Exception ex) { | ||
| log.error("Unexpected exception: {}", ex.getMessage(), ex); | ||
| return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) | ||
| .body(Map.of("error", "An unexpected error occurred")); | ||
| } | ||
| } | ||
132 changes: 92 additions & 40 deletions
132
src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,67 +1,119 @@ | ||
| package ru.yandex.practicum.filmorate.controller; | ||
|
|
||
| import jakarta.validation.Valid; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.beans.factory.annotation.Autowired; | ||
| import org.springframework.validation.annotation.Validated; | ||
| import org.springframework.web.bind.annotation.*; | ||
| import jakarta.validation.Valid; | ||
| import jakarta.validation.constraints.Positive; | ||
| import ru.yandex.practicum.filmorate.exception.ValidationException; | ||
| import ru.yandex.practicum.filmorate.exception.NotFoundException; | ||
| import ru.yandex.practicum.filmorate.model.User; | ||
| import org.springframework.web.bind.annotation.*; | ||
| import ru.yandex.practicum.filmorate.service.UserService; | ||
| import ru.yandex.practicum.filmorate.storage.InMemoryUserStorage; | ||
|
|
||
| import java.time.LocalDate; | ||
| import java.util.ArrayList; | ||
| import java.util.HashMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
|
|
||
| @RestController | ||
| @RequestMapping("/users") | ||
| @Slf4j | ||
| @Validated | ||
| public class UserController { | ||
| private final Map<Long, User> users = new HashMap<>(); | ||
| private Long idCounter = 1L; | ||
| private final UserService userService; | ||
|
|
||
| private void validateUser(User user) { | ||
| if (user.getEmail() == null || user.getEmail().isBlank() || !user.getEmail().contains("@")) { | ||
| log.warn("Электронная почта не может быть пустой и должна содержать символ @"); | ||
| throw new ValidationException("Электронная почта не может быть пустой и должна содержать символ @"); | ||
| } | ||
| if (user.getLogin() == null || user.getLogin().isBlank() || user.getLogin().contains(" ")) { | ||
| log.warn("Логин не может быть пустым и содержать пробелы"); | ||
| throw new ValidationException("Логин не может быть пустым и содержать пробелы"); | ||
| } | ||
| if (user.getName() == null || user.getName().isBlank()) { | ||
| user.setName(user.getLogin()); | ||
| log.info("Для пользователя {} установлено имя из логина", user.getLogin()); | ||
| } | ||
| if (user.getBirthday() == null || user.getBirthday().isAfter(LocalDate.now())) { | ||
| log.warn("Дата рождения не может быть в будущем"); | ||
| throw new ValidationException("Дата рождения не может быть в будущем"); | ||
| } | ||
| public UserController() { | ||
| this.userService = new UserService(new InMemoryUserStorage()); | ||
| } | ||
|
|
||
| @Autowired | ||
| public UserController(UserService userService) { | ||
| this.userService = userService; | ||
| } | ||
|
|
||
| @GetMapping | ||
| public List<User> getAllUsers() { | ||
| log.info("Fetching all users"); | ||
| return userService.getAllUsers(); | ||
| } | ||
|
|
||
| @GetMapping("/{id}") | ||
| public User getUser(@Positive @PathVariable Long id) { | ||
| log.info("Fetching user with id {}", id); | ||
| return userService.getUserById(id); | ||
| } | ||
|
|
||
| @PostMapping | ||
| public User createUser(@Valid @RequestBody User user) { | ||
| log.info("Creating user: {}", user); | ||
| validateUser(user); | ||
| user.setId(idCounter++); | ||
| users.put(user.getId(), user); | ||
| log.info("Создан пользователь: {}", user); | ||
| return user; | ||
| return userService.createUser(user); | ||
| } | ||
|
|
||
| @PutMapping | ||
| public User updateUser(@Valid @RequestBody User user) { | ||
| if (user.getId() == null || !users.containsKey(user.getId())) { | ||
| log.warn("Пользователь с id {} не найден", user.getId()); | ||
| throw new ValidationException("Пользователь с указанным id не существует"); | ||
| } | ||
| log.info("Updating user: {}", user); | ||
| validateUser(user); | ||
| users.put(user.getId(), user); | ||
| log.info("Обновлен пользователь: {}", user); | ||
| return user; | ||
| try { | ||
| return userService.updateUser(user); | ||
| } catch (NotFoundException e) { | ||
| log.warn("User not found: {}", user.getId()); | ||
| throw new ValidationException("User not found"); | ||
| } | ||
| } | ||
|
|
||
| @GetMapping | ||
| public List<User> getAllUsers() { | ||
| log.info("Получен запрос всех пользователей. Текущее количество: {}", users.size()); | ||
| return new ArrayList<>(users.values()); | ||
| @DeleteMapping("/{id}") | ||
| public void deleteUser(@Positive @PathVariable Long id) { | ||
| log.info("Deleting user with id {}", id); | ||
| userService.removeUser(id); | ||
| } | ||
|
|
||
| @PutMapping("/{id}/friends/{friendId}") | ||
| public void addFriend(@Positive @PathVariable Long id, | ||
| @Positive @PathVariable Long friendId) { | ||
| log.info("User {} adds friend {}", id, friendId); | ||
| userService.addFriend(id, friendId); | ||
| } | ||
|
|
||
| @DeleteMapping("/{id}/friends/{friendId}") | ||
| public void removeFriend(@Positive @PathVariable Long id, | ||
| @Positive @PathVariable Long friendId) { | ||
| log.info("User {} removes friend {}", id, friendId); | ||
| userService.removeFriend(id, friendId); | ||
| } | ||
|
|
||
| @GetMapping("/{id}/friends") | ||
| public List<User> getFriends(@Positive @PathVariable Long id) { | ||
| log.info("Fetching friends of user {}", id); | ||
| return userService.getFriends(id); | ||
| } | ||
|
|
||
| @GetMapping("/{id}/friends/common/{otherId}") | ||
| public List<User> getCommonFriends(@Positive @PathVariable Long id, | ||
| @Positive @PathVariable Long otherId) { | ||
| log.info("Fetching common friends of users {} and {}", id, otherId); | ||
| return userService.getCommonFriends(id, otherId); | ||
| } | ||
|
|
||
| private void validateUser(User user) { | ||
| if (user == null) { | ||
| throw new ValidationException("User cannot be null"); | ||
| } | ||
| if (user.getEmail() == null || user.getEmail().isBlank() || !user.getEmail().contains("@")) { | ||
| log.warn("Email is invalid"); | ||
| throw new ValidationException("Email is invalid"); | ||
| } | ||
| if (user.getLogin() == null || user.getLogin().isBlank() || user.getLogin().contains(" ")) { | ||
| log.warn("Login is invalid"); | ||
| throw new ValidationException("Login is invalid"); | ||
| } | ||
| if (user.getName() == null || user.getName().isBlank()) { | ||
| user.setName(user.getLogin()); | ||
| log.info("Name set to login for user {}", user.getLogin()); | ||
| } | ||
| if (user.getBirthday() == null || user.getBirthday().isAfter(LocalDate.now())) { | ||
| log.warn("Birthday is invalid"); | ||
| throw new ValidationException("Birthday is invalid"); | ||
| } | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
В класс хорошо бы добавить логирование. Это полезно при разборе логов. Помогает быстрее понять когда и в каком случае возникла та или иная ошибка.