diff --git a/pom.xml b/pom.xml
index 00a0703..9c95881 100644
--- a/pom.xml
+++ b/pom.xml
@@ -38,6 +38,13 @@
org.springframework.boot
spring-boot-starter-validation
+
+
+ org.zalando
+ logbook-spring-boot-starter
+ 3.7.2
+
+
diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java
index f51bdc0..068d66f 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java
@@ -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 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 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 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 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");
+ }
}
}
+
+
diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/GlobalExceptionHandler.java b/src/main/java/ru/yandex/practicum/filmorate/controller/GlobalExceptionHandler.java
new file mode 100644
index 0000000..adc883f
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/GlobalExceptionHandler.java
@@ -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