From 67907904e3b9708ed2e45b5c017d6a4dc01ad0f8 Mon Sep 17 00:00:00 2001 From: vs Date: Thu, 19 Jun 2025 00:56:27 +0300 Subject: [PATCH 1/2] =?UTF-8?q?=D0=92=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=BE=20=D1=84=D0=B8=D0=BD=D0=B0=D0=BB=D1=8C=D0=BD=D0=BE?= =?UTF-8?q?=D0=B5=20=D0=B7=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8=D0=B5=2011.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 97 +++++++----- .../filmorate/FilmorateApplication.java | 9 +- .../filmorate/controller/FilmController.java | 47 +++++- .../filmorate/controller/UserController.java | 54 +++++++ .../ApplicationExceptionHandler.java | 32 ++++ .../filmorate/exception/ErrorResponse.java | 15 ++ .../exception/ExceptionNotFound.java | 7 + .../filmorate/json/DeserializerDuration.java | 19 +++ .../filmorate/json/SerializerDuration.java | 19 +++ .../practicum/filmorate/model/Film.java | 38 ++++- .../practicum/filmorate/model/User.java | 28 ++++ .../filmorate/service/FilmService.java | 53 +++++++ .../filmorate/service/UserService.java | 60 ++++++++ .../filmorate/storage/film/FilmStorage.java | 25 ++++ .../storage/film/InMemoryFilmStorage.java | 124 ++++++++++++++++ .../storage/user/InMemoryUserStorage.java | 132 +++++++++++++++++ .../filmorate/storage/user/UserStorage.java | 29 ++++ .../filmorate/validation/MinimumDate.java | 21 +++ .../validation/MinimumDateValidator.java | 20 +++ .../validation/PositiveDuration.java | 20 +++ .../validation/PositiveDurationValidator.java | 15 ++ src/main/resources/application.properties | 2 +- .../filmorate/FilmorateApplicationTests.java | 8 +- .../filmorate/ValidationFilmTest.java | 139 ++++++++++++++++++ .../filmorate/ValidationUserTest.java | 119 +++++++++++++++ 25 files changed, 1073 insertions(+), 59 deletions(-) create mode 100644 src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/exception/ApplicationExceptionHandler.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/ExceptionNotFound.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/json/DeserializerDuration.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/json/SerializerDuration.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/storage/film/FilmStorage.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/storage/film/InMemoryFilmStorage.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/storage/user/InMemoryUserStorage.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/storage/user/UserStorage.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/validation/MinimumDate.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/validation/MinimumDateValidator.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/validation/PositiveDuration.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/validation/PositiveDurationValidator.java create mode 100644 src/test/java/ru/yandex/practicum/filmorate/ValidationFilmTest.java create mode 100644 src/test/java/ru/yandex/practicum/filmorate/ValidationUserTest.java diff --git a/pom.xml b/pom.xml index 0cad031..6ad9433 100644 --- a/pom.xml +++ b/pom.xml @@ -1,46 +1,63 @@ - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 3.2.4 - - - ru.yandex.practicum - filmorate - 0.0.1-SNAPSHOT - filmorate - filmorate - - 21 - - - - org.springframework.boot - spring-boot-starter-web - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.2.4 + + + ru.yandex.practicum + filmorate + 0.0.1-SNAPSHOT + filmorate + filmorate + + 21 + + + + org.springframework.boot + spring-boot-starter-web + - - org.projectlombok - lombok - provided - - - org.springframework.boot - spring-boot-starter-test - test - - + + org.projectlombok + lombok + provided + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-validation + + + org.zalando + logbook-spring-boot-starter + 3.7.2 + + - - - - org.springframework.boot - spring-boot-maven-plugin - - - + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + -XX:+EnableDynamicAgentLoading + + + + diff --git a/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java b/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java index dca451b..2e4a618 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java +++ b/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java @@ -5,8 +5,7 @@ @SpringBootApplication public class FilmorateApplication { - public static void main(String[] args) { - SpringApplication.run(FilmorateApplication.class, args); - } - -} + public static void main(String[] args) { + SpringApplication.run(FilmorateApplication.class, args); + } +} \ No newline at end of file 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 08cf0a1..ed7be62 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,52 @@ package ru.yandex.practicum.filmorate.controller; -import org.springframework.web.bind.annotation.RestController; +import jakarta.validation.Valid; +import jakarta.validation.constraints.Positive; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import ru.yandex.practicum.filmorate.service.FilmService; +import ru.yandex.practicum.filmorate.model.Film; + +import java.util.Collection; +import java.util.List; @RestController +@RequiredArgsConstructor +@RequestMapping("/films") public class FilmController { + + private final FilmService filmService; + + @GetMapping + public Collection getFilms() { + return filmService.getFilms(); + } + + @PostMapping + public Film createFilm(@Valid @RequestBody Film film) { + return filmService.createFilm(film); + } + + @PutMapping + public Film updateFilm(@Valid @RequestBody Film film) { + return filmService.updateFilm(film); + } + + @PutMapping("/{id}/like/{userId}") + public void addLike(@PathVariable Long id, @PathVariable Long userId) { + filmService.addLike(id, userId); + } + + @DeleteMapping("/{id}/like/{userId}") + public void removeLike(@PathVariable Long id, @PathVariable Long userId) { + filmService.removeLike(id, userId); + } + + @Validated + @GetMapping("/popular") + public List getPopularFilms(@Positive @RequestParam(defaultValue = "10") int count) { + return filmService.getPopularFilms(count); + } + } 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 0000000..189c38e --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java @@ -0,0 +1,54 @@ +package ru.yandex.practicum.filmorate.controller; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; +import ru.yandex.practicum.filmorate.service.UserService; +import ru.yandex.practicum.filmorate.model.User; + +import java.util.Collection; +import java.util.Optional; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/users") +public class UserController { + + private final UserService userService; + + @GetMapping + public Collection getUsers() { + return userService.getUsers(); + } + + @PostMapping + public User createFilm(@Valid @RequestBody User user) { + return userService.createUser(user); + } + + @PutMapping + public User updateUser(@Valid @RequestBody User user) { + return userService.updateUser(user); + } + + @PutMapping("/{id}/friends/{friendId}") + public void addFriend(@PathVariable Long id, @PathVariable Long friendId) { + userService.addFriend(id, friendId); + } + + @DeleteMapping("/{id}/friends/{friendId}") + public void deleteFriend(@PathVariable Long id, @PathVariable Long friendId) { + userService.removeFriend(id, friendId); + } + + @GetMapping("/{id}/friends") + public Collection getFriends(@PathVariable Long id) { + return userService.getFriends(id); + } + + @GetMapping("/{id}/friends/common/{otherId}") + public Collection> getCommonFriends(@PathVariable Long id, @PathVariable Long otherId) { + return userService.getCommonFriends(id, otherId); + } + +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/ApplicationExceptionHandler.java b/src/main/java/ru/yandex/practicum/filmorate/exception/ApplicationExceptionHandler.java new file mode 100644 index 0000000..5250509 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/exception/ApplicationExceptionHandler.java @@ -0,0 +1,32 @@ +package ru.yandex.practicum.filmorate.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class ApplicationExceptionHandler { + + @ExceptionHandler(MethodArgumentNotValidException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorResponse handleIncorrectParameter(final MethodArgumentNotValidException e) { + return new ErrorResponse( + "Ошибка валидации.", e.getMessage() + ); + } + + @ExceptionHandler(ExceptionNotFound.class) + @ResponseStatus(HttpStatus.NOT_FOUND) + public ErrorResponse handleObjectNotFound() { + return new ErrorResponse("Запрошенный объект не найден.", "Такой страницы не существует или запрошенный объект не найден."); + } + + @ExceptionHandler(Exception.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ErrorResponse handleException() { + return new ErrorResponse("Внутренняя ошибка сервера.", "Попробуйте обратиться позже."); + } + +} \ No newline at end of file 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 0000000..e04a4ac --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/exception/ErrorResponse.java @@ -0,0 +1,15 @@ +package ru.yandex.practicum.filmorate.exception; + +import lombok.Getter; + +@Getter +public class ErrorResponse { + private final String error; + private final String description; + + public ErrorResponse(String error, String description) { + this.error = error; + this.description = description; + } + +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/ExceptionNotFound.java b/src/main/java/ru/yandex/practicum/filmorate/exception/ExceptionNotFound.java new file mode 100644 index 0000000..0062a08 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/exception/ExceptionNotFound.java @@ -0,0 +1,7 @@ +package ru.yandex.practicum.filmorate.exception; + +public class ExceptionNotFound extends RuntimeException { + public ExceptionNotFound(String message) { + super(message); + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/json/DeserializerDuration.java b/src/main/java/ru/yandex/practicum/filmorate/json/DeserializerDuration.java new file mode 100644 index 0000000..1bfaf1b --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/json/DeserializerDuration.java @@ -0,0 +1,19 @@ +package ru.yandex.practicum.filmorate.json; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; + +import java.io.IOException; +import java.time.Duration; + +public class DeserializerDuration extends JsonDeserializer { + @Override + public Duration deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException { + if (jsonParser.getCurrentToken() == JsonToken.VALUE_NULL) { + return null; + } + return Duration.ofMinutes(jsonParser.getLongValue()); + } +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/json/SerializerDuration.java b/src/main/java/ru/yandex/practicum/filmorate/json/SerializerDuration.java new file mode 100644 index 0000000..bd984c3 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/json/SerializerDuration.java @@ -0,0 +1,19 @@ +package ru.yandex.practicum.filmorate.json; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; +import java.time.Duration; + +public class SerializerDuration extends JsonSerializer { + @Override + public void serialize(Duration value, JsonGenerator jsonGenerator, SerializerProvider serializers) throws IOException { + if (value == null) { + jsonGenerator.writeNull(); + } else { + jsonGenerator.writeNumber(value.toMinutes()); + } + } +} \ No newline at end of file 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 3614a44..f5cf2c0 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,34 @@ package ru.yandex.practicum.filmorate.model; -import lombok.Getter; -import lombok.Setter; - -/** - * Film. - */ -@Getter -@Setter +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import ru.yandex.practicum.filmorate.json.DeserializerDuration; +import ru.yandex.practicum.filmorate.json.SerializerDuration; +import ru.yandex.practicum.filmorate.validation.MinimumDate; +import ru.yandex.practicum.filmorate.validation.PositiveDuration; +import lombok.Data; + +import java.time.Duration; +import java.time.LocalDate; +import java.util.HashSet; +import java.util.Set; + +@Data public class Film { + + private Long id; + @NotBlank + private String name; + @Size(max = 200) + private String description; + @MinimumDate + private LocalDate releaseDate; + @PositiveDuration + @JsonSerialize(using = SerializerDuration.class) + @JsonDeserialize(using = DeserializerDuration.class) + private Duration duration; + private final 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 new file mode 100644 index 0000000..4e90863 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/model/User.java @@ -0,0 +1,28 @@ +package ru.yandex.practicum.filmorate.model; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.PastOrPresent; +import jakarta.validation.constraints.Pattern; +import lombok.Data; + +import java.time.LocalDate; +import java.util.HashSet; +import java.util.Set; + +@Data +public class User { + + private Long id; + @NotBlank + @Email + private String email; + @NotBlank + @Pattern(regexp = "(?=\\S+$).+", message = "Логин не должен содержать пробелы.") + private String login; + private String name; + @PastOrPresent + private LocalDate birthday; + private final 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 new file mode 100644 index 0000000..8d54f38 --- /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 org.springframework.stereotype.Service; +import ru.yandex.practicum.filmorate.exception.ExceptionNotFound; +import ru.yandex.practicum.filmorate.model.Film; +import ru.yandex.practicum.filmorate.storage.film.FilmStorage; + +import java.util.Collection; +import java.util.List; + +@RequiredArgsConstructor +@Service +public class FilmService { + + private final FilmStorage filmStorage; + private final UserService userService; + + public Collection getFilms() { + return filmStorage.getFilms(); + } + + public Film createFilm(Film film) { + return filmStorage.createFilm(film); + } + + public Film updateFilm(Film film) { + return filmStorage.updateFilm(film) + .orElseThrow(() -> new ExceptionNotFound("Фильм с идентификатором '%d' не найден".formatted(film.getId()))); + } + + public void addLike(Long filmId, Long userId) { + if (userService.getUserById(userId).getId() > 0 && getFilmById(filmId).getId() > 0) { + filmStorage.addLike(filmId, userId); + } + } + + public void removeLike(Long filmId, Long userId) { + if (userService.getUserById(userId).getId() > 0 && getFilmById(filmId).getId() > 0) { + filmStorage.removeLike(filmId, userId); + } + } + + public List getPopularFilms(int count) { + return filmStorage.getPopularFilms(count); + } + + public Film getFilmById(Long id) { + return filmStorage.getFilmById(id) + .orElseThrow(() -> new ExceptionNotFound("Фильм с идентификатором '%d' не найден".formatted(id))); + } + +} 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 0000000..3107cda --- /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 org.springframework.stereotype.Service; +import ru.yandex.practicum.filmorate.exception.ExceptionNotFound; +import ru.yandex.practicum.filmorate.model.User; +import ru.yandex.practicum.filmorate.storage.user.UserStorage; + +import java.util.Collection; +import java.util.Optional; +import java.util.Set; + +@RequiredArgsConstructor +@Service +public class UserService { + + private final UserStorage userStorage; + + public Collection getUsers() { + return userStorage.getUsers(); + } + + public User createUser(User user) { + return userStorage.createUser(user); + } + + public User updateUser(User user) { + return userStorage.updateUser(user) + .orElseThrow(() -> new ExceptionNotFound("Пользователь с идентификатором '%d' не найден".formatted(user.getId()))); + } + + public void addFriend(Long userId, Long friendId) { + if (getUserById(userId).getId() > 0 && getUserById(friendId).getId() > 0) { + userStorage.addFriend(userId, friendId); + } + } + + public void removeFriend(Long userId, Long friendId) { + if (getUserById(userId).getId() > 0 && getUserById(friendId).getId() > 0) { + userStorage.removeFriend(userId, friendId); + } + } + + public Set> getCommonFriends(Long userIdOne, Long userIdTwo) { + return userStorage.getCommonFriends(userIdOne, userIdTwo); + } + + public Set getFriends(Long userId) { + if (getUserById(userId).getId() > 0) { + return userStorage.getFriends(userId); + } + return null; + } + + public User getUserById(Long id) { + return userStorage.getUserById(id) + .orElseThrow(() -> new ExceptionNotFound("Пользователь с идентификатором '%d' не найден".formatted(id))); + } + +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmStorage.java new file mode 100644 index 0000000..ef0e726 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmStorage.java @@ -0,0 +1,25 @@ +package ru.yandex.practicum.filmorate.storage.film; + +import ru.yandex.practicum.filmorate.model.Film; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +public interface FilmStorage { + + Collection getFilms(); + + Film createFilm(Film film); + + Optional updateFilm(Film film); + + void addLike(Long filmId, Long userId); + + Optional getFilmById(Long filmId); + + void removeLike(Long filmId, Long userId); + + List getPopularFilms(int count); + +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/film/InMemoryFilmStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/film/InMemoryFilmStorage.java new file mode 100644 index 0000000..22950f9 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/film/InMemoryFilmStorage.java @@ -0,0 +1,124 @@ +package ru.yandex.practicum.filmorate.storage.film; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import ru.yandex.practicum.filmorate.model.Film; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@Slf4j +@Component +public class InMemoryFilmStorage implements FilmStorage { + + private final Map films = new HashMap<>(); + private Long id = 1L; + + @Override + public Collection getFilms() { + log.trace("Вызван метод getFilms"); + try { + log.trace("Успешно выполнен метод getFilms"); + return films.values(); + } catch (RuntimeException e) { + log.error("Ошибка в методе getFilms: {}", e.getMessage(), e); + throw e; + } + } + + @Override + public Film createFilm(Film film) { + log.trace("Вызван метод createFilm"); + try { + log.trace("Успешно выполнен метод createFilm"); + film.setId(generateId()); + films.put(film.getId(), film); + return film; + } catch (RuntimeException e) { + log.error("Ошибка в методе createFilm: {}", e.getMessage(), e); + throw e; + } + } + + @Override + public Optional updateFilm(Film film) { + log.trace("Вызван метод updateFilm"); + try { + log.trace("Успешно выполнен метод updateFilm"); + return Optional.ofNullable(films.computeIfPresent(film.getId(), (a, b) -> film)); + } catch (RuntimeException e) { + log.error("Ошибка в методе updateFilm: {}", e.getMessage(), e); + throw e; + } + } + + @Override + public void addLike(Long filmId, Long userId) { + log.trace("Вызван метод addLike"); + try { + log.trace("Успешно выполнен метод addLike"); + films.get(filmId).getLikes().add(userId); + } catch (RuntimeException e) { + log.error("Ошибка в методе addLike: {}", e.getMessage(), e); + throw e; + } + } + + @Override + public Optional getFilmById(Long id) { + log.trace("Вызван метод getFilmById"); + try { + log.trace("Успешно выполнен метод getFilmById"); + return Optional.ofNullable(films.get(id)); + } catch (RuntimeException e) { + log.error("Ошибка в методе getFilmById: {}", e.getMessage(), e); + throw e; + } + } + + @Override + public void removeLike(Long filmId, Long userId) { + log.trace("Вызван метод removeLike"); + try { + log.trace("Успешно выполнен метод removeLike"); + films.get(filmId).getLikes().remove(userId); + } catch (RuntimeException e) { + log.error("Ошибка в методе removeLike: {}", e.getMessage(), e); + throw e; + } + } + + @Override + public List getPopularFilms(int count) { + log.trace("Вызван метод getPopularFilms"); + try { + log.trace("Успешно выполнен метод getPopularFilms"); + return films.entrySet() + .stream() + .collect( + Collectors.toMap( + Map.Entry::getKey, + filmEntry -> filmEntry.getValue().getLikes().size() + ) + ) + .entrySet() + .stream() + .sorted(Map.Entry.comparingByValue().reversed()) + .limit(count) + .map(entry -> films.get(entry.getKey())) + .collect(Collectors.toList()); + } catch (RuntimeException e) { + log.error("Ошибка в методе getPopularFilms: {}", e.getMessage(), e); + throw e; + } + } + + private Long generateId() { + return id++; + } + +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/user/InMemoryUserStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/user/InMemoryUserStorage.java new file mode 100644 index 0000000..50e76bf --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/user/InMemoryUserStorage.java @@ -0,0 +1,132 @@ +package ru.yandex.practicum.filmorate.storage.user; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import ru.yandex.practicum.filmorate.model.User; + +import java.util.*; +import java.util.stream.Collectors; + +@Slf4j +@Component +public class InMemoryUserStorage implements UserStorage { + + private final Map users = new HashMap<>(); + private Long id = 1L; + + @Override + public Collection getUsers() { + log.trace("Вызван метод getUsers"); + try { + log.trace("Успешно выполнен метод getUsers"); + return users.values(); + } catch (RuntimeException e) { + log.error("Ошибка в методе getUsers: {}", e.getMessage(), e); + throw e; + } + } + + @Override + public User createUser(User user) { + log.trace("Вызван метод createUser"); + try { + if (user.getName() == null) { + user.setName(user.getLogin()); + log.debug("Имя пользователя не указано, поэтому используем логин как имя"); + } + user.setId(generateId()); + users.put(user.getId(), user); + log.trace("Успешно выполнен метод createUser"); + return user; + } catch (RuntimeException e) { + log.error("Ошибка в методе createUser: {}", e.getMessage(), e); + throw e; + } + } + + @Override + public Optional updateUser(User user) { + log.trace("Вызван метод updateUser"); + try { + log.trace("Успешно выполнен метод updateUser"); + return Optional.ofNullable(users.computeIfPresent(user.getId(), (a, b) -> user)); + } catch (RuntimeException e) { + log.error("Ошибка в методе updateUser: {}", e.getMessage(), e); + throw e; + } + } + + @Override + public void addFriend(Long userId, Long friendId) { + log.trace("Вызван метод addFriend"); + try { + log.trace("Успешно выполнен метод addFriend"); + users.get(userId).getFriends().add(friendId); + users.get(friendId).getFriends().add(userId); + } catch (RuntimeException e) { + log.error("Ошибка в методе addFriend: {}", e.getMessage(), e); + throw e; + } + } + + @Override + public Optional getUserById(Long id) { + log.trace("Вызван метод getUserById"); + try { + log.trace("Успешно выполнен метод getUserById"); + return Optional.ofNullable(users.get(id)); + } catch (RuntimeException e) { + log.error("Ошибка в методе getUserById: {}", e.getMessage(), e); + throw e; + } + } + + @Override + public void removeFriend(Long userId, Long friendId) { + log.trace("Вызван метод removeFriend"); + try { + log.trace("Успешно выполнен метод removeFriend"); + users.get(userId).getFriends().remove(friendId); + users.get(friendId).getFriends().remove(userId); + } catch (RuntimeException e) { + log.error("Ошибка в методе removeFriend: {}", e.getMessage(), e); + throw e; + } + } + + @Override + public Set> getCommonFriends(Long userIdOne, Long userIdTwo) { + log.trace("Вызван метод getCommonFriends"); + try { + log.trace("Успешно выполнен метод getCommonFriends"); + return users.get(userIdOne).getFriends() + .stream() + .filter(users.get(userIdTwo).getFriends()::contains) + .map(this::getUserById) + .collect(Collectors.toSet()); + } catch (RuntimeException e) { + log.error("Ошибка в методе getCommonFriends: {}", e.getMessage(), e); + throw e; + } + } + + @Override + public Set getFriends(Long userId) { + log.trace("Вызван метод getFriends"); + try { + log.trace("Успешно выполнен метод getFriends"); + return users.get(userId).getFriends() + .stream() + .map(users::get) + .collect(Collectors.toSet()); + } catch (RuntimeException e) { + log.error("Ошибка в методе getFriends: {}", e.getMessage(), e); + throw e; + } + } + + private Long generateId() { + return id++; + } + +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/user/UserStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/user/UserStorage.java new file mode 100644 index 0000000..e92e42d --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/user/UserStorage.java @@ -0,0 +1,29 @@ +package ru.yandex.practicum.filmorate.storage.user; + +import ru.yandex.practicum.filmorate.model.User; + +import java.util.Collection; +import java.util.Optional; +import java.util.Set; + +public interface UserStorage { + + Collection getUsers(); + + User createUser(User user); + + Optional updateUser(User user); + + void addFriend(Long userId, Long friendId); + + Optional getUserById(Long id); + + void removeFriend(Long userId, Long friendId); + + Set> getCommonFriends(Long userIdOne, Long userIdTwo); + + Set getFriends(Long ownerId); + +} + + diff --git a/src/main/java/ru/yandex/practicum/filmorate/validation/MinimumDate.java b/src/main/java/ru/yandex/practicum/filmorate/validation/MinimumDate.java new file mode 100644 index 0000000..d2d24f2 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/validation/MinimumDate.java @@ -0,0 +1,21 @@ +package ru.yandex.practicum.filmorate.validation; + +import jakarta.validation.Constraint; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@Constraint(validatedBy = MinimumDateValidator.class) +public @interface MinimumDate { + String message() default "Дата релиза фильма не может быть раньше {value}"; + + Class[] groups() default {}; + + Class[] payload() default {}; + + String value() default "1895-12-28"; +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/validation/MinimumDateValidator.java b/src/main/java/ru/yandex/practicum/filmorate/validation/MinimumDateValidator.java new file mode 100644 index 0000000..b340a14 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/validation/MinimumDateValidator.java @@ -0,0 +1,20 @@ +package ru.yandex.practicum.filmorate.validation; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +import java.time.LocalDate; + +public class MinimumDateValidator implements ConstraintValidator { + private LocalDate minimumDate; + + @Override + public void initialize(MinimumDate constraintAnnotation) { + minimumDate = LocalDate.parse(constraintAnnotation.value()); + } + + @Override + public boolean isValid(LocalDate value, ConstraintValidatorContext context) { + return value == null || !value.isBefore(minimumDate); + } +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/validation/PositiveDuration.java b/src/main/java/ru/yandex/practicum/filmorate/validation/PositiveDuration.java new file mode 100644 index 0000000..8890be6 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/validation/PositiveDuration.java @@ -0,0 +1,20 @@ +package ru.yandex.practicum.filmorate.validation; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@Constraint(validatedBy = PositiveDurationValidator.class) +public @interface PositiveDuration { + String message() default "Продолжительность фильма должна быть положительной"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/validation/PositiveDurationValidator.java b/src/main/java/ru/yandex/practicum/filmorate/validation/PositiveDurationValidator.java new file mode 100644 index 0000000..0a8851d --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/validation/PositiveDurationValidator.java @@ -0,0 +1,15 @@ +package ru.yandex.practicum.filmorate.validation; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +import java.time.Duration; + +public class PositiveDurationValidator implements + ConstraintValidator { + + @Override + public boolean isValid(Duration value, ConstraintValidatorContext context) { + return value == null || value.getSeconds() > 0; + } +} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8b13789..cc392b5 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1 @@ - +logging.level.org.zalando.logbook=TRACE \ No newline at end of file diff --git a/src/test/java/ru/yandex/practicum/filmorate/FilmorateApplicationTests.java b/src/test/java/ru/yandex/practicum/filmorate/FilmorateApplicationTests.java index 660412e..91559e2 100644 --- a/src/test/java/ru/yandex/practicum/filmorate/FilmorateApplicationTests.java +++ b/src/test/java/ru/yandex/practicum/filmorate/FilmorateApplicationTests.java @@ -6,8 +6,8 @@ @SpringBootTest class FilmorateApplicationTests { - @Test - void contextLoads() { - } + @Test + void contextLoads() { + } -} +} \ No newline at end of file diff --git a/src/test/java/ru/yandex/practicum/filmorate/ValidationFilmTest.java b/src/test/java/ru/yandex/practicum/filmorate/ValidationFilmTest.java new file mode 100644 index 0000000..3f9c4a9 --- /dev/null +++ b/src/test/java/ru/yandex/practicum/filmorate/ValidationFilmTest.java @@ -0,0 +1,139 @@ +package ru.yandex.practicum.filmorate; + +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import jakarta.validation.ValidatorFactory; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.boot.test.context.SpringBootTest; +import ru.yandex.practicum.filmorate.model.Film; + +import java.time.Duration; +import java.time.LocalDate; +import java.util.Set; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +public class ValidationFilmTest { + + private static Validator validator; + private Film film; + + @BeforeAll + static void setupValidator() { + try (ValidatorFactory factory = Validation.buildDefaultValidatorFactory()) { + validator = factory.getValidator(); + } + } + + @BeforeEach + void setupValidFilm() { + film = new Film(); + film.setId(1L); + film.setName("Name Film"); + film.setDescription("Description Film"); + film.setReleaseDate(LocalDate.of(2025, 6, 1)); + film.setDuration(Duration.ofMinutes(120)); + } + + @Test + void validationNameDescriptionDateDurationFilm() { + Set> violations = validator.validate(film); + assertThat(violations).isEmpty(); + } + + + @ParameterizedTest + @MethodSource("validName") + void validationName(String name, boolean isValid) { + film.setName(name); + + boolean isViolation = validator.validate(film) + .stream() + .anyMatch(a -> a.getPropertyPath().toString().equals("name")); + + assertThat(isViolation).isEqualTo(!isValid); + } + + private static Stream validName() { + return Stream.of( + Arguments.of("Name", true), + Arguments.of("Name Film", true), + Arguments.of("Name-Film", true), + Arguments.of("Name_Film", true), + Arguments.of("Название фильма", true), + Arguments.of(" ", false), + Arguments.of(" ", false), + Arguments.of("", false), + Arguments.of(null, false) + ); + } + + @ParameterizedTest + @MethodSource("validDescription") + void validationDescriptionMax200Characters(String description, boolean isValid) { + film.setDescription(description); + + boolean isViolation = validator.validate(film) + .stream() + .anyMatch(a -> a.getPropertyPath().toString().equals("description")); + + assertThat(isViolation).isEqualTo(!isValid); + } + + private static Stream validDescription() { + return Stream.of( + Arguments.of("Name Film", true), + Arguments.of("Name Film ".repeat(20), true), + Arguments.of("Name Film ".repeat(20) + "!", false) + ); + } + + @ParameterizedTest + @MethodSource("validReleaseDate") + void validationReleaseDate(LocalDate date, boolean isValid) { + film.setReleaseDate(date); + + boolean isViolation = validator.validate(film) + .stream() + .anyMatch(a -> a.getPropertyPath().toString().equals("releaseDate")); + + assertThat(isViolation).isEqualTo(!isValid); + } + + private static Stream validReleaseDate() { + return Stream.of( + Arguments.of(LocalDate.of(1895, 12, 29), true), + Arguments.of(LocalDate.of(1895, 12, 28), true), + Arguments.of(LocalDate.of(1895, 12, 27), false) + ); + } + + @ParameterizedTest + @MethodSource("validDurations") + void validationDuration(Duration duration, boolean isValid) { + film.setDuration(duration); + + boolean isViolation = validator.validate(film) + .stream() + .anyMatch(a -> a.getPropertyPath().toString().equals("duration")); + + assertThat(isViolation).isEqualTo(!isValid); + } + + private static Stream validDurations() { + return Stream.of( + Arguments.of(Duration.ofMinutes(1), true), + Arguments.of(Duration.ZERO, false), + Arguments.of(Duration.ofMinutes(-1), false) + ); + } + +} \ No newline at end of file diff --git a/src/test/java/ru/yandex/practicum/filmorate/ValidationUserTest.java b/src/test/java/ru/yandex/practicum/filmorate/ValidationUserTest.java new file mode 100644 index 0000000..1820e19 --- /dev/null +++ b/src/test/java/ru/yandex/practicum/filmorate/ValidationUserTest.java @@ -0,0 +1,119 @@ +package ru.yandex.practicum.filmorate; + +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import jakarta.validation.ValidatorFactory; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.boot.test.context.SpringBootTest; +import ru.yandex.practicum.filmorate.model.User; + +import java.time.LocalDate; +import java.util.Set; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +public class ValidationUserTest { + + private static Validator validator; + private User user; + + + @BeforeAll + static void setupValidator() { + try (ValidatorFactory factory = Validation.buildDefaultValidatorFactory()) { + validator = factory.getValidator(); + } + } + + @BeforeEach + void setupValidUser() { + user = new User(); + user.setId(1L); + user.setEmail("internet-prodashi@yandex.ru"); + user.setLogin("internet-prodashi"); + user.setName("Ivan"); + user.setBirthday(LocalDate.of(1990, 2, 5)); + } + + @Test + void validationEmailLoginAndBirthdayUser() { + Set> violations = validator.validate(user); + assertThat(violations).isEmpty(); + } + + @ParameterizedTest + @MethodSource("validEmail") + void validationEmail(String email, boolean isValid) { + user.setEmail(email); + + boolean isViolation = validator.validate(user) + .stream() + .anyMatch(a -> a.getPropertyPath().toString().equals("email")); + + assertThat(isViolation).isEqualTo(!isValid); + } + + private static Stream validEmail() { + return Stream.of( + Arguments.of("internet-prodashi@yandex.ru", true), + Arguments.of(" ", false), + Arguments.of(" ", false), + Arguments.of("", false), + Arguments.of("internet prodashi@yandex.ru", false), + Arguments.of("internet-prodashiyandex.ru", false), + Arguments.of(null, false) + ); + } + + @ParameterizedTest + @MethodSource("validLogin") + void validationLogin(String login, boolean isValid) { + user.setLogin(login); + + boolean isViolation = validator.validate(user) + .stream() + .anyMatch(a -> a.getPropertyPath().toString().equals("login")); + + assertThat(isViolation).isEqualTo(!isValid); + } + + private static Stream validLogin() { + return Stream.of( + Arguments.of("internet-prodashi", true), + Arguments.of("internet prodashi", false), + Arguments.of(" ", false), + Arguments.of(" ", false), + Arguments.of("", false), + Arguments.of(null, false) + ); + } + + @ParameterizedTest + @MethodSource("validBirthday") + void validationBirthday(LocalDate birthday, boolean isValid) { + user.setBirthday(birthday); + + boolean isViolation = validator.validate(user) + .stream() + .anyMatch(a -> a.getPropertyPath().toString().equals("birthday")); + + assertThat(isViolation).isEqualTo(!isValid); + } + + private static Stream validBirthday() { + return Stream.of( + Arguments.of(LocalDate.of(1990, 2, 5), true), + Arguments.of(LocalDate.now(), true), + Arguments.of(LocalDate.now().plusDays(1), false) + ); + } + +} \ No newline at end of file From bd6f478664efd844db8e3950e7a3cf156ac75304 Mon Sep 17 00:00:00 2001 From: vs Date: Wed, 9 Jul 2025 12:44:51 +0300 Subject: [PATCH 2/2] =?UTF-8?q?=D0=92=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=BE=20=D0=BF=D1=80=D0=BE=D0=BC=D0=B5=D0=B6=D1=83=D1=82?= =?UTF-8?q?=D0=BE=D1=87=D0=BD=D0=BE=D0=B5=20=D0=B7=D0=B0=D0=B4=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=2012=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD=D1=82=D0=B0.?= =?UTF-8?q?=20=D0=A1=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B0=20=D1=81=D1=85?= =?UTF-8?q?=D0=B5=D0=BC=D0=B0=20=D0=B1=D0=B0=D0=B7=D1=8B=20=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D0=BD=D1=8B=D1=85.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 16 +++++++++++++++- img/schema-database.png | Bin 0 -> 51146 bytes 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 img/schema-database.png diff --git a/README.md b/README.md index 2cf454a..c35ae4c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,16 @@ # java-filmorate -Template repository for Filmorate project. + +## Схема базы данных + +![Схема БД](/img/schema-database.png) + +### Таблицы + +- **users** — таблица пользователей: `email` - электронная почта, `login` - логин, `name` - имя, `birthday` - день рождения +- **films** — таблица фильмов: `name` - название, `description` - описание, `release_date` - дата релиза, `duration` - продолжительность в минутах, `rating_id` - идентификатор рейтинга +- **friendships** — таблица дружбы между пользователями: `status` - статус заявки (0 - заявка в друзья не принята, 1 - заявка в друзья принята) +- **film_likes** — таблица лайков (пользователь отмечает понравившийся фильм) +- **film_genres** — таблица связей между фильмами и жанрами +- **ratings** — таблица возрастных рейтингов фильма: `name` - название рейтинга +- **genres** — таблица жанров фильмов: `name` - название жанра + diff --git a/img/schema-database.png b/img/schema-database.png new file mode 100644 index 0000000000000000000000000000000000000000..473408b89f9f5580069363b92fd4b01ec7c1cfa4 GIT binary patch literal 51146 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

re%RH)@gSoL|^gRas+K6t?G_ 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%0Dxhzc0p|pnz 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 NOiWrdU*wJVe*rl!m5~4d literal 0 HcmV?d00001