From 8aafe48ccc5ebee1c6de7a3486b838f3ff7498ee Mon Sep 17 00:00:00 2001 From: AlexanderB777 Date: Sun, 5 May 2024 21:38:19 +0300 Subject: [PATCH 1/3] =?UTF-8?q?*=20=D0=9F=D1=80=D0=BE=D0=B5=D0=BA=D1=82=20?= =?UTF-8?q?10=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD=D1=82=D0=B0=20*=201=20?= =?UTF-8?q?=D0=98=D1=82=D0=B5=D1=80=D0=B0=D1=86=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 17 ++++++ .../filmorate/FilmorateApplication.java | 1 - .../annotations/AfterFirstFilmDate.java | 20 +++++++ .../filmorate/controller/FilmController.java | 38 +++++++++++- .../filmorate/controller/UserController.java | 43 +++++++++++++ .../practicum/filmorate/dao/FilmDao.java | 16 +++++ .../practicum/filmorate/dao/UserDao.java | 16 +++++ .../filmorate/dao/impl/InMemoryFilmDao.java | 50 ++++++++++++++++ .../filmorate/dao/impl/InMemoryUserDao.java | 52 ++++++++++++++++ .../exception/NotFoundException.java | 8 +++ .../exception/ValidationException.java | 7 +++ .../practicum/filmorate/model/Film.java | 24 ++++++-- .../practicum/filmorate/model/User.java | 22 +++++++ .../filmorate/service/FilmService.java | 53 ++++++++++++++++ .../filmorate/service/UserService.java | 60 +++++++++++++++++++ .../AfterFirstFilmDateValidator.java | 20 +++++++ .../filmorate/FilmorateApplicationTests.java | 1 + 17 files changed, 442 insertions(+), 6 deletions(-) create mode 100644 src/main/java/ru/yandex/practicum/filmorate/annotations/AfterFirstFilmDate.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/dao/FilmDao.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/dao/UserDao.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryFilmDao.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryUserDao.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/exception/NotFoundException.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/exception/ValidationException.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/model/User.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/service/UserService.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/validators/AfterFirstFilmDateValidator.java diff --git a/pom.xml b/pom.xml index 0cad0315..9222b016 100644 --- a/pom.xml +++ b/pom.xml @@ -32,6 +32,23 @@ spring-boot-starter-test test + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-aop + + + org.slf4j + slf4j-api + + + org.apache.commons + commons-lang3 + 3.13.0 + diff --git a/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java b/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java index dca451bc..e55e7102 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java +++ b/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java @@ -8,5 +8,4 @@ public class FilmorateApplication { public static void main(String[] args) { SpringApplication.run(FilmorateApplication.class, args); } - } diff --git a/src/main/java/ru/yandex/practicum/filmorate/annotations/AfterFirstFilmDate.java b/src/main/java/ru/yandex/practicum/filmorate/annotations/AfterFirstFilmDate.java new file mode 100644 index 00000000..2e5633eb --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/annotations/AfterFirstFilmDate.java @@ -0,0 +1,20 @@ +package ru.yandex.practicum.filmorate.annotations; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import ru.yandex.practicum.filmorate.validators.AfterFirstFilmDateValidator; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Constraint(validatedBy = AfterFirstFilmDateValidator.class) +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface AfterFirstFilmDate { + String message() default "Дата релиза не может быть раньше 28 декабря 1895 года"; + Class[] groups() default {}; + Class[] payload() default {}; + String date() default "1895-12-28"; +} 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 08cf0a18..cc36c9be 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java @@ -1,7 +1,43 @@ package ru.yandex.practicum.filmorate.controller; -import org.springframework.web.bind.annotation.RestController; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import ru.yandex.practicum.filmorate.exception.NotFoundException; +import ru.yandex.practicum.filmorate.model.Film; +import ru.yandex.practicum.filmorate.service.FilmService; + +import java.util.Collection; @RestController +@Slf4j +@RequestMapping("/films") +@RequiredArgsConstructor public class FilmController { + private final FilmService filmService; + + @GetMapping + public Collection findAll() { + log.info("Получен запрос на получение всех фильмов"); + return filmService.findAll(); + } + + @PostMapping + public Film create(@Valid @RequestBody Film film) { + log.info("Получен запрос на создание фильма: {}", film); + return filmService.createFilm(film); + } + + @PutMapping + public ResponseEntity update(@Valid @RequestBody Film film) { + log.info("Получен запрос на обновление фильма с ID: {}", film.getId()); + try { + Film persistedFilm = filmService.updateFilm(film); + return ResponseEntity.ok(persistedFilm); + } catch (NotFoundException notFoundException) { + return ResponseEntity.internalServerError().body(film); + } + } } diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java new file mode 100644 index 00000000..2bd4d46b --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java @@ -0,0 +1,43 @@ +package ru.yandex.practicum.filmorate.controller; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import ru.yandex.practicum.filmorate.exception.NotFoundException; +import ru.yandex.practicum.filmorate.model.User; +import ru.yandex.practicum.filmorate.service.UserService; + +import java.util.Collection; + +@RestController +@Slf4j +@RequestMapping("/users") +@RequiredArgsConstructor +public class UserController { + private final UserService userService; + + @GetMapping + public Collection findAll() { + log.info("Получен запрос на получение всех пользователей"); + return userService.findAll(); + } + + @PostMapping + public User create(@Valid @RequestBody User user) { + log.info("Получен запрос на создание пользователя: {}", user); + return userService.createUser(user); + } + + @PutMapping + public ResponseEntity update(@Valid @RequestBody User user) { + log.info("Получен запрос на обновление пользователя с ID: {}", user.getId()); + try { + User persistedUser = userService.updateUser(user); + return ResponseEntity.ok(persistedUser); + } catch (NotFoundException notFoundException) { + return ResponseEntity.internalServerError().body(user); + } + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/dao/FilmDao.java b/src/main/java/ru/yandex/practicum/filmorate/dao/FilmDao.java new file mode 100644 index 00000000..8c2759c9 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/dao/FilmDao.java @@ -0,0 +1,16 @@ +package ru.yandex.practicum.filmorate.dao; + +import ru.yandex.practicum.filmorate.model.Film; + +import java.util.Collection; +import java.util.Optional; + +public interface FilmDao { + Film save(Film film); + + Collection findAll(); + + Optional findById(Long id); + + Long findMaxId(); +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/dao/UserDao.java b/src/main/java/ru/yandex/practicum/filmorate/dao/UserDao.java new file mode 100644 index 00000000..23114ddb --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/dao/UserDao.java @@ -0,0 +1,16 @@ +package ru.yandex.practicum.filmorate.dao; + +import ru.yandex.practicum.filmorate.model.User; + +import java.util.Collection; +import java.util.Optional; + +public interface UserDao { + User save(User user); + + Collection findAll(); + + Optional findById(Long id); + + Long findMaxId(); +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryFilmDao.java b/src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryFilmDao.java new file mode 100644 index 00000000..4ae56a62 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryFilmDao.java @@ -0,0 +1,50 @@ +package ru.yandex.practicum.filmorate.dao.impl; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Repository; +import ru.yandex.practicum.filmorate.dao.FilmDao; +import ru.yandex.practicum.filmorate.model.Film; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +@Repository +@Slf4j +public class InMemoryFilmDao implements FilmDao { + private final Map films = new HashMap<>(); + + @Override + public Film save(Film film) { + log.info("Сохранение фильма с названием: {}", film.getName()); + films.put(film.getId(), film); + return film; + } + + @Override + public Collection findAll() { + log.info("Получение списка всех фильмов"); + return films.values(); + } + + + @Override + public Optional findById(Long id) { + log.debug("Поиск фильма по id: {}", id); + if (films.containsKey(id)) { + return Optional.of(films.get(id)); + } + return Optional.empty(); + } + + @Override + public Long findMaxId() { + log.debug("Поиск максимального значения Id среди существующих фильмов"); + return films.keySet() + .stream() + .mapToLong(id -> id) + .max() + .orElse(0); + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryUserDao.java b/src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryUserDao.java new file mode 100644 index 00000000..ac71d603 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryUserDao.java @@ -0,0 +1,52 @@ +package ru.yandex.practicum.filmorate.dao.impl; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Repository; +import ru.yandex.practicum.filmorate.dao.UserDao; +import ru.yandex.practicum.filmorate.model.User; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +@Repository +@Slf4j +public class InMemoryUserDao implements UserDao { + private final Map users = new HashMap<>(); + + @Override + public User save(User user) { + log.info("Сохранение пользователя: {}", user.getName()); + users.put(user.getId(), user); + return user; + } + + @Override + public Collection findAll() { + log.info("Получение списка всех пользователей"); + return users.values(); + } + + @Override + public Optional findById(Long id) { + log.debug("Поиск пользователя по id: {}", id); + if (!users.containsKey(id)) { + log.debug("Пользователя с ID: {} не существует", id); + return Optional.empty(); + } else { + log.info("Пользователь с ID {} найден", id); + return Optional.of(users.get(id)); + } + } + + @Override + public Long findMaxId() { + log.debug("Поиск максимального значения Id среди существующих пользователей"); + return users.keySet() + .stream() + .mapToLong(id -> id) + .max() + .orElse(0); + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/NotFoundException.java b/src/main/java/ru/yandex/practicum/filmorate/exception/NotFoundException.java new file mode 100644 index 00000000..bb4890d9 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/exception/NotFoundException.java @@ -0,0 +1,8 @@ +package ru.yandex.practicum.filmorate.exception; + +public class NotFoundException extends RuntimeException { + + public NotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/ValidationException.java b/src/main/java/ru/yandex/practicum/filmorate/exception/ValidationException.java new file mode 100644 index 00000000..52dc49c6 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/exception/ValidationException.java @@ -0,0 +1,7 @@ +package ru.yandex.practicum.filmorate.exception; + +public class ValidationException extends RuntimeException { + public ValidationException(String message) { + super(message); + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java index 3614a44b..d5ae18f5 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java @@ -1,12 +1,28 @@ package ru.yandex.practicum.filmorate.model; -import lombok.Getter; -import lombok.Setter; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.Size; +import lombok.Data; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ru.yandex.practicum.filmorate.annotations.AfterFirstFilmDate; + +import java.time.LocalDate; /** * Film. */ -@Getter -@Setter +@Data public class Film { + private static final Logger log = LoggerFactory.getLogger(Film.class); + private Long id; + @NotBlank(message = "Название не должно быть пустым") + private String name; + @Size(max = 200, message = "Превышена максимальная длина описания") + private String description; + @AfterFirstFilmDate + private LocalDate releaseDate; + @Positive(message = "Продолжительность должна быть положительным числом") + private int duration; } diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/User.java b/src/main/java/ru/yandex/practicum/filmorate/model/User.java new file mode 100644 index 00000000..f64317ba --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/model/User.java @@ -0,0 +1,22 @@ +package ru.yandex.practicum.filmorate.model; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Past; +import jakarta.validation.constraints.Pattern; +import lombok.Data; + +import java.time.LocalDate; + +@Data +public class User { + private Long id; + @Email + private String email; + @NotBlank + @Pattern(regexp = "^\\S+$", message = "Логин не может содержать пробелы") + private String login; + private String name; + @Past(message = "Дата рождения не может быть в будущем") + private LocalDate birthday; +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java new file mode 100644 index 00000000..91961be3 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java @@ -0,0 +1,53 @@ +package ru.yandex.practicum.filmorate.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import ru.yandex.practicum.filmorate.dao.FilmDao; +import ru.yandex.practicum.filmorate.exception.NotFoundException; +import ru.yandex.practicum.filmorate.model.Film; + +import java.util.Collection; +import java.util.Optional; + +@Service +@Slf4j +@RequiredArgsConstructor +public class FilmService { + private final FilmDao filmDao; + + public Collection findAll() { + log.info("Получен запрос на получение всех фильмов"); + return filmDao.findAll(); + } + + public Film createFilm(Film film) { + log.info("Получен запрос на создание фильма: {}", film); + film.setId(getNextId()); + film = filmDao.save(film); + return film; + } + + public Film updateFilm(Film film) throws NotFoundException { + + Long id = film.getId(); + Optional optionalFilm = filmDao.findById(id); + + if (optionalFilm.isEmpty()) { + log.warn("Фильм с ID {} не найден", id); + throw new NotFoundException("Нет такого фильма"); + } + + Film storedFilm = optionalFilm.get(); + storedFilm.setName(film.getName()); + storedFilm.setDescription(film.getDescription()); + storedFilm.setReleaseDate(film.getReleaseDate()); + storedFilm.setDuration(film.getDuration()); + log.info("Фильм успешно обновлен"); + return filmDao.save(storedFilm); + } + + private long getNextId() { + return filmDao.findMaxId() + 1; + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java b/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java new file mode 100644 index 00000000..07b83a64 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java @@ -0,0 +1,60 @@ +package ru.yandex.practicum.filmorate.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.coyote.Response; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import ru.yandex.practicum.filmorate.dao.UserDao; +import ru.yandex.practicum.filmorate.exception.NotFoundException; +import ru.yandex.practicum.filmorate.model.User; + +import java.util.Collection; +import java.util.Optional; + +@Service +@Slf4j +@RequiredArgsConstructor +public class UserService { + private final UserDao userDao; + + public Collection findAll() { + log.info("Получение списка всех пользователей"); + return userDao.findAll(); + } + + public User createUser(User user) { + user.setId(getNextId()); + if (StringUtils.isBlank(user.getName())) { + user.setName(user.getLogin()); + } + log.info("Создание нового пользователя: {}", user); + return userDao.save(user); + } + + public User updateUser(User user) { + Long id = user.getId(); + Optional optionalUser = userDao.findById(id); + + + if (optionalUser.isEmpty()) { + log.warn("Пользователь с ID {} не найден", id); + throw new NotFoundException("Пользователь не найден"); + } + + User storedUser = optionalUser.get(); + storedUser.setEmail(user.getEmail()); + storedUser.setLogin(user.getLogin()); + storedUser.setName(user.getName()); + storedUser.setBirthday(user.getBirthday()); + log.info("Обновление пользователя с ID {}", id); + return userDao.save(storedUser); + } + + private long getNextId() { + return userDao.findMaxId() + 1; + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/validators/AfterFirstFilmDateValidator.java b/src/main/java/ru/yandex/practicum/filmorate/validators/AfterFirstFilmDateValidator.java new file mode 100644 index 00000000..b652351d --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/validators/AfterFirstFilmDateValidator.java @@ -0,0 +1,20 @@ +package ru.yandex.practicum.filmorate.validators; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import ru.yandex.practicum.filmorate.annotations.AfterFirstFilmDate; + +import java.time.LocalDate; + +public class AfterFirstFilmDateValidator implements ConstraintValidator { + private LocalDate firstFilm; + @Override + public void initialize(AfterFirstFilmDate constraintAnnotation) { + firstFilm = LocalDate.parse(constraintAnnotation.date()); + } + + @Override + public boolean isValid(LocalDate localDate, ConstraintValidatorContext constraintValidatorContext) { + return localDate != null && localDate.isAfter(firstFilm); + } +} diff --git a/src/test/java/ru/yandex/practicum/filmorate/FilmorateApplicationTests.java b/src/test/java/ru/yandex/practicum/filmorate/FilmorateApplicationTests.java index 660412e8..b1aeed7f 100644 --- a/src/test/java/ru/yandex/practicum/filmorate/FilmorateApplicationTests.java +++ b/src/test/java/ru/yandex/practicum/filmorate/FilmorateApplicationTests.java @@ -10,4 +10,5 @@ class FilmorateApplicationTests { void contextLoads() { } + } From 94425b309ecaa5ccc3f9c95d48b2f4a5a64e7ecc Mon Sep 17 00:00:00 2001 From: AlexanderB777 Date: Mon, 6 May 2024 16:36:56 +0300 Subject: [PATCH 2/3] =?UTF-8?q?*=20=D0=9F=D1=80=D0=BE=D0=B5=D0=BA=D1=82=20?= =?UTF-8?q?10=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD=D1=82=D0=B0=20*=20=D0=A3?= =?UTF-8?q?=D1=81=D1=82=D1=80=D0=B0=D0=BD=D0=B5=D0=BD=D1=8B=20=D0=B7=D0=B0?= =?UTF-8?q?=D0=BC=D0=B5=D1=87=D0=B0=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../practicum/filmorate/dao/impl/InMemoryFilmDao.java | 4 ++-- .../practicum/filmorate/dao/impl/InMemoryUserDao.java | 6 +++--- src/main/java/ru/yandex/practicum/filmorate/model/Film.java | 4 ---- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryFilmDao.java b/src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryFilmDao.java index 4ae56a62..021d2418 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryFilmDao.java +++ b/src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryFilmDao.java @@ -31,7 +31,7 @@ public Collection findAll() { @Override public Optional findById(Long id) { - log.debug("Поиск фильма по id: {}", id); + log.info("Поиск фильма по id: {}", id); if (films.containsKey(id)) { return Optional.of(films.get(id)); } @@ -40,7 +40,7 @@ public Optional findById(Long id) { @Override public Long findMaxId() { - log.debug("Поиск максимального значения Id среди существующих фильмов"); + log.info("Поиск максимального значения Id среди существующих фильмов"); return films.keySet() .stream() .mapToLong(id -> id) diff --git a/src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryUserDao.java b/src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryUserDao.java index ac71d603..954bbcf7 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryUserDao.java +++ b/src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryUserDao.java @@ -30,9 +30,9 @@ public Collection findAll() { @Override public Optional findById(Long id) { - log.debug("Поиск пользователя по id: {}", id); + log.info("Поиск пользователя по id: {}", id); if (!users.containsKey(id)) { - log.debug("Пользователя с ID: {} не существует", id); + log.info("Пользователя с ID: {} не существует", id); return Optional.empty(); } else { log.info("Пользователь с ID {} найден", id); @@ -42,7 +42,7 @@ public Optional findById(Long id) { @Override public Long findMaxId() { - log.debug("Поиск максимального значения Id среди существующих пользователей"); + log.info("Поиск максимального значения Id среди существующих пользователей"); return users.keySet() .stream() .mapToLong(id -> id) diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java index d5ae18f5..d54d8798 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java @@ -10,12 +10,8 @@ import java.time.LocalDate; -/** - * Film. - */ @Data public class Film { - private static final Logger log = LoggerFactory.getLogger(Film.class); private Long id; @NotBlank(message = "Название не должно быть пустым") private String name; From 6da449dd26405a0ac702fdcddd1d6aee8810e62c Mon Sep 17 00:00:00 2001 From: AlexanderB777 Date: Thu, 18 Jul 2024 23:09:35 +0300 Subject: [PATCH 3/3] =?UTF-8?q?*=20=D0=9F=D1=80=D0=BE=D0=B5=D0=BA=D1=82=20?= =?UTF-8?q?11=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 5 ++ .../filmorate/controller/FilmController.java | 42 +++++++--- .../filmorate/controller/UserController.java | 56 +++++++++---- .../practicum/filmorate/dao/FilmDao.java | 16 ---- .../practicum/filmorate/dao/FilmStorage.java | 15 ++++ .../practicum/filmorate/dao/UserDao.java | 16 ---- .../practicum/filmorate/dao/UserStorage.java | 15 ++++ ...yFilmDao.java => InMemoryFilmStorage.java} | 23 ++--- ...yUserDao.java => InMemoryUserStorage.java} | 19 +++-- .../filmorate/exception/ErrorHandler.java | 18 ++++ .../filmorate/exception/ErrorResponse.java | 4 + .../exception/FilmNotFoundException.java | 8 ++ .../exception/NotFoundException.java | 9 +- .../exception/UserNotFoundException.java | 8 ++ .../exception/ValidationException.java | 7 -- .../practicum/filmorate/model/Film.java | 8 +- .../practicum/filmorate/model/User.java | 4 + .../filmorate/service/FilmService.java | 59 +++++++++---- .../filmorate/service/UserService.java | 84 +++++++++++++------ .../AfterFirstFilmDate.java | 2 +- .../filmorate/utils/FilmByLikeComparator.java | 12 +++ .../AfterFirstFilmDateValidator.java | 2 +- src/main/resources/application.properties | 3 +- 23 files changed, 297 insertions(+), 138 deletions(-) delete mode 100644 src/main/java/ru/yandex/practicum/filmorate/dao/FilmDao.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/dao/FilmStorage.java delete mode 100644 src/main/java/ru/yandex/practicum/filmorate/dao/UserDao.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/dao/UserStorage.java rename src/main/java/ru/yandex/practicum/filmorate/dao/impl/{InMemoryFilmDao.java => InMemoryFilmStorage.java} (67%) rename src/main/java/ru/yandex/practicum/filmorate/dao/impl/{InMemoryUserDao.java => InMemoryUserStorage.java} (74%) create mode 100644 src/main/java/ru/yandex/practicum/filmorate/exception/ErrorHandler.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/exception/ErrorResponse.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/exception/FilmNotFoundException.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/exception/UserNotFoundException.java delete mode 100644 src/main/java/ru/yandex/practicum/filmorate/exception/ValidationException.java rename src/main/java/ru/yandex/practicum/filmorate/{annotations => utils}/AfterFirstFilmDate.java (93%) create mode 100644 src/main/java/ru/yandex/practicum/filmorate/utils/FilmByLikeComparator.java diff --git a/pom.xml b/pom.xml index 9222b016..1c143d68 100644 --- a/pom.xml +++ b/pom.xml @@ -49,6 +49,11 @@ commons-lang3 3.13.0 + + org.zalando + logbook-spring-boot-starter + 3.9.0 + 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 cc36c9be..bfd08160 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java @@ -5,11 +5,11 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import ru.yandex.practicum.filmorate.exception.NotFoundException; import ru.yandex.practicum.filmorate.model.Film; import ru.yandex.practicum.filmorate.service.FilmService; import java.util.Collection; +import java.util.List; @RestController @Slf4j @@ -20,24 +20,46 @@ public class FilmController { @GetMapping public Collection findAll() { - log.info("Получен запрос на получение всех фильмов"); + log.debug("Получен запрос на получение всех фильмов"); return filmService.findAll(); } @PostMapping public Film create(@Valid @RequestBody Film film) { - log.info("Получен запрос на создание фильма: {}", film); + log.debug("Получен запрос на создание фильма: {}", film.getName()); return filmService.createFilm(film); } @PutMapping public ResponseEntity update(@Valid @RequestBody Film film) { - log.info("Получен запрос на обновление фильма с ID: {}", film.getId()); - try { - Film persistedFilm = filmService.updateFilm(film); - return ResponseEntity.ok(persistedFilm); - } catch (NotFoundException notFoundException) { - return ResponseEntity.internalServerError().body(film); - } + log.debug("Получен запрос на обновление фильма с ID: {}", film.getId()); + return ResponseEntity.ok(filmService.updateFilm(film)); + } + + @GetMapping("/popular") + public ResponseEntity> getTenPopularFilms() { + log.debug("Получен запрос на топ 10 фильмов"); + return ResponseEntity.ok(filmService.getPopularFilms(10)); + } + + + @GetMapping("/popular?count={count}") + public ResponseEntity> getPopularFilms(@RequestParam(defaultValue = "10") int count) { + log.debug("Получен запрос на получение списка популярный фильмов в количестве %d".formatted(count)); + return ResponseEntity.ok(filmService.getPopularFilms(count)); + } + + @PutMapping("/{id}/like/{userId}") + public ResponseEntity putLike(@PathVariable Long id, @PathVariable Long userId) { + log.debug("Получен запрос на создание лайка фильму с id=%d пользователем с Id=%d".formatted(id, userId)); + filmService.putLike(id, userId); + return ResponseEntity.noContent().build(); + } + + @DeleteMapping("/{id}/like/{userId}") + public ResponseEntity deleteLike(@PathVariable Long id, @PathVariable Long userId) { + log.debug("Получен запрос на удаление лайка фильму с id=%d пользователем с Id=%d".formatted(id, userId)); + filmService.deleteLike(id, userId); + return ResponseEntity.noContent().build(); } } diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java index 2bd4d46b..63ec050f 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java @@ -5,11 +5,10 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import ru.yandex.practicum.filmorate.exception.NotFoundException; import ru.yandex.practicum.filmorate.model.User; import ru.yandex.practicum.filmorate.service.UserService; -import java.util.Collection; +import java.util.List; @RestController @Slf4j @@ -19,25 +18,52 @@ public class UserController { private final UserService userService; @GetMapping - public Collection findAll() { - log.info("Получен запрос на получение всех пользователей"); - return userService.findAll(); + public ResponseEntity> findAll() { + log.debug("Получен запрос на получение всех пользователей"); + return ResponseEntity.ok(userService.findAll()); } @PostMapping - public User create(@Valid @RequestBody User user) { - log.info("Получен запрос на создание пользователя: {}", user); - return userService.createUser(user); + public ResponseEntity create(@Valid @RequestBody User user) { + log.debug("Получен запрос на создание пользователя: {}", user); + return ResponseEntity.ok(userService.createUser(user)); } @PutMapping public ResponseEntity update(@Valid @RequestBody User user) { - log.info("Получен запрос на обновление пользователя с ID: {}", user.getId()); - try { - User persistedUser = userService.updateUser(user); - return ResponseEntity.ok(persistedUser); - } catch (NotFoundException notFoundException) { - return ResponseEntity.internalServerError().body(user); - } + log.debug("Получен запрос на обновление пользователя с ID: {}", user.getId()); + User persistedUser = userService.updateUser(user); + return ResponseEntity.ok(persistedUser); + } + + @PutMapping("/{id}/friends/{friendId}") + public ResponseEntity createFriendship(@PathVariable Long id, @PathVariable Long friendId) { + log.debug( + "Получен запрос на создание дружбы пользователя с id %d с пользователем с id %d".formatted(id, friendId) + ); + userService.createFriendship(id, friendId); + return ResponseEntity.noContent().build(); + } + + @DeleteMapping("/{id}/friends/{friendId}") + public ResponseEntity removeFriendship(@PathVariable Long id, @PathVariable Long friendId) { + log.debug( + "Получен запрос на удаление дружбы пользователя с id %d с пользователем с id %d".formatted(id, friendId) + ); + userService.removeFriendship(id, friendId); + return ResponseEntity.noContent().build(); + } + + @GetMapping("/{id}/friends") + public ResponseEntity> showFriends(@PathVariable Long id) { + log.debug("Получен запрос на отображение всех друзей пользователя с id " + id); + return userService.getAllFriendsFromUser(id); + } + + @GetMapping("/{id}/friends/common/{friendId}") + public ResponseEntity> showCommonFriends(@PathVariable Long id, @PathVariable Long friendId) { + log.debug(("Получен запрос на отображение списка общих друзей " + + "пользователя с id %d, и пользователя с id %d").formatted(id, friendId)); + return userService.getCommonFriends(id, friendId); } } diff --git a/src/main/java/ru/yandex/practicum/filmorate/dao/FilmDao.java b/src/main/java/ru/yandex/practicum/filmorate/dao/FilmDao.java deleted file mode 100644 index 8c2759c9..00000000 --- a/src/main/java/ru/yandex/practicum/filmorate/dao/FilmDao.java +++ /dev/null @@ -1,16 +0,0 @@ -package ru.yandex.practicum.filmorate.dao; - -import ru.yandex.practicum.filmorate.model.Film; - -import java.util.Collection; -import java.util.Optional; - -public interface FilmDao { - Film save(Film film); - - Collection findAll(); - - Optional findById(Long id); - - Long findMaxId(); -} diff --git a/src/main/java/ru/yandex/practicum/filmorate/dao/FilmStorage.java b/src/main/java/ru/yandex/practicum/filmorate/dao/FilmStorage.java new file mode 100644 index 00000000..6326b48e --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/dao/FilmStorage.java @@ -0,0 +1,15 @@ +package ru.yandex.practicum.filmorate.dao; + +import ru.yandex.practicum.filmorate.model.Film; + +import java.util.List; + +public interface FilmStorage { + Film save(Film film); + + List findAll(); + + Film findById(Long id); + + Long findMaxId(); +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/dao/UserDao.java b/src/main/java/ru/yandex/practicum/filmorate/dao/UserDao.java deleted file mode 100644 index 23114ddb..00000000 --- a/src/main/java/ru/yandex/practicum/filmorate/dao/UserDao.java +++ /dev/null @@ -1,16 +0,0 @@ -package ru.yandex.practicum.filmorate.dao; - -import ru.yandex.practicum.filmorate.model.User; - -import java.util.Collection; -import java.util.Optional; - -public interface UserDao { - User save(User user); - - Collection findAll(); - - Optional findById(Long id); - - Long findMaxId(); -} diff --git a/src/main/java/ru/yandex/practicum/filmorate/dao/UserStorage.java b/src/main/java/ru/yandex/practicum/filmorate/dao/UserStorage.java new file mode 100644 index 00000000..2a7f4e0c --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/dao/UserStorage.java @@ -0,0 +1,15 @@ +package ru.yandex.practicum.filmorate.dao; + +import ru.yandex.practicum.filmorate.model.User; + +import java.util.List; + +public interface UserStorage { + User save(User user); + + List findAll(); + + User findById(Long id); + + Long findMaxId(); +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryFilmDao.java b/src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryFilmStorage.java similarity index 67% rename from src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryFilmDao.java rename to src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryFilmStorage.java index 021d2418..0553ecc6 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryFilmDao.java +++ b/src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryFilmStorage.java @@ -2,19 +2,21 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Repository; -import ru.yandex.practicum.filmorate.dao.FilmDao; +import ru.yandex.practicum.filmorate.dao.FilmStorage; +import ru.yandex.practicum.filmorate.exception.FilmNotFoundException; import ru.yandex.practicum.filmorate.model.Film; -import java.util.Collection; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; -import java.util.Optional; @Repository @Slf4j -public class InMemoryFilmDao implements FilmDao { +public class InMemoryFilmStorage implements FilmStorage { private final Map films = new HashMap<>(); + @Override public Film save(Film film) { log.info("Сохранение фильма с названием: {}", film.getName()); @@ -23,19 +25,18 @@ public Film save(Film film) { } @Override - public Collection findAll() { + public List findAll() { log.info("Получение списка всех фильмов"); - return films.values(); + System.out.println(films.values()); + return new ArrayList<>(films.values()); } @Override - public Optional findById(Long id) { + public Film findById(Long id) { log.info("Поиск фильма по id: {}", id); - if (films.containsKey(id)) { - return Optional.of(films.get(id)); - } - return Optional.empty(); + if (!films.containsKey(id)) throw new FilmNotFoundException(id); + return films.get(id); } @Override diff --git a/src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryUserDao.java b/src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryUserStorage.java similarity index 74% rename from src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryUserDao.java rename to src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryUserStorage.java index 954bbcf7..3b53a803 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryUserDao.java +++ b/src/main/java/ru/yandex/practicum/filmorate/dao/impl/InMemoryUserStorage.java @@ -2,17 +2,18 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Repository; -import ru.yandex.practicum.filmorate.dao.UserDao; +import ru.yandex.practicum.filmorate.dao.UserStorage; +import ru.yandex.practicum.filmorate.exception.UserNotFoundException; import ru.yandex.practicum.filmorate.model.User; -import java.util.Collection; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; -import java.util.Optional; @Repository @Slf4j -public class InMemoryUserDao implements UserDao { +public class InMemoryUserStorage implements UserStorage { private final Map users = new HashMap<>(); @Override @@ -23,20 +24,20 @@ public User save(User user) { } @Override - public Collection findAll() { + public List findAll() { log.info("Получение списка всех пользователей"); - return users.values(); + return new ArrayList<>(users.values()); } @Override - public Optional findById(Long id) { + public User findById(Long id) { log.info("Поиск пользователя по id: {}", id); if (!users.containsKey(id)) { log.info("Пользователя с ID: {} не существует", id); - return Optional.empty(); + throw new UserNotFoundException(id); } else { log.info("Пользователь с ID {} найден", id); - return Optional.of(users.get(id)); + return users.get(id); } } diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/ErrorHandler.java b/src/main/java/ru/yandex/practicum/filmorate/exception/ErrorHandler.java new file mode 100644 index 00000000..13f49579 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/exception/ErrorHandler.java @@ -0,0 +1,18 @@ +package ru.yandex.practicum.filmorate.exception; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@Slf4j +@RestControllerAdvice +public class ErrorHandler { + @ExceptionHandler(NotFoundException.class) + @ResponseStatus(HttpStatus.NOT_FOUND) + public ErrorResponse handleNotFoundException(final NotFoundException e) { + log.warn("Ошибка Not Found Exception"); + return new ErrorResponse("Ошибка NotFoundException", e.getMessage()); + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/ErrorResponse.java b/src/main/java/ru/yandex/practicum/filmorate/exception/ErrorResponse.java new file mode 100644 index 00000000..88cf6b23 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/exception/ErrorResponse.java @@ -0,0 +1,4 @@ +package ru.yandex.practicum.filmorate.exception; + +public record ErrorResponse(String error, String description) { +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/FilmNotFoundException.java b/src/main/java/ru/yandex/practicum/filmorate/exception/FilmNotFoundException.java new file mode 100644 index 00000000..00b610f0 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/exception/FilmNotFoundException.java @@ -0,0 +1,8 @@ +package ru.yandex.practicum.filmorate.exception; + +public class FilmNotFoundException extends NotFoundException { + public FilmNotFoundException(Long id) { + super(id); + message = "Фильм с id=%d не найден".formatted(id); + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/NotFoundException.java b/src/main/java/ru/yandex/practicum/filmorate/exception/NotFoundException.java index bb4890d9..d3c1541c 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/exception/NotFoundException.java +++ b/src/main/java/ru/yandex/practicum/filmorate/exception/NotFoundException.java @@ -1,8 +1,9 @@ package ru.yandex.practicum.filmorate.exception; -public class NotFoundException extends RuntimeException { - - public NotFoundException(String message) { - super(message); +public class NotFoundException extends RuntimeException{ + long id; + String message; + public NotFoundException(long id) { + this.id = id; } } diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/UserNotFoundException.java b/src/main/java/ru/yandex/practicum/filmorate/exception/UserNotFoundException.java new file mode 100644 index 00000000..a8e5345d --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/exception/UserNotFoundException.java @@ -0,0 +1,8 @@ +package ru.yandex.practicum.filmorate.exception; + +public class UserNotFoundException extends NotFoundException { + public UserNotFoundException(Long id) { + super(id); + message = "Пользователь с id=%d не найден".formatted(id); + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/ValidationException.java b/src/main/java/ru/yandex/practicum/filmorate/exception/ValidationException.java deleted file mode 100644 index 52dc49c6..00000000 --- a/src/main/java/ru/yandex/practicum/filmorate/exception/ValidationException.java +++ /dev/null @@ -1,7 +0,0 @@ -package ru.yandex.practicum.filmorate.exception; - -public class ValidationException extends RuntimeException { - public ValidationException(String message) { - super(message); - } -} diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java index d54d8798..7442ae08 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java @@ -4,11 +4,11 @@ import jakarta.validation.constraints.Positive; import jakarta.validation.constraints.Size; import lombok.Data; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ru.yandex.practicum.filmorate.annotations.AfterFirstFilmDate; +import ru.yandex.practicum.filmorate.utils.AfterFirstFilmDate; import java.time.LocalDate; +import java.util.HashSet; +import java.util.Set; @Data public class Film { @@ -21,4 +21,6 @@ public class Film { private LocalDate releaseDate; @Positive(message = "Продолжительность должна быть положительным числом") private int duration; + + private Set likes = new HashSet<>(); } diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/User.java b/src/main/java/ru/yandex/practicum/filmorate/model/User.java index f64317ba..6a6fdb65 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/User.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/User.java @@ -7,6 +7,8 @@ import lombok.Data; import java.time.LocalDate; +import java.util.HashSet; +import java.util.Set; @Data public class User { @@ -19,4 +21,6 @@ public class User { private String name; @Past(message = "Дата рождения не может быть в будущем") private LocalDate birthday; + + private Set friends = new HashSet<>(); } diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java index 91961be3..f7381724 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java @@ -3,51 +3,74 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import ru.yandex.practicum.filmorate.dao.FilmDao; -import ru.yandex.practicum.filmorate.exception.NotFoundException; +import ru.yandex.practicum.filmorate.dao.FilmStorage; +import ru.yandex.practicum.filmorate.dao.UserStorage; import ru.yandex.practicum.filmorate.model.Film; +import ru.yandex.practicum.filmorate.utils.FilmByLikeComparator; import java.util.Collection; -import java.util.Optional; +import java.util.List; @Service @Slf4j @RequiredArgsConstructor public class FilmService { - private final FilmDao filmDao; + private final FilmStorage filmStorage; + private final UserStorage userStorage; public Collection findAll() { log.info("Получен запрос на получение всех фильмов"); - return filmDao.findAll(); + return filmStorage.findAll(); } public Film createFilm(Film film) { log.info("Получен запрос на создание фильма: {}", film); film.setId(getNextId()); - film = filmDao.save(film); + film = filmStorage.save(film); return film; } - public Film updateFilm(Film film) throws NotFoundException { - + public Film updateFilm(Film film) { Long id = film.getId(); - Optional optionalFilm = filmDao.findById(id); - - if (optionalFilm.isEmpty()) { - log.warn("Фильм с ID {} не найден", id); - throw new NotFoundException("Нет такого фильма"); - } - - Film storedFilm = optionalFilm.get(); + log.info("Получен запрос на обновление фильма с Id=%d".formatted(id)); + Film storedFilm = filmStorage.findById(id); + log.info("Фильм с id=%d найден".formatted(id)); storedFilm.setName(film.getName()); storedFilm.setDescription(film.getDescription()); storedFilm.setReleaseDate(film.getReleaseDate()); storedFilm.setDuration(film.getDuration()); log.info("Фильм успешно обновлен"); - return filmDao.save(storedFilm); + return filmStorage.save(storedFilm); } private long getNextId() { - return filmDao.findMaxId() + 1; + return filmStorage.findMaxId() + 1; + } + + public List getPopularFilms(int count) { + log.info("Вызван метод по поиску популярных фильмов в количестве %d".formatted(count)); + List all = filmStorage.findAll(); + List result = all.stream() + .sorted(new FilmByLikeComparator().reversed()) + .limit(count) + .toList(); + log.info("Список фильмов успешно сформирован"); + return result; + } + + public void putLike(Long id, Long userId) { + log.info("Вызван метод по добавлению лайка фильму с id=%d пользователем с id=%d".formatted(id, userId)); + userStorage.findById(userId); + log.info("Фильм найден"); + filmStorage.findById(id).getLikes().add(userId); + log.info("Лайк добавлен"); + } + + public void deleteLike(Long id, Long userId) { + log.info("Вызван метод по удалению лайка фильму с id=%d пользователем с id=%d".formatted(id, userId)); + userStorage.findById(userId); + log.info("Фильм найден"); + filmStorage.findById(id).getLikes().remove(userId); + log.info("Лайк удален"); } } diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java b/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java index 07b83a64..bd7c12f1 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java @@ -3,27 +3,24 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.apache.coyote.Response; -import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.ResponseStatus; -import ru.yandex.practicum.filmorate.dao.UserDao; -import ru.yandex.practicum.filmorate.exception.NotFoundException; +import ru.yandex.practicum.filmorate.dao.UserStorage; import ru.yandex.practicum.filmorate.model.User; -import java.util.Collection; -import java.util.Optional; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; @Service @Slf4j @RequiredArgsConstructor public class UserService { - private final UserDao userDao; + private final UserStorage userStorage; - public Collection findAll() { + public List findAll() { log.info("Получение списка всех пользователей"); - return userDao.findAll(); + return userStorage.findAll(); } public User createUser(User user) { @@ -32,29 +29,64 @@ public User createUser(User user) { user.setName(user.getLogin()); } log.info("Создание нового пользователя: {}", user); - return userDao.save(user); + return userStorage.save(user); } public User updateUser(User user) { Long id = user.getId(); - Optional optionalUser = userDao.findById(id); + User foundedUser = userStorage.findById(id); + foundedUser.setEmail(user.getEmail()); + foundedUser.setLogin(user.getLogin()); + foundedUser.setName(user.getName()); + foundedUser.setBirthday(user.getBirthday()); + log.info("Обновление пользователя с ID {}", id); + return userStorage.save(foundedUser); + } + private long getNextId() { + return userStorage.findMaxId() + 1; + } - if (optionalUser.isEmpty()) { - log.warn("Пользователь с ID {} не найден", id); - throw new NotFoundException("Пользователь не найден"); - } + public void createFriendship(Long id, Long friendId) { + log.info("Вызван метод создающий дружбу между пользователями с Id=%d и Id=%d".formatted(id, friendId)); + User user = userStorage.findById(id); + log.info("Пользователь с id=%d найден".formatted(id)); + User friend = userStorage.findById(friendId); + log.info("Пользователь с id=%d найден".formatted(friendId)); + user.getFriends().add(friendId); + friend.getFriends().add(id); + log.info("Дружба создана"); + } - User storedUser = optionalUser.get(); - storedUser.setEmail(user.getEmail()); - storedUser.setLogin(user.getLogin()); - storedUser.setName(user.getName()); - storedUser.setBirthday(user.getBirthday()); - log.info("Обновление пользователя с ID {}", id); - return userDao.save(storedUser); + public void removeFriendship(Long id, Long friendId) { + log.info("Вызван метод удаляющий дружбу между пользователями с Id=%d и Id=%d".formatted(id, friendId)); + User user = userStorage.findById(id); + log.info("Пользователь с id=%d найден".formatted(id)); + User friend = userStorage.findById(friendId); + log.info("Пользователь с id=%d найден".formatted(friendId)); + user.getFriends().remove(friendId); + friend.getFriends().remove(id); + log.info("Дружба удалена"); } - private long getNextId() { - return userDao.findMaxId() + 1; + public ResponseEntity> getAllFriendsFromUser(Long id) { + log.info("Вызван метод для получения списка друзей пользователя с id=%d".formatted(id)); + User user = userStorage.findById(id); + log.info("Пользователь с id=%d найден".formatted(id)); + Set friends = user.getFriends(); + if (friends.isEmpty()) return ResponseEntity.ok(new ArrayList<>()); + return ResponseEntity.ok( + user.getFriends().stream() + .map(userStorage::findById) + .toList()); + } + + public ResponseEntity> getCommonFriends(Long id, Long friendId) { + log.info("Вызван метод по поиску общих друзей пользователей с id=%d и id=%d".formatted(id, friendId)); + Set commonFriends = userStorage.findById(id).getFriends(); + commonFriends.retainAll(userStorage.findById(friendId).getFriends()); + return ResponseEntity.ok(commonFriends.stream(). + map(userStorage::findById) + .toList()); } } diff --git a/src/main/java/ru/yandex/practicum/filmorate/annotations/AfterFirstFilmDate.java b/src/main/java/ru/yandex/practicum/filmorate/utils/AfterFirstFilmDate.java similarity index 93% rename from src/main/java/ru/yandex/practicum/filmorate/annotations/AfterFirstFilmDate.java rename to src/main/java/ru/yandex/practicum/filmorate/utils/AfterFirstFilmDate.java index 2e5633eb..3153193d 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/annotations/AfterFirstFilmDate.java +++ b/src/main/java/ru/yandex/practicum/filmorate/utils/AfterFirstFilmDate.java @@ -1,4 +1,4 @@ -package ru.yandex.practicum.filmorate.annotations; +package ru.yandex.practicum.filmorate.utils; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/src/main/java/ru/yandex/practicum/filmorate/utils/FilmByLikeComparator.java b/src/main/java/ru/yandex/practicum/filmorate/utils/FilmByLikeComparator.java new file mode 100644 index 00000000..095c9b98 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/utils/FilmByLikeComparator.java @@ -0,0 +1,12 @@ +package ru.yandex.practicum.filmorate.utils; + +import ru.yandex.practicum.filmorate.model.Film; + +import java.util.Comparator; + +public class FilmByLikeComparator implements Comparator { + @Override + public int compare(Film film1, Film film2) { + return Integer.compare(film1.getLikes().size(), film2.getLikes().size()); + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/validators/AfterFirstFilmDateValidator.java b/src/main/java/ru/yandex/practicum/filmorate/validators/AfterFirstFilmDateValidator.java index b652351d..075d92bf 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/validators/AfterFirstFilmDateValidator.java +++ b/src/main/java/ru/yandex/practicum/filmorate/validators/AfterFirstFilmDateValidator.java @@ -2,7 +2,7 @@ import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; -import ru.yandex.practicum.filmorate.annotations.AfterFirstFilmDate; +import ru.yandex.practicum.filmorate.utils.AfterFirstFilmDate; import java.time.LocalDate; diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8b137891..aa0ada85 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,2 @@ - +logging.level.org.zalando.logbook= TRACE +logging.level.ru.yandex.practicum.filmorate.controller=DEBUG