diff --git a/README.md b/README.md index 05dd41b2..66accb07 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,67 @@ ## Entity relations table for filmorate database -![er table](filmorate_er.jpg) +![er table](er.jpg) --- ### База данных состоит из следующих таблиц: 1. __filmorate_user__: - - *id* (первичный ключ) - - *email* (электронная почта пользователя) - - *login* (логин пользователя) - - *user_name* (имя пользователя) - - *birthday* (дата рождения пользователя) - + - *id* (первичный ключ) + - *email* (электронная почта пользователя) + - *login* (логин пользователя) + - *user_name* (имя пользователя) + - *birthday* (дата рождения пользователя) 2. __friendship_status__: - - *id* (первичный ключ) - - *status_name* (статус, например, 'Ожидает подтверждения', 'В друзьях') + - *id* (первичный ключ) + - *status_name* (статус, например, 'Ожидает подтверждения', 'В друзьях') 3. __friendship__: - *user_id* (составной первичный ключ, внешний ключ, содержит id пользователя из таблицы __user__) - *friend_id* (составной первичный ключ, внешний ключ, содержит id пользователя из таблицы __user__, который является другом пользователя c идентификатором *user_id*) - *friendship_id* (внешний ключ, содержит id статуса дружбы из таблицы __friendship__) 4. __mpa_rating__: - - *id* (первичный ключ) - - *rating_name* (навзвание возрастного рейтинга) + - *id* (первичный ключ) + - *rating_name* (навзвание возрастного рейтинга) 5. __film__: - - *id* (первичный ключ) - - *title* (название фильма) - - *description* (опсисание фильма) - - *release_date* (дата релиза фильма) - - duration (длительность фильма в минутах) - - *mpa_rating_id* (внешний кллюч, содержит id возрастного рейтинга из таблицы __mpa_rating__) + - *id* (первичный ключ) + - *title* (название фильма) + - *description* (опсисание фильма) + - *release_date* (дата релиза фильма) + - duration (длительность фильма в минутах) + - *mpa_rating_id* (внешний кллюч, содержит id возрастного рейтинга из таблицы __mpa_rating__) 6. __genre__: - - *id* (первичный ключ) - - *genre_name* (название жанра фильма) + - *id* (первичный ключ) + - *genre_name* (название жанра фильма) 7. __film_genre__ (соединительная таблица между __film__ и __genre__): - - *film_id* (составной первичный ключ, внешний ключ, содержит id фильма из таблицы __film__) - - *genre_id* (составной первичный ключ, внешний ключ, содержит id жанра из таблицы __genre__) + - *film_id* (составной первичный ключ, внешний ключ, содержит id фильма из таблицы __film__) + - *genre_id* (составной первичный ключ, внешний ключ, содержит id жанра из таблицы __genre__) 8. __film_like__ (содержит список лайков фильмов от пользователей): - - *film_id* (составной первичный ключ, внешний ключ, содержит id фильма из таблицы __film__) - - *user_id (составной первичный ключ, внешний ключ, содержит id пользователя из таблицы __user__) + - *film_id* (составной первичный ключ, внешний ключ, содержит id фильма из таблицы __film__) + - *user_id* (составной первичный ключ, внешний ключ, содержит id пользователя из таблицы __user__) +9. __review__ + - *id* (первичный ключ) + - *review_content* (содержание отзыва) + - *is_positive* (является ли отзыв положительным) + - *useful* (количество положительных лайков отзыва) + - *user_id* (идентификатор пользователя, который написал отзыв) + - *film_id* (идентификатор фильма, о котором напсисан отзыв) + +10. __review_like__ (соединительная таблица между __review__ и __filmorate_user__) + - *review_id* (внешний ключ, содержит id фильма из таблицы __review__) + - *user_id* (внешний ключ, содержит id фильма из таблицы __filmorate_user__) + - *like_type* (тип оценки (лайк/дизлайк)) +11. __director__ + - *id* (первичный ключ) + - *director_name* (имя режиссера) +12. __film_director__ (соединительная таблица между __film__ и __director__) + - *id* (первичный ключ) + - *film_id* (внешний ключ, содержит id фильма из таблицы __film__) + - *director_id* (внешний ключ, содержит id фильма из таблицы __director__)) +13. __feed_events__ + - *id* (первичный ключ) + - *event_type* (тип события в ленте) + - *operation* (тип операции над объектом) + - *entity_id* (идентификатор объекта, на котром прозводилась операция) + - *publication_time* (время выполнения события) + - *user_id* (внешний ключ, содержит id фильма из таблицы __filmorate_user__) --- ### Примеры запросов для основных операций: diff --git a/er.jpg b/er.jpg new file mode 100644 index 00000000..b0e43886 Binary files /dev/null and b/er.jpg differ 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 f71d949c..cbace21f 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java @@ -9,6 +9,7 @@ import ru.yandex.practicum.filmorate.service.FilmService; import javax.validation.Valid; +import javax.validation.constraints.Min; import java.util.Collection; @RestController @@ -50,9 +51,9 @@ public FilmDto removeLike(@PathVariable long id, @PathVariable long userId) { } @GetMapping("/popular") - public Collection getMostPopularFilms(@RequestParam(required = false, defaultValue = "10") int count, + public Collection getMostPopularFilms(@RequestParam(defaultValue = "10") int count, @RequestParam(required = false) Integer genreId, - @RequestParam(required = false) Integer year) { + @RequestParam(required = false) @Min(1895) Integer year) { return filmService.getMostPopularFilms(count, genreId, year); } diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/ReviewController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/ReviewController.java index fd58f98c..3a127c5a 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/ReviewController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/ReviewController.java @@ -40,7 +40,7 @@ public ReviewDto getReviewById(@PathVariable long id) { @GetMapping public List getReviewsByFilmId(@RequestParam(required = false) Long filmId, - @RequestParam(required = false, defaultValue = "10") int count) { + @RequestParam(defaultValue = "10") int count) { return reviewService.getReviewsByFilmId(filmId, count); } diff --git a/src/main/java/ru/yandex/practicum/filmorate/dao/impl/ReviewDbStorage.java b/src/main/java/ru/yandex/practicum/filmorate/dao/impl/ReviewDbStorage.java index fd07dabe..573fb967 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/dao/impl/ReviewDbStorage.java +++ b/src/main/java/ru/yandex/practicum/filmorate/dao/impl/ReviewDbStorage.java @@ -1,7 +1,6 @@ package ru.yandex.practicum.filmorate.dao.impl; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.support.GeneratedKeyHolder; @@ -21,7 +20,6 @@ @Repository @RequiredArgsConstructor -@Slf4j public class ReviewDbStorage implements ReviewStorage { private final JdbcTemplate jdbcTemplate; diff --git a/src/main/java/ru/yandex/practicum/filmorate/dto/DirectorDto.java b/src/main/java/ru/yandex/practicum/filmorate/dto/DirectorDto.java index 6062a71c..3e550bf2 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/dto/DirectorDto.java +++ b/src/main/java/ru/yandex/practicum/filmorate/dto/DirectorDto.java @@ -13,7 +13,7 @@ @Builder public class DirectorDto { - long id; + private long id; @NotBlank(message = "Имя режиссера не может быть пустым.") - String name; + private String name; } diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Director.java b/src/main/java/ru/yandex/practicum/filmorate/model/Director.java index 589d535d..3d9ae898 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/Director.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Director.java @@ -5,12 +5,15 @@ import lombok.Data; import lombok.NoArgsConstructor; +import javax.validation.constraints.Size; + @Data @AllArgsConstructor @NoArgsConstructor @Builder public class Director { - long id; - String name; + private long id; + @Size(max = 255, message = "Имя режиссера не должно превышать 255 символов.") + private String name; } diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/SortBy.java b/src/main/java/ru/yandex/practicum/filmorate/model/SortBy.java new file mode 100644 index 00000000..8c857828 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/model/SortBy.java @@ -0,0 +1,35 @@ +package ru.yandex.practicum.filmorate.model; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@RequiredArgsConstructor +@Getter +public enum SortBy { + YEAR("f.release_date"), + LIKES("likes DESC"); + + private final String sql; + + public static List getStringValues() { + return Stream.of(SortBy.values()) + .map(Enum::name) + .map(String::toLowerCase) + .collect(Collectors.toList()); + } + + public static SortBy fromString(String text) { + return Arrays.stream(SortBy.values()) + .filter(sortBy -> sortBy.name().equalsIgnoreCase(text)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("Данная сортировка не поддерживатется")); + } +} + + + diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/impl/FilmServiceImpl.java b/src/main/java/ru/yandex/practicum/filmorate/service/impl/FilmServiceImpl.java index c711084c..1e803ca1 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/impl/FilmServiceImpl.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/impl/FilmServiceImpl.java @@ -8,15 +8,11 @@ import ru.yandex.practicum.filmorate.dto.FilmDto; import ru.yandex.practicum.filmorate.dto.FilmSearchDto; import ru.yandex.practicum.filmorate.mapper.FilmMapper; -import ru.yandex.practicum.filmorate.model.EventType; -import ru.yandex.practicum.filmorate.model.Film; -import ru.yandex.practicum.filmorate.model.Operation; -import ru.yandex.practicum.filmorate.model.SearchBy; +import ru.yandex.practicum.filmorate.model.*; import ru.yandex.practicum.filmorate.service.FilmService; import java.util.ArrayList; import java.util.Collection; -import java.util.Map; import java.util.stream.Collectors; import static ru.yandex.practicum.filmorate.mapper.FilmMapper.toDto; @@ -27,11 +23,6 @@ @Slf4j public class FilmServiceImpl implements FilmService { - public static final Map ALLOWED_SORTS = Map.of( - "year", "f.release_date", - "likes", "likes DESC" - ); - private final FilmStorage filmStorage; private final UserStorage userStorage; @@ -89,11 +80,10 @@ public Collection getAllFilms() { * @return найденный фильм. */ @Override - @Transactional public FilmDto getFilmById(final long filmId) { - filmStorage.findById(filmId); + Film film = filmStorage.findById(filmId); log.info("Фильм с id {} найден.", filmId); - return toDto(filmStorage.findById(filmId)); + return toDto(film); } /** @@ -189,11 +179,12 @@ public Collection searchFilms(FilmSearchDto search) { @Override @Transactional public Collection getFilmsFromDirector(final long directorId, final String sortBy) { - if (!ALLOWED_SORTS.containsKey(sortBy)) { + if (!SortBy.getStringValues().contains(sortBy)) { throw new IllegalArgumentException("Поле сортировки '" + sortBy + "' не поддерживается."); } + String sort = SortBy.fromString(sortBy).getSql(); directorStorage.findById(directorId); - return filmStorage.findFilmsFromDirectorOrderBy(directorId, ALLOWED_SORTS.get(sortBy)).stream() + return filmStorage.findFilmsFromDirectorOrderBy(directorId, sort).stream() .map(FilmMapper::toDto) .collect(Collectors.toList()); } diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/impl/ReviewServiceImpl.java b/src/main/java/ru/yandex/practicum/filmorate/service/impl/ReviewServiceImpl.java index 9bf0e1d2..1519d1a5 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/impl/ReviewServiceImpl.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/impl/ReviewServiceImpl.java @@ -38,6 +38,7 @@ public class ReviewServiceImpl implements ReviewService { * @return отзыв с присвоенным идентификатором. */ @Override + @Transactional public ReviewDto addReview(final ReviewDto reviewDto) { findUserAndFilmInDb(reviewDto); final Review review = toModel(reviewDto); @@ -101,7 +102,6 @@ public void deleteReview(final long id) { * @return список отзывов. */ @Override - @Transactional public List getReviewsByFilmId(final Long filmId, final int count) { if (filmId == null) { final List reviews = reviewStorage.findAllLimitBy(count); diff --git a/src/test/java/ru/yandex/practicum/filmorate/storage/DirectorDbStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/storage/DirectorDbStorageTest.java index 55b76278..9e867f7f 100644 --- a/src/test/java/ru/yandex/practicum/filmorate/storage/DirectorDbStorageTest.java +++ b/src/test/java/ru/yandex/practicum/filmorate/storage/DirectorDbStorageTest.java @@ -9,6 +9,7 @@ import ru.yandex.practicum.filmorate.dao.impl.DirectorDbStorage; import ru.yandex.practicum.filmorate.exception.NotFoundException; import ru.yandex.practicum.filmorate.model.Director; +import ru.yandex.practicum.filmorate.model.SortBy; import java.util.Collection; import java.util.List; @@ -46,6 +47,8 @@ public void init() { public void testFindEmptyDirectors() { Collection emptyDirectors = directorStorage.findAll(); + System.out.println(SortBy.getStringValues()); + assertThat(emptyDirectors) .isNotNull() .isEmpty(); diff --git a/src/test/java/ru/yandex/practicum/filmorate/storage/FilmDbStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/storage/FilmDbStorageTest.java index 81a389a4..fa0d8882 100644 --- a/src/test/java/ru/yandex/practicum/filmorate/storage/FilmDbStorageTest.java +++ b/src/test/java/ru/yandex/practicum/filmorate/storage/FilmDbStorageTest.java @@ -17,7 +17,6 @@ import ru.yandex.practicum.filmorate.dto.FilmSearchDto; import ru.yandex.practicum.filmorate.exception.NotFoundException; import ru.yandex.practicum.filmorate.model.*; -import ru.yandex.practicum.filmorate.service.impl.FilmServiceImpl; import java.time.LocalDate; import java.util.Collection; @@ -563,7 +562,7 @@ public void findFilmsByDirectorSortByYear() { filmDbStorage.add(film2); Collection films = filmDbStorage.findFilmsFromDirectorOrderBy(director.getId(), - FilmServiceImpl.ALLOWED_SORTS.get("year")); + SortBy.YEAR.getSql()); assertThat(films) .isNotNull() @@ -589,7 +588,7 @@ public void findFilmsByDirectorSortByLikes() { Collection films = filmDbStorage.findFilmsFromDirectorOrderBy(director.getId(), - FilmServiceImpl.ALLOWED_SORTS.get("likes")); + SortBy.LIKES.getSql()); assertThat(films) .isNotNull() @@ -603,7 +602,7 @@ public void findFilmsByDirectorSortByLikes() { public void findFilmsByDirectorUnknownId() { directorStorage.add(director); Collection films = filmDbStorage.findFilmsFromDirectorOrderBy(director.getId(), - FilmServiceImpl.ALLOWED_SORTS.get("year")); + SortBy.YEAR.getSql()); assertThat(films) .isNotNull()