From 48103e6a3aeb762556d43250891d872ac5d42cd8 Mon Sep 17 00:00:00 2001 From: kimdohyung Date: Sun, 7 Apr 2024 19:29:07 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20=EB=A6=AC=EB=B7=B0=20=EC=A2=8B?= =?UTF-8?q?=EC=95=84=EC=9A=94=20=EC=A1=B0=ED=9A=8C=20=EC=BF=BC=EB=A6=AC=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC=20(#62)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/jisungin/api/user/UserController.java | 6 +- .../response/ReviewContentGetAllResponse.java | 32 +++++ .../response/ReviewContentResponse.java | 8 -- .../application/user/UserService.java | 19 ++- .../repository/ReviewRepositoryImpl.java | 26 ---- .../repository/ReviewLikeRepository.java | 5 + .../application/user/UserServiceTest.java | 23 ++-- .../repository/ReviewRepositoryTest.java | 14 +- .../repository/ReviewLikeRepositoryTest.java | 129 ++++++++++++++++++ 9 files changed, 200 insertions(+), 62 deletions(-) create mode 100644 src/main/java/com/jisungin/application/review/response/ReviewContentGetAllResponse.java create mode 100644 src/test/java/com/jisungin/domain/reviewlike/repository/ReviewLikeRepositoryTest.java diff --git a/src/main/java/com/jisungin/api/user/UserController.java b/src/main/java/com/jisungin/api/user/UserController.java index 5169f00..d25531a 100644 --- a/src/main/java/com/jisungin/api/user/UserController.java +++ b/src/main/java/com/jisungin/api/user/UserController.java @@ -7,7 +7,7 @@ import com.jisungin.api.user.request.UserRatingGetAllRequest; import com.jisungin.application.PageResponse; import com.jisungin.application.review.response.RatingFindAllResponse; -import com.jisungin.application.review.response.ReviewContentResponse; +import com.jisungin.application.review.response.ReviewContentGetAllResponse; import com.jisungin.application.user.UserService; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; @@ -33,11 +33,11 @@ public ApiResponse> getUserRatings( } @GetMapping("/reviews") - public ApiResponse> getReviewContents( + public ApiResponse getReviewContents( @ModelAttribute ReviewContentGetAllRequest request, @Auth AuthContext authContext ) { - PageResponse response = userService.getReviewContents( + ReviewContentGetAllResponse response = userService.getReviewContents( authContext.getUserId(), request.toService()); return ApiResponse.ok(response); } diff --git a/src/main/java/com/jisungin/application/review/response/ReviewContentGetAllResponse.java b/src/main/java/com/jisungin/application/review/response/ReviewContentGetAllResponse.java new file mode 100644 index 0000000..507eaca --- /dev/null +++ b/src/main/java/com/jisungin/application/review/response/ReviewContentGetAllResponse.java @@ -0,0 +1,32 @@ +package com.jisungin.application.review.response; + +import com.jisungin.application.PageResponse; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Getter +@NoArgsConstructor +public class ReviewContentGetAllResponse { + + private PageResponse reviewContents; + + private List userLikes; + + @Builder + private ReviewContentGetAllResponse(PageResponse reviewContents, List userLikes) { + this.reviewContents = reviewContents; + this.userLikes = userLikes; + } + + public static ReviewContentGetAllResponse of( + PageResponse reviewContents, List userLikes) { + return ReviewContentGetAllResponse.builder() + .reviewContents(reviewContents) + .userLikes(userLikes) + .build(); + } + +} diff --git a/src/main/java/com/jisungin/application/review/response/ReviewContentResponse.java b/src/main/java/com/jisungin/application/review/response/ReviewContentResponse.java index 39c2754..862c9ec 100644 --- a/src/main/java/com/jisungin/application/review/response/ReviewContentResponse.java +++ b/src/main/java/com/jisungin/application/review/response/ReviewContentResponse.java @@ -5,8 +5,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; -import java.util.List; - @Getter @NoArgsConstructor public class ReviewContentResponse { @@ -27,8 +25,6 @@ public class ReviewContentResponse { private String bookImage; - private List users; - @Builder @QueryProjection public ReviewContentResponse( @@ -44,8 +40,4 @@ public ReviewContentResponse( this.bookImage = bookImage; } - public void addUsers(List users) { - this.users = users; - } - } diff --git a/src/main/java/com/jisungin/application/user/UserService.java b/src/main/java/com/jisungin/application/user/UserService.java index 92aedb1..eff34b2 100644 --- a/src/main/java/com/jisungin/application/user/UserService.java +++ b/src/main/java/com/jisungin/application/user/UserService.java @@ -2,11 +2,12 @@ import com.jisungin.application.PageResponse; import com.jisungin.application.review.response.RatingFindAllResponse; +import com.jisungin.application.review.response.ReviewContentGetAllResponse; import com.jisungin.application.review.response.ReviewContentResponse; import com.jisungin.application.user.request.ReviewContentGetAllServiceRequest; import com.jisungin.application.user.request.UserRatingGetAllServiceRequest; -import com.jisungin.domain.review.Review; import com.jisungin.domain.review.repository.ReviewRepository; +import com.jisungin.domain.reviewlike.repository.ReviewLikeRepository; import com.jisungin.domain.user.User; import com.jisungin.domain.user.repository.UserRepository; import com.jisungin.exception.BusinessException; @@ -14,6 +15,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; + import static com.jisungin.exception.ErrorCode.*; @RequiredArgsConstructor @@ -25,6 +28,8 @@ public class UserService { private final ReviewRepository reviewRepository; + private final ReviewLikeRepository reviewLikeRepository; + public PageResponse getUserRatings(Long userId, UserRatingGetAllServiceRequest request) { User user = userRepository.findById(userId) .orElseThrow(() -> new BusinessException(USER_NOT_FOUND)); @@ -33,12 +38,20 @@ public PageResponse getUserRatings(Long userId, UserRatin user.getId(), request.getOrderType(), request.getRating(), request.getSize(), request.getOffset()); } - public PageResponse getReviewContents(Long userId, ReviewContentGetAllServiceRequest request) { + public ReviewContentGetAllResponse getReviewContents(Long userId, ReviewContentGetAllServiceRequest request) { User user = userRepository.findById(userId) .orElseThrow(() -> new BusinessException(USER_NOT_FOUND)); - return reviewRepository.findAllReviewContentOrderBy( + PageResponse reviewContents = reviewRepository.findAllReviewContentOrderBy( user.getId(), request.getOrderType(), request.getSize(), request.getOffset()); + + List reviewIds = reviewContents.getQueryResponse().stream() + .map(ReviewContentResponse::getReviewId) + .toList(); + + List likeReviewIds = reviewLikeRepository.findLikeReviewByReviewId(userId, reviewIds); + + return ReviewContentGetAllResponse.of(reviewContents, likeReviewIds); } } diff --git a/src/main/java/com/jisungin/domain/review/repository/ReviewRepositoryImpl.java b/src/main/java/com/jisungin/domain/review/repository/ReviewRepositoryImpl.java index 6785aee..aaa6c4f 100644 --- a/src/main/java/com/jisungin/domain/review/repository/ReviewRepositoryImpl.java +++ b/src/main/java/com/jisungin/domain/review/repository/ReviewRepositoryImpl.java @@ -13,14 +13,10 @@ import lombok.extern.slf4j.Slf4j; import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; import static com.jisungin.domain.book.QBook.book; import static com.jisungin.domain.review.QReview.review; import static com.jisungin.domain.review.RatingOrderType.*; -import static com.jisungin.domain.reviewlike.QReviewLike.reviewLike; import static com.jisungin.domain.user.QUser.user; @Slf4j @@ -49,16 +45,6 @@ public PageResponse findAllReviewContentOrderBy( log.info("--------------start--------------"); // 리뷰 내용을 가져온다. 쿼리 1회 List reviewContents = getReviewContents(userId, orderType, size, offset); - // review_id를 Key로 해당 리뷰를 좋아요한 users를 가져온다. 쿼리 1회 - Map> reviewLikeUsers = getReviewLikeUsers(); - - reviewLikeUsers.forEach((reviewId, users) -> { - Optional optionalReviewContents = reviewContents.stream() - .filter(content -> content.getReviewId().equals(reviewId)) - .findFirst(); - - optionalReviewContents.ifPresent(reviewContent -> reviewContent.addUsers(users)); - }); return PageResponse.builder() .queryResponse(reviewContents) @@ -67,18 +53,6 @@ public PageResponse findAllReviewContentOrderBy( .build(); } - private Map> getReviewLikeUsers() { - return queryFactory - .select(reviewLike.review.id, reviewLike.user.id) - .from(reviewLike) - .fetch() - .stream() - .collect(Collectors.groupingBy( - tuple -> tuple.get(0, Long.class), // reviewId - Collectors.mapping(tuple -> tuple.get(1, Long.class), Collectors.toList()) // List 유저 ID 리스트 - )); - } - private List getReviewContents( Long userId, RatingOrderType orderType, int size, int offset) { return queryFactory diff --git a/src/main/java/com/jisungin/domain/reviewlike/repository/ReviewLikeRepository.java b/src/main/java/com/jisungin/domain/reviewlike/repository/ReviewLikeRepository.java index 4d099bc..43cfcc5 100644 --- a/src/main/java/com/jisungin/domain/reviewlike/repository/ReviewLikeRepository.java +++ b/src/main/java/com/jisungin/domain/reviewlike/repository/ReviewLikeRepository.java @@ -4,8 +4,10 @@ import com.jisungin.domain.reviewlike.ReviewLike; import com.jisungin.domain.user.User; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; +import java.util.List; import java.util.Optional; @Repository @@ -13,4 +15,7 @@ public interface ReviewLikeRepository extends JpaRepository { Optional findByUserAndReview(User user, Review review); + @Query("select rl.review.id from ReviewLike rl where rl.review.id in :reviewIds and rl.user.id = :userId") + List findLikeReviewByReviewId(Long userId, List reviewIds); + } diff --git a/src/test/java/com/jisungin/application/user/UserServiceTest.java b/src/test/java/com/jisungin/application/user/UserServiceTest.java index 5ed3a65..836bb90 100644 --- a/src/test/java/com/jisungin/application/user/UserServiceTest.java +++ b/src/test/java/com/jisungin/application/user/UserServiceTest.java @@ -3,7 +3,7 @@ import com.jisungin.ServiceTestSupport; import com.jisungin.application.PageResponse; import com.jisungin.application.review.response.RatingFindAllResponse; -import com.jisungin.application.review.response.ReviewContentResponse; +import com.jisungin.application.review.response.ReviewContentGetAllResponse; import com.jisungin.application.user.request.ReviewContentGetAllServiceRequest; import com.jisungin.application.user.request.UserRatingGetAllServiceRequest; import com.jisungin.domain.book.Book; @@ -104,23 +104,20 @@ void getReviewContents() { .build(); //when - PageResponse result = userService.getReviewContents(user1.getId(), request); + ReviewContentGetAllResponse result = userService.getReviewContents(user1.getId(), request); //then - assertThat(result.getTotalCount()).isEqualTo(20); - assertThat(result.getQueryResponse()).hasSize(4) + assertThat(result.getReviewContents().getTotalCount()).isEqualTo(20); + assertThat(result.getReviewContents().getQueryResponse()).hasSize(4) .extracting( - "userImage", "userName", "rating", "content", "isbn", "title", "bookImage", "users") + "userImage", "userName", "rating", "content", "isbn", "title", "bookImage") .containsExactly( - tuple("userImage", "김도형", 1.0, "리뷰 내용1", "1", "제목1", "bookImage", - List.of(user1.getId(), user2.getId())), - tuple("userImage", "김도형", 1.0, "리뷰 내용6", "6", "제목6", "bookImage", - List.of(user1.getId(), user2.getId())), - tuple("userImage", "김도형", 1.0, "리뷰 내용11", "11", "제목11", "bookImage", - List.of(user1.getId(), user2.getId())), - tuple("userImage", "김도형", 1.0, "리뷰 내용16", "16", "제목16", "bookImage", - List.of(user1.getId(), user2.getId())) + tuple("userImage", "김도형", 1.0, "리뷰 내용1", "1", "제목1", "bookImage"), + tuple("userImage", "김도형", 1.0, "리뷰 내용6", "6", "제목6", "bookImage"), + tuple("userImage", "김도형", 1.0, "리뷰 내용11", "11", "제목11", "bookImage"), + tuple("userImage", "김도형", 1.0, "리뷰 내용16", "16", "제목16", "bookImage") ); + assertThat(result.getUserLikes()).hasSize(4); } private static List createBooks() { diff --git a/src/test/java/com/jisungin/domain/review/repository/ReviewRepositoryTest.java b/src/test/java/com/jisungin/domain/review/repository/ReviewRepositoryTest.java index 77ee46e..da6149f 100644 --- a/src/test/java/com/jisungin/domain/review/repository/ReviewRepositoryTest.java +++ b/src/test/java/com/jisungin/domain/review/repository/ReviewRepositoryTest.java @@ -220,16 +220,12 @@ void getReviewContentOrderByRatingAsc() { assertThat(result.getTotalCount()).isEqualTo(20); assertThat(result.getQueryResponse()).hasSize(4) .extracting( - "userImage", "userName", "rating", "content", "isbn", "title", "bookImage", "users") + "userImage", "userName", "rating", "content", "isbn", "title", "bookImage") .containsExactly( - tuple("userImage", "김도형", 1.0, "리뷰 내용1", "1", "제목1", "bookImage", - List.of(user1.getId(), user2.getId())), - tuple("userImage", "김도형", 1.0, "리뷰 내용6", "6", "제목6", "bookImage", - List.of(user1.getId(), user2.getId())), - tuple("userImage", "김도형", 1.0, "리뷰 내용11", "11", "제목11", "bookImage", - List.of(user1.getId(), user2.getId())), - tuple("userImage", "김도형", 1.0, "리뷰 내용16", "16", "제목16", "bookImage", - List.of(user1.getId(), user2.getId())) + tuple("userImage", "김도형", 1.0, "리뷰 내용1", "1", "제목1", "bookImage"), + tuple("userImage", "김도형", 1.0, "리뷰 내용6", "6", "제목6", "bookImage"), + tuple("userImage", "김도형", 1.0, "리뷰 내용11", "11", "제목11", "bookImage"), + tuple("userImage", "김도형", 1.0, "리뷰 내용16", "16", "제목16", "bookImage") ); } diff --git a/src/test/java/com/jisungin/domain/reviewlike/repository/ReviewLikeRepositoryTest.java b/src/test/java/com/jisungin/domain/reviewlike/repository/ReviewLikeRepositoryTest.java new file mode 100644 index 0000000..da0d8c9 --- /dev/null +++ b/src/test/java/com/jisungin/domain/reviewlike/repository/ReviewLikeRepositoryTest.java @@ -0,0 +1,129 @@ +package com.jisungin.domain.reviewlike.repository; + +import com.jisungin.RepositoryTestSupport; +import com.jisungin.domain.book.Book; +import com.jisungin.domain.book.repository.BookRepository; +import com.jisungin.domain.oauth.OauthId; +import com.jisungin.domain.oauth.OauthType; +import com.jisungin.domain.review.Review; +import com.jisungin.domain.review.repository.ReviewRepository; +import com.jisungin.domain.reviewlike.ReviewLike; +import com.jisungin.domain.user.User; +import com.jisungin.domain.user.repository.UserRepository; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static org.assertj.core.api.Assertions.*; + +class ReviewLikeRepositoryTest extends RepositoryTestSupport { + + @Autowired + private ReviewRepository reviewRepository; + + @Autowired + private BookRepository bookRepository; + + @Autowired + private UserRepository userRepository; + + @Autowired + private ReviewLikeRepository reviewLikeRepository; + + @AfterEach + void tearDown() { + reviewLikeRepository.deleteAllInBatch(); + reviewRepository.deleteAllInBatch(); + bookRepository.deleteAllInBatch(); + userRepository.deleteAllInBatch(); + } + + @DisplayName("사용자가 좋아요한 리뷰를 가져온다.") + @Test + void findLikeReviewByReviewId() { + //given + User user1 = userRepository.save(createUser("1")); + User user2 = userRepository.save(createUser("2")); + List books = bookRepository.saveAll(createBooks()); + List reviews = reviewRepository.saveAll(createReviews(user1, books)); + List reviewLikesWithUser1 = reviewLikeRepository.saveAll(createReviewLikes(user1, reviews)); + List reviewLikesWithUser2 = reviewLikeRepository.saveAll(createReviewLikes(user2, reviews)); + + //when + List result = reviewLikeRepository.findLikeReviewByReviewId( + user1.getId(), List.of(reviews.get(0).getId(), reviews.get(1).getId())); + + //then + assertThat(result).hasSize(2); + } + + private static List createBooks() { + return IntStream.rangeClosed(1, 20) + .mapToObj(i -> createBook( + "제목" + String.valueOf(i), "내용" + String.valueOf(i), String.valueOf(i))) + .collect(Collectors.toList()); + } + + private static Book createBook(String title, String content, String isbn) { + return Book.builder() + .title(title) + .content(content) + .authors("김도형") + .isbn(isbn) + .publisher("지성인") + .dateTime(LocalDateTime.of(2024, 1, 1, 0, 0)) + .imageUrl("bookImage") + .build(); + } + + private static List createReviews(User user, List books) { + return IntStream.range(0, 20) + .mapToObj(i -> { + double rating = i % 5 + 1.0; // 1.0, 2.0, 3.0, 4.0, 5.0이 순환되도록 설정 + return createReview(user, books.get(i), rating); // Review 객체 생성 + }) + .collect(Collectors.toList()); + } + + private static Review createReview(User user, Book book, Double rating) { + return Review.builder() + .user(user) + .book(book) + .content("리뷰 내용" + book.getIsbn()) + .rating(rating) + .build(); + } + + private static List createReviewLikes(User user, List reviews) { + return reviews.stream() + .map(review -> createReviewLike(user, review)) + .toList(); + } + + private static ReviewLike createReviewLike(User user, Review review) { + return ReviewLike.builder() + .user(user) + .review(review) + .build(); + } + + private static User createUser(String oauthId) { + return User.builder() + .name("김도형") + .profileImage("userImage") + .oauthId( + OauthId.builder() + .oauthId(oauthId) + .oauthType(OauthType.KAKAO) + .build() + ) + .build(); + } + +}