diff --git a/src/main/java/ru/yandex/practicum/filmorate/Enum/EventType.java b/src/main/java/ru/yandex/practicum/filmorate/Enum/EventType.java index 86b32c0..c31602b 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/Enum/EventType.java +++ b/src/main/java/ru/yandex/practicum/filmorate/Enum/EventType.java @@ -2,7 +2,6 @@ public enum EventType { LIKE, - DISLIKE, FRIEND, REVIEW } 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 8b53672..af952ac 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/ReviewController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/ReviewController.java @@ -26,7 +26,7 @@ public Optional getReviewById(@PathVariable long id) { @GetMapping @ResponseStatus(HttpStatus.OK) - public List getAllReviewsByFilmId(@RequestParam(value = "filmId") long filmId, + public List getAllReviewsByFilmId(@RequestParam(value = "filmId", defaultValue = "-1") long filmId, @RequestParam(value = "count", defaultValue = "10") long count) { return reviewService.getAllReviewsByFilmId(filmId, count); } diff --git a/src/main/java/ru/yandex/practicum/filmorate/dal/JdbcFilmRepository.java b/src/main/java/ru/yandex/practicum/filmorate/dal/JdbcFilmRepository.java index fe7e86f..60cd7fd 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/dal/JdbcFilmRepository.java +++ b/src/main/java/ru/yandex/practicum/filmorate/dal/JdbcFilmRepository.java @@ -1,11 +1,14 @@ package ru.yandex.practicum.filmorate.dal; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.jdbc.support.rowset.SqlRowSet; import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; import ru.yandex.practicum.filmorate.Enum.EventType; import ru.yandex.practicum.filmorate.Enum.OperationType; import ru.yandex.practicum.filmorate.dal.mappers.DirectorRowMapper; @@ -21,6 +24,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +@Slf4j @Repository @RequiredArgsConstructor public class JdbcFilmRepository implements FilmRepository { @@ -279,21 +283,26 @@ public void connectDirectors(Collection films) { } } + @Transactional @Override public void addLike(long filmId, long userId) { - GeneratedKeyHolder keyHolder = new GeneratedKeyHolder(); - MapSqlParameterSource params = new MapSqlParameterSource(); - params.addValue("user_id", userId); - params.addValue("film_id", filmId); - jdbc.update(ADD_LIKE_QUERY, params, keyHolder); MapSqlParameterSource paramsActivity = new MapSqlParameterSource(); paramsActivity.addValue("userId", userId); paramsActivity.addValue("entityId", filmId); jdbc.update(ACTIVITY_FILM_LIKE, paramsActivity); + try { + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("user_id", userId); + params.addValue("film_id", filmId); + jdbc.update(ADD_LIKE_QUERY, params); + } catch (DataIntegrityViolationException ex) { + log.debug("Лайк уже существует: userId={}, filmId={}", userId, filmId); + } } + @Transactional @Override public void deleteLike(long filmId, long userId) { GeneratedKeyHolder keyHolder = new GeneratedKeyHolder(); diff --git a/src/main/java/ru/yandex/practicum/filmorate/dal/JdbcReviewRepository.java b/src/main/java/ru/yandex/practicum/filmorate/dal/JdbcReviewRepository.java index 707cfe4..6964c1f 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/dal/JdbcReviewRepository.java +++ b/src/main/java/ru/yandex/practicum/filmorate/dal/JdbcReviewRepository.java @@ -5,14 +5,18 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; import ru.yandex.practicum.filmorate.Enum.EventType; import ru.yandex.practicum.filmorate.Enum.OperationType; import ru.yandex.practicum.filmorate.dal.mappers.ReviewRowMapper; +import ru.yandex.practicum.filmorate.exception.NotFoundException; import ru.yandex.practicum.filmorate.model.Review; import java.time.Instant; +import java.util.Comparator; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; import java.util.stream.Stream; @Repository @@ -45,7 +49,22 @@ public class JdbcReviewRepository implements ReviewRepository { COALESCE(SUM(rl.reaction_type), 0) AS useful FROM reviews r LEFT JOIN reviews_likes rl ON r.reviewId = rl.reviewId - GROUP BY r.reviewId, r.content, r.isPositive, r.userId, r.filmId; + GROUP BY r.reviewId, r.content, r.isPositive, r.userId, r.filmId + LIMIT :count; + """; + private static final String GET_ALL_REVIEW_BY_FILM = """ + SELECT + r.reviewId, + r.content, + r.isPositive, + r.userId, + r.filmId, + COALESCE(SUM(rl.reaction_type), 0) AS useful + FROM reviews r + LEFT JOIN reviews_likes rl ON r.reviewId = rl.reviewId + WHERE r.filmId = :filmId + GROUP BY r.reviewId, r.content, r.isPositive, r.userId, r.filmId + LIMIT :count; """; private static final String CREATE_REVIEW = """ @@ -54,7 +73,7 @@ INSERT INTO reviews (content, isPositive, userId, filmId, useful) """; private static final String UPDATE_REVIEW = """ UPDATE reviews - SET content=:content, isPositive=:isPositive, userId=:userId, filmId=:filmId, useful=:useful + SET content=:content, isPositive=:isPositive, useful=:useful WHERE reviewId=:reviewId """; private static final String DELETE_REVIEW = """ @@ -86,14 +105,6 @@ INSERT INTO reviews_likes (reviewId, userId, reaction_type) private static final String ACTIVITY_REVIEW_DELETE = ACTIVITY_GENERAL + EventType.REVIEW + "','" + OperationType.REMOVE + "', " + instantOfMilliSecond() + ")"; - private static final String ACTIVITY_REVIEW_ADD_LIKE = ACTIVITY_GENERAL + - EventType.LIKE + "','" + OperationType.ADD + "', " + instantOfMilliSecond() + ")"; - - private static final String ACTIVITY_REVIEW_ADD_DISLIKE = ACTIVITY_GENERAL + - EventType.DISLIKE + "','" + OperationType.ADD + "', " + instantOfMilliSecond() + ")"; - - private static final String ACTIVITY_REVIEW_DELETE_REACTION = ACTIVITY_GENERAL + - EventType.LIKE + "','" + OperationType.REMOVE + "', " + instantOfMilliSecond() + ")"; private static long instantOfMilliSecond() { return Instant.now().toEpochMilli(); @@ -110,9 +121,20 @@ public Optional getReviewById(long reviewId) { @Override public List getAllReviewsByFilmId(long filmId, long count) { - return jdbc.query(GET_ALL_REVIEW, reviewMapper).reversed(); + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("count", count); + if (filmId == -1) { + return jdbc.query(GET_ALL_REVIEW, params, reviewMapper).stream() + .sorted(Comparator.comparingLong(Review::getUseful).reversed()) + .collect(Collectors.toList()); + } + params.addValue("filmId", filmId); + return jdbc.query(GET_ALL_REVIEW_BY_FILM, params, reviewMapper).stream() + .sorted(Comparator.comparingLong(Review::getUseful).reversed()) + .collect(Collectors.toList()); } + @Transactional @Override public Review create(Review review) { GeneratedKeyHolder keyHolder = new GeneratedKeyHolder(); @@ -122,7 +144,7 @@ public Review create(Review review) { params.addValue("isPositive", review.getIsPositive()); params.addValue("userId", review.getUserId()); params.addValue("filmId", review.getFilmId()); - params.addValue("useful", review.getUseful()); + params.addValue("useful", 0); jdbc.update(CREATE_REVIEW, params, keyHolder); review.setReviewId(keyHolder.getKeyAs(Long.class)); @@ -136,6 +158,7 @@ public Review create(Review review) { return review; } + @Transactional @Override public Review update(Review review) { GeneratedKeyHolder keyHolder = new GeneratedKeyHolder(); @@ -143,31 +166,31 @@ public Review update(Review review) { params.addValue("content", review.getContent()); params.addValue("isPositive", review.getIsPositive()); - params.addValue("userId", review.getUserId()); - params.addValue("filmId", review.getFilmId()); params.addValue("useful", review.getUseful()); params.addValue("reviewId", review.getReviewId()); - jdbc.update(UPDATE_REVIEW, params, keyHolder); + Review reviewUpdated = getReviewById(review.getReviewId()) + .orElseThrow(() -> new NotFoundException("Review not found")); MapSqlParameterSource paramsActivity = new MapSqlParameterSource(); - paramsActivity.addValue("userId", review.getUserId()); - paramsActivity.addValue("entityId", review.getReviewId()); - + paramsActivity.addValue("userId", reviewUpdated.getUserId()); + paramsActivity.addValue("entityId", reviewUpdated.getReviewId()); jdbc.update(ACTIVITY_REVIEW_UPDATE, paramsActivity); - return review; + return reviewUpdated; } + @Transactional @Override public void deleteReview(long reviewId) { MapSqlParameterSource params = new MapSqlParameterSource(); params.addValue("reviewId", reviewId); - Optional review = getReviewById(reviewId); MapSqlParameterSource paramsActivity = new MapSqlParameterSource(); - paramsActivity.addValue("userId", review.get().getUserId()); - paramsActivity.addValue("entityId", review.get().getReviewId()); + Review review = getReviewById(reviewId) + .orElseThrow(() -> new NotFoundException("Review not found")); + paramsActivity.addValue("userId", review.getUserId()); + paramsActivity.addValue("entityId", reviewId); jdbc.update(ACTIVITY_REVIEW_DELETE, paramsActivity); jdbc.update(DELETE_REVIEW, params); @@ -180,12 +203,6 @@ public void addLike(long reviewId, long userId) { params.addValue("reviewId", reviewId); params.addValue("userId", userId); jdbc.update(ADD_LIKE_REVIEW, params); - - MapSqlParameterSource paramsActivity = new MapSqlParameterSource(); - paramsActivity.addValue("userId", userId); - paramsActivity.addValue("entityId", reviewId); - - jdbc.update(ACTIVITY_REVIEW_ADD_LIKE, paramsActivity); } @Override @@ -195,12 +212,6 @@ public void addDislike(long reviewId, long userId) { params.addValue("reviewId", reviewId); params.addValue("userId", userId); jdbc.update(ADD_DISLIKE_REVIEW, params); - - MapSqlParameterSource paramsActivity = new MapSqlParameterSource(); - paramsActivity.addValue("userId", userId); - paramsActivity.addValue("entityId", reviewId); - - jdbc.update(ACTIVITY_REVIEW_ADD_DISLIKE, paramsActivity); } @Override @@ -209,12 +220,6 @@ public void deleteReaction(long reviewId, long userId) { params.addValue("reviewId", reviewId); params.addValue("userId", userId); jdbc.update(DELETE_LIKE_REVIEW, params); - - MapSqlParameterSource paramsActivity = new MapSqlParameterSource(); - paramsActivity.addValue("userId", userId); - paramsActivity.addValue("entityId", reviewId); - - jdbc.update(ACTIVITY_REVIEW_DELETE_REACTION, paramsActivity); } } diff --git a/src/main/java/ru/yandex/practicum/filmorate/dal/JdbcUserRepository.java b/src/main/java/ru/yandex/practicum/filmorate/dal/JdbcUserRepository.java index e1ad717..881c63d 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/dal/JdbcUserRepository.java +++ b/src/main/java/ru/yandex/practicum/filmorate/dal/JdbcUserRepository.java @@ -50,7 +50,7 @@ public class JdbcUserRepository implements UserRepository { private static final String ACTIVITY_FRIEND_DELETE = ACTIVITY_GENERAL + EventType.FRIEND + "','" + OperationType.REMOVE + "', " + instantOfMilliSecond() + ")"; - private static final String GET_ACTIVITY_BY_USER_ID = "SELECT * FROM activity WHERE userId = :userId"; + private static final String GET_ACTIVITY_BY_USER_ID = "SELECT * FROM activity WHERE userId = :userId ORDER BY eventId ASC"; private static long instantOfMilliSecond() { return Instant.now().toEpochMilli(); @@ -107,7 +107,6 @@ public Optional getUserById(long userId) { @Override public List getAllUsers() { return jdbc.query(GET_ALL_USERS_QUERY, mapper); - } @Override diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/ReviewService.java b/src/main/java/ru/yandex/practicum/filmorate/service/ReviewService.java index 55d8dab..f2a70b1 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/ReviewService.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/ReviewService.java @@ -6,20 +6,20 @@ import java.util.Optional; public interface ReviewService { - public Optional getReviewById(long reviewId); + Optional getReviewById(long reviewId); - public List getAllReviewsByFilmId(long filmId, long count); + List getAllReviewsByFilmId(long filmId, long count); - public Review create(Review review); + Review create(Review review); - public Review update(Review review); + Review update(Review review); - public void deleteReview(long reviewId); + void deleteReview(long reviewId); - public void addLike(long reviewId, long userId); + void addLike(long reviewId, long userId); - public void addDislike(long reviewId, long userId); + void addDislike(long reviewId, long userId); - public void deleteReaction(long reviewId, long userId); + void deleteReaction(long reviewId, long userId); } diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/ReviewServiceImpl.java b/src/main/java/ru/yandex/practicum/filmorate/service/ReviewServiceImpl.java index ecf6c7e..6b8f385 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/ReviewServiceImpl.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/ReviewServiceImpl.java @@ -30,7 +30,6 @@ public Optional getReviewById(long reviewId) { @Override public List getAllReviewsByFilmId(long filmId, long count) { - checkFilmId(filmId); return jdbcReviewRepository.getAllReviewsByFilmId(filmId, count); } diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/UserServiceImpl.java b/src/main/java/ru/yandex/practicum/filmorate/service/UserServiceImpl.java index 05d6655..3decc38 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/UserServiceImpl.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/UserServiceImpl.java @@ -22,7 +22,11 @@ public class UserServiceImpl implements UserService { @Override public List getActivityById(long userId) { - return jdbcUserRepository.getActivityById(userId); + List activities = jdbcUserRepository.getActivityById(userId); + if (activities.isEmpty()) { + throw new NotFoundException("Активность пользователя не найдена"); + } + return activities; } @Override