diff --git a/lime-api/src/main/java/com/programmers/lime/domains/review/api/ReviewController.java b/lime-api/src/main/java/com/programmers/lime/domains/review/api/ReviewController.java index c13ef927f..f15c4d506 100644 --- a/lime-api/src/main/java/com/programmers/lime/domains/review/api/ReviewController.java +++ b/lime-api/src/main/java/com/programmers/lime/domains/review/api/ReviewController.java @@ -22,6 +22,7 @@ import com.programmers.lime.domains.review.api.dto.response.ReviewGetByCursorResponse; import com.programmers.lime.domains.review.api.dto.response.ReviewGetResponse; import com.programmers.lime.domains.review.api.dto.response.ReviewModifyResponse; +import com.programmers.lime.domains.review.application.ReviewLikeService; import com.programmers.lime.domains.review.application.ReviewService; import com.programmers.lime.domains.review.application.dto.ReviewGetByCursorServiceResponse; import com.programmers.lime.domains.review.application.dto.ReviewGetServiceResponse; @@ -39,6 +40,7 @@ public class ReviewController { private final ReviewService reviewService; + private final ReviewLikeService reviewLikeService; @Operation(summary = "아이템 리뷰 등록", description = "itemId, ReviewCreateRequest을 이용하여 아이템 리뷰를 등록 합니다.") @PostMapping() @@ -106,4 +108,26 @@ public ResponseEntity getReview( return ResponseEntity.ok(response); } + + @Operation(summary = "아이템 리뷰 좋아요", description = "reviewId을 이용하여 아이템 리뷰를 좋아요 합니다.") + @PostMapping("/{reviewId}/like") + public ResponseEntity likeReview( + @PathVariable final Long itemId, + @PathVariable final Long reviewId + ) { + reviewLikeService.like(itemId, reviewId); + + return ResponseEntity.ok().build(); + } + + @Operation(summary = "아이템 리뷰 좋아요 취소", description = "reviewId을 이용하여 아이템 리뷰를 좋아요 취소 합니다.") + @DeleteMapping("/{reviewId}/like") + public ResponseEntity cancelLikedReview( + @PathVariable final Long itemId, + @PathVariable final Long reviewId + ) { + reviewLikeService.unlike(itemId, reviewId); + + return ResponseEntity.ok().build(); + } } diff --git a/lime-api/src/main/java/com/programmers/lime/domains/review/application/ReviewLikeService.java b/lime-api/src/main/java/com/programmers/lime/domains/review/application/ReviewLikeService.java new file mode 100644 index 000000000..31ab0c0a0 --- /dev/null +++ b/lime-api/src/main/java/com/programmers/lime/domains/review/application/ReviewLikeService.java @@ -0,0 +1,34 @@ +package com.programmers.lime.domains.review.application; + +import org.springframework.stereotype.Service; + +import com.programmers.lime.domains.review.implementation.ReviewLikeAppender; +import com.programmers.lime.domains.review.implementation.ReviewLikeRemover; +import com.programmers.lime.domains.review.implementation.ReviewLikeValidator; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class ReviewLikeService { + + private final ReviewLikeAppender reviewLikeAppender; + private final ReviewLikeRemover reviewLikeRemover; + private final ReviewLikeValidator reviewLikeValidator; + + public void like( + final Long memberId, + final Long reviewId + ) { + reviewLikeValidator.validateReviewLike(memberId, reviewId); + reviewLikeAppender.append(memberId, reviewId); + } + + public void unlike( + final Long memberId, + final Long reviewId + ) { + reviewLikeValidator.validateReviewUnlike(memberId, reviewId); + reviewLikeRemover.deleteByMemberIdAndReviewId(memberId, reviewId); + } +} diff --git a/lime-common/src/main/java/com/programmers/lime/error/ErrorCode.java b/lime-common/src/main/java/com/programmers/lime/error/ErrorCode.java index ba10d969b..b9640e632 100644 --- a/lime-common/src/main/java/com/programmers/lime/error/ErrorCode.java +++ b/lime-common/src/main/java/com/programmers/lime/error/ErrorCode.java @@ -55,6 +55,8 @@ public enum ErrorCode { REVIEW_NOT_EQUAL_ITEM("REVIEW_002", "리뷰 아이디와 아이템 아이디가 일치하지 않습니다."), REVIEW_NOT_MINE("REVIEW_003", "리뷰 작성자와 로그인한 회원아이디가 일치하지 않습니다."), REVIEW_BAD_SORT_CONDITION("REVIEW_004", "잘못된 리뷰 정렬 조건 입니다."), + ALREADY_REVIEW_LIKED("REVIEW_005", "이미 리뷰를 좋아요 했습니다."), + NOT_REVIEW_LIKED("REVIEW_006", "좋아요를 누르지 않은 리뷰입니다."), // Vote VOTE_NOT_FOUND("VOTE_001", "투표를 찾을 수 없습니다."), diff --git a/lime-domain/src/main/java/com/programmers/lime/domains/review/domain/ReviewLike.java b/lime-domain/src/main/java/com/programmers/lime/domains/review/domain/ReviewLike.java new file mode 100644 index 000000000..b114c4866 --- /dev/null +++ b/lime-domain/src/main/java/com/programmers/lime/domains/review/domain/ReviewLike.java @@ -0,0 +1,41 @@ +package com.programmers.lime.domains.review.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Entity +@Table(name = "review_likes") +public class ReviewLike { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @Column(name = "member_id") + private Long memberId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "review_id") + private Review review; + + public ReviewLike( + final Long memberId, + final Review review + ) { + this.memberId = memberId; + this.review = review; + } +} diff --git a/lime-domain/src/main/java/com/programmers/lime/domains/review/implementation/ReviewCursorReader.java b/lime-domain/src/main/java/com/programmers/lime/domains/review/implementation/ReviewCursorReader.java index 7296b7742..87abea0e0 100644 --- a/lime-domain/src/main/java/com/programmers/lime/domains/review/implementation/ReviewCursorReader.java +++ b/lime-domain/src/main/java/com/programmers/lime/domains/review/implementation/ReviewCursorReader.java @@ -14,6 +14,8 @@ import com.programmers.lime.domains.review.model.MemberInfoWithReviewId; import com.programmers.lime.domains.review.model.ReviewCursorIdInfo; import com.programmers.lime.domains.review.model.ReviewCursorSummary; +import com.programmers.lime.domains.review.model.ReviewImageInfo; +import com.programmers.lime.domains.review.model.ReviewInfo; import com.programmers.lime.domains.review.model.ReviewSortCondition; import com.programmers.lime.domains.review.model.ReviewSummary; import com.programmers.lime.domains.review.repository.ReviewRepository; @@ -59,9 +61,14 @@ private List getReviewCursorSummaries( .toList(); // 리뷰 아이디를 이용하여 리뷰 정보를 가져옴 - Map reviewSummaryMap = reviewRepository.getReviewSummaries(reviewIds) + Map reviewInfoMap = reviewRepository.getReviewInfo(reviewIds) .stream() - .collect(Collectors.toMap(ReviewSummary::reviewId, Function.identity())); + .collect(Collectors.toMap(ReviewInfo::reviewId, Function.identity())); + + // 리뷰 아이디를 이용하여 리뷰 이미지 정보를 가져옴 + Map reviewImageInfoMap = reviewRepository.getReviewImageInfos(reviewIds) + .stream() + .collect(Collectors.toMap(ReviewImageInfo::reviewId, Function.identity())); // 리뷰 아이디를 이용하여 멤버 정보를 가져옴 Map memberInfoMap = reviewRepository.getMemberInfos(reviewIds) @@ -71,7 +78,9 @@ private List getReviewCursorSummaries( return reviewCursorIdInfos.stream() .map(reviewCursorIdInfo -> { - ReviewSummary reviewSummary = reviewSummaryMap.get(reviewCursorIdInfo.reviewId()); + ReviewInfo reviewInfo = reviewInfoMap.get(reviewCursorIdInfo.reviewId()); + ReviewImageInfo reviewImageInfo = reviewImageInfoMap.get(reviewCursorIdInfo.reviewId()); + ReviewSummary reviewSummary = ReviewSummary.of(reviewInfo, reviewImageInfo.imageUrls()); MemberInfoWithReviewId memberInfoWithReviewId = memberInfoMap.get(reviewCursorIdInfo.reviewId()); MemberInfo memberInfo = memberInfoWithReviewId.memberInfo(); diff --git a/lime-domain/src/main/java/com/programmers/lime/domains/review/implementation/ReviewLikeAppender.java b/lime-domain/src/main/java/com/programmers/lime/domains/review/implementation/ReviewLikeAppender.java new file mode 100644 index 000000000..e77b214dd --- /dev/null +++ b/lime-domain/src/main/java/com/programmers/lime/domains/review/implementation/ReviewLikeAppender.java @@ -0,0 +1,32 @@ +package com.programmers.lime.domains.review.implementation; + +import org.springframework.stereotype.Component; + +import com.programmers.lime.domains.review.domain.Review; +import com.programmers.lime.domains.review.domain.ReviewLike; +import com.programmers.lime.domains.review.repository.ReviewLikeRepository; + +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class ReviewLikeAppender { + + private final ReviewLikeRepository reviewLikeRepository; + + private final ReviewReader reviewReader; + + public void append( + final Long memberId, + final Long reviewId + ) { + Review review = reviewReader.read(reviewId); + + ReviewLike reviewLike = new ReviewLike( + memberId, + review + ); + + reviewLikeRepository.save(reviewLike); + } +} diff --git a/lime-domain/src/main/java/com/programmers/lime/domains/review/implementation/ReviewLikeReader.java b/lime-domain/src/main/java/com/programmers/lime/domains/review/implementation/ReviewLikeReader.java new file mode 100644 index 000000000..05f6acc98 --- /dev/null +++ b/lime-domain/src/main/java/com/programmers/lime/domains/review/implementation/ReviewLikeReader.java @@ -0,0 +1,27 @@ +package com.programmers.lime.domains.review.implementation; + +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import com.programmers.lime.domains.review.repository.ReviewLikeRepository; + +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class ReviewLikeReader { + + private final ReviewLikeRepository reviewLikeRepository; + + public boolean alreadyLiked( + final Long memberId, + final Long reviewId + ) { + if (memberId == null) { + return false; + } + + return reviewLikeRepository.existsByMemberIdAndReviewId(memberId, reviewId); + } +} diff --git a/lime-domain/src/main/java/com/programmers/lime/domains/review/implementation/ReviewLikeRemover.java b/lime-domain/src/main/java/com/programmers/lime/domains/review/implementation/ReviewLikeRemover.java new file mode 100644 index 000000000..4773ffd6b --- /dev/null +++ b/lime-domain/src/main/java/com/programmers/lime/domains/review/implementation/ReviewLikeRemover.java @@ -0,0 +1,26 @@ +package com.programmers.lime.domains.review.implementation; + +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import com.programmers.lime.domains.review.domain.Review; +import com.programmers.lime.domains.review.repository.ReviewLikeRepository; + +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class ReviewLikeRemover { + + private final ReviewReader reviewReader; + private final ReviewLikeRepository reviewLikeRepository; + + @Transactional + public void deleteByMemberIdAndReviewId( + final Long memberId, + final Long reviewId + ) { + Review review = reviewReader.read(reviewId); + reviewLikeRepository.deleteReviewLikeByMemberIdAndReview(memberId, review); + } +} diff --git a/lime-domain/src/main/java/com/programmers/lime/domains/review/implementation/ReviewLikeValidator.java b/lime-domain/src/main/java/com/programmers/lime/domains/review/implementation/ReviewLikeValidator.java new file mode 100644 index 000000000..49f66b4ad --- /dev/null +++ b/lime-domain/src/main/java/com/programmers/lime/domains/review/implementation/ReviewLikeValidator.java @@ -0,0 +1,33 @@ +package com.programmers.lime.domains.review.implementation; + +import org.springframework.stereotype.Component; + +import com.programmers.lime.error.BusinessException; +import com.programmers.lime.error.ErrorCode; + +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class ReviewLikeValidator { + + private final ReviewLikeReader reviewLikeReader; + + public void validateReviewLike( + final Long memberId, + final Long reviewId + ) { + if (reviewLikeReader.alreadyLiked(memberId, reviewId)) { + throw new BusinessException(ErrorCode.ALREADY_REVIEW_LIKED); + } + } + + public void validateReviewUnlike( + final Long memberId, + final Long reviewId + ) { + if (!reviewLikeReader.alreadyLiked(memberId, reviewId)) { + throw new BusinessException(ErrorCode.NOT_REVIEW_LIKED); + } + } +} diff --git a/lime-domain/src/main/java/com/programmers/lime/domains/review/model/ReviewImageInfo.java b/lime-domain/src/main/java/com/programmers/lime/domains/review/model/ReviewImageInfo.java new file mode 100644 index 000000000..37f039ef5 --- /dev/null +++ b/lime-domain/src/main/java/com/programmers/lime/domains/review/model/ReviewImageInfo.java @@ -0,0 +1,13 @@ +package com.programmers.lime.domains.review.model; + +import java.util.List; + +import lombok.Builder; + +@Builder +public record ReviewImageInfo( + Long reviewId, + List imageUrls +) { + +} diff --git a/lime-domain/src/main/java/com/programmers/lime/domains/review/model/ReviewInfo.java b/lime-domain/src/main/java/com/programmers/lime/domains/review/model/ReviewInfo.java new file mode 100644 index 000000000..e9ed1d860 --- /dev/null +++ b/lime-domain/src/main/java/com/programmers/lime/domains/review/model/ReviewInfo.java @@ -0,0 +1,22 @@ +package com.programmers.lime.domains.review.model; + +import java.time.LocalDateTime; + +import lombok.Builder; + +@Builder +public record ReviewInfo( + + Long reviewId, + + int rate, + + String content, + + Long likeCount, + + LocalDateTime createdAt, + + LocalDateTime updatedAt +) { +} diff --git a/lime-domain/src/main/java/com/programmers/lime/domains/review/model/ReviewSummary.java b/lime-domain/src/main/java/com/programmers/lime/domains/review/model/ReviewSummary.java index 1ad9098bf..d069645f9 100644 --- a/lime-domain/src/main/java/com/programmers/lime/domains/review/model/ReviewSummary.java +++ b/lime-domain/src/main/java/com/programmers/lime/domains/review/model/ReviewSummary.java @@ -16,8 +16,25 @@ public record ReviewSummary( List imageUrls, + Long likeCount, + LocalDateTime createdAt, LocalDateTime updatedAt ) { + + public static ReviewSummary of( + final ReviewInfo reviewInfo, + final List imageUrls + ) { + return ReviewSummary.builder() + .reviewId(reviewInfo.reviewId()) + .rate(reviewInfo.rate()) + .content(reviewInfo.content()) + .imageUrls(imageUrls) + .likeCount(reviewInfo.likeCount()) + .createdAt(reviewInfo.createdAt()) + .updatedAt(reviewInfo.updatedAt()) + .build(); + } } diff --git a/lime-domain/src/main/java/com/programmers/lime/domains/review/repository/ReviewLikeRepository.java b/lime-domain/src/main/java/com/programmers/lime/domains/review/repository/ReviewLikeRepository.java new file mode 100644 index 000000000..e5fdb5010 --- /dev/null +++ b/lime-domain/src/main/java/com/programmers/lime/domains/review/repository/ReviewLikeRepository.java @@ -0,0 +1,19 @@ +package com.programmers.lime.domains.review.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.programmers.lime.domains.review.domain.Review; +import com.programmers.lime.domains.review.domain.ReviewLike; + +public interface ReviewLikeRepository extends JpaRepository { + + void deleteReviewLikeByMemberIdAndReview( + final Long memberId, + final Review review + ); + + boolean existsByMemberIdAndReviewId( + final Long memberId, + final Long reviewId + ); +} diff --git a/lime-domain/src/main/java/com/programmers/lime/domains/review/repository/ReviewRepositoryForCursor.java b/lime-domain/src/main/java/com/programmers/lime/domains/review/repository/ReviewRepositoryForCursor.java index b4f8dcd05..cd7e640ba 100644 --- a/lime-domain/src/main/java/com/programmers/lime/domains/review/repository/ReviewRepositoryForCursor.java +++ b/lime-domain/src/main/java/com/programmers/lime/domains/review/repository/ReviewRepositoryForCursor.java @@ -4,8 +4,9 @@ import com.programmers.lime.domains.review.model.MemberInfoWithReviewId; import com.programmers.lime.domains.review.model.ReviewCursorIdInfo; +import com.programmers.lime.domains.review.model.ReviewImageInfo; +import com.programmers.lime.domains.review.model.ReviewInfo; import com.programmers.lime.domains.review.model.ReviewSortCondition; -import com.programmers.lime.domains.review.model.ReviewSummary; public interface ReviewRepositoryForCursor { List findAllByCursor( @@ -17,7 +18,9 @@ List findAllByCursor( List getMemberInfos(List reviewIds); - List getReviewSummaries(List reviewIds); + List getReviewImageInfos(List reviewIds); + + List getReviewInfo(List reviewIds); int getReviewCount(Long itemId); } diff --git a/lime-domain/src/main/java/com/programmers/lime/domains/review/repository/ReviewRepositoryForCursorImpl.java b/lime-domain/src/main/java/com/programmers/lime/domains/review/repository/ReviewRepositoryForCursorImpl.java index 37bba065f..4a7e0cb59 100644 --- a/lime-domain/src/main/java/com/programmers/lime/domains/review/repository/ReviewRepositoryForCursorImpl.java +++ b/lime-domain/src/main/java/com/programmers/lime/domains/review/repository/ReviewRepositoryForCursorImpl.java @@ -3,6 +3,7 @@ import static com.programmers.lime.domains.member.domain.QMember.*; import static com.programmers.lime.domains.review.domain.QReview.*; import static com.programmers.lime.domains.review.domain.QReviewImage.*; +import static com.programmers.lime.domains.review.domain.QReviewLike.*; import static com.querydsl.core.group.GroupBy.*; import java.util.List; @@ -10,8 +11,9 @@ import com.programmers.lime.domains.member.model.MemberInfo; import com.programmers.lime.domains.review.model.MemberInfoWithReviewId; import com.programmers.lime.domains.review.model.ReviewCursorIdInfo; +import com.programmers.lime.domains.review.model.ReviewImageInfo; +import com.programmers.lime.domains.review.model.ReviewInfo; import com.programmers.lime.domains.review.model.ReviewSortCondition; -import com.programmers.lime.domains.review.model.ReviewSummary; import com.querydsl.core.types.ConstantImpl; import com.querydsl.core.types.Order; import com.querydsl.core.types.OrderSpecifier; @@ -51,10 +53,13 @@ public List findAllByCursor( ) .from(review) .where( - cursorIdCondition(cursorId, reviewSortCondition), - review.itemId.eq(itemId) + review.itemId.eq(itemId), + whereCursorIdCondition(cursorId, reviewSortCondition) ) .limit(pageSize) + .leftJoin(reviewLike).on(review.eq(reviewLike.review)) + .groupBy(review.id) + .having(havingCursorIdCondition(cursorId, reviewSortCondition)) .orderBy(orderBySortCondition(reviewSortCondition), orderByIdSortCondition(reviewSortCondition)) .fetch(); } @@ -82,29 +87,37 @@ public List getMemberInfos(final List reviewIds) { } @Override - public List getReviewSummaries(final List reviewIds) { + public List getReviewImageInfos(final List reviewIds) { return jpaQueryFactory - .select( - review - ) - .from(review) + .selectFrom(review) .where(review.id.in(reviewIds)) - .leftJoin(reviewImage).on(review.eq(reviewImage.review)) + .leftJoin(reviewImage).on(reviewImage.review.id.eq(review.id)) .transform( groupBy(review.id) - .list( - Projections.constructor( - ReviewSummary.class, - review.id, - review.rating, - review.content, - list( - reviewImage.imageUrl - ), - review.createdAt, - review.modifiedAt - ) - ) + .list(Projections.constructor( + ReviewImageInfo.class, + review.id, + list(reviewImage.imageUrl) + ) + )); + } + + @Override + public List getReviewInfo(final List reviewIds) { + return jpaQueryFactory + .selectFrom(review) + .where(review.id.in(reviewIds)) + .leftJoin(reviewLike).on(review.eq(reviewLike.review)) + .groupBy(review.id, review.rating, review.content, review.createdAt, review.modifiedAt) + .transform(groupBy(review.id, review.rating, review.content, review.createdAt, review.modifiedAt, reviewLike.id.count()) + .list(Projections.constructor(ReviewInfo.class, + review.id, + review.rating, + review.content, + reviewLike.id.count(), + review.createdAt, + review.modifiedAt + )) ); } @@ -122,6 +135,7 @@ private OrderSpecifier orderBySortCondition(ReviewSortCondition reviewSortCon return switch (reviewSortCondition) { case LOWEST_RATE -> new OrderSpecifier<>(Order.ASC, review.rating); case HIGHEST_RATE -> new OrderSpecifier<>(Order.DESC, review.rating); + case LIKE_COUNT_DESC -> new OrderSpecifier<>(Order.DESC, reviewLike.id.count()); default -> new OrderSpecifier<>(Order.DESC, review.createdAt); }; } @@ -130,11 +144,12 @@ private OrderSpecifier orderByIdSortCondition(ReviewSortCondition reviewSortC return switch (reviewSortCondition) { case LOWEST_RATE -> new OrderSpecifier<>(Order.ASC, review.id); case HIGHEST_RATE -> new OrderSpecifier<>(Order.DESC, review.id); + case LIKE_COUNT_DESC -> new OrderSpecifier<>(Order.DESC, review.id); default -> new OrderSpecifier<>(Order.DESC, review.id); }; } - private BooleanExpression cursorIdCondition(final String cursorId, final ReviewSortCondition reviewSortCondition) { + private BooleanExpression whereCursorIdCondition(final String cursorId, final ReviewSortCondition reviewSortCondition) { if (cursorId == null) { return null; } @@ -142,14 +157,29 @@ private BooleanExpression cursorIdCondition(final String cursorId, final ReviewS return switch (reviewSortCondition) { case LOWEST_RATE -> generateCursorId(reviewSortCondition).gt(cursorId); case HIGHEST_RATE -> generateCursorId(reviewSortCondition).lt(cursorId); + case LIKE_COUNT_DESC -> null; default -> generateCursorId(reviewSortCondition).lt(cursorId); }; } + private BooleanExpression havingCursorIdCondition(final String cursorId, final ReviewSortCondition reviewSortCondition) { + if (cursorId == null) { + return null; + } + + return switch (reviewSortCondition) { + case LOWEST_RATE -> null; + case HIGHEST_RATE -> null; + case LIKE_COUNT_DESC -> generateCursorId(reviewSortCondition).lt(cursorId); + default -> null; + }; + } + public StringExpression generateCursorId(ReviewSortCondition reviewSortCondition) { return switch (reviewSortCondition) { case LOWEST_RATE -> concatWithLpadZero(review.rating.stringValue(), review.id.stringValue()); case HIGHEST_RATE -> concatWithLpadZero(review.rating.stringValue(), review.id.stringValue()); + case LIKE_COUNT_DESC -> concatWithLpadZero(reviewLike.id.count().stringValue(), review.id.stringValue()); default -> { StringTemplate dateStr = Expressions.stringTemplate( "DATE_FORMAT({0}, {1})", @@ -161,6 +191,8 @@ public StringExpression generateCursorId(ReviewSortCondition reviewSortCondition }; } + + // str1 문자열을 8자리로 만들고, 앞에 0으로 채움 // str2 문자열을 8자리로 만들고, 앞에 0으로 채움 // str1 + str2 반환 (총 16자리) diff --git a/lime-domain/src/test/java/com/programmers/lime/domains/review/implementation/ReviewCursorReaderTest.java b/lime-domain/src/test/java/com/programmers/lime/domains/review/implementation/ReviewCursorReaderTest.java index 00887477c..988872347 100644 --- a/lime-domain/src/test/java/com/programmers/lime/domains/review/implementation/ReviewCursorReaderTest.java +++ b/lime-domain/src/test/java/com/programmers/lime/domains/review/implementation/ReviewCursorReaderTest.java @@ -24,10 +24,14 @@ import com.programmers.lime.domains.member.domain.MemberInfoWithReviewIdBuilder; import com.programmers.lime.domains.review.ReviewCursorIdInfoBuilder; import com.programmers.lime.domains.review.ReviewCursorSummaryBuilder; +import com.programmers.lime.domains.review.ReviewImageInfoBuilder; +import com.programmers.lime.domains.review.ReviewInfoBuilder; import com.programmers.lime.domains.review.ReviewSummaryBuilder; import com.programmers.lime.domains.review.model.MemberInfoWithReviewId; import com.programmers.lime.domains.review.model.ReviewCursorIdInfo; import com.programmers.lime.domains.review.model.ReviewCursorSummary; +import com.programmers.lime.domains.review.model.ReviewImageInfo; +import com.programmers.lime.domains.review.model.ReviewInfo; import com.programmers.lime.domains.review.model.ReviewSummary; import com.programmers.lime.domains.review.repository.ReviewRepository; @@ -59,15 +63,24 @@ void readByCursor(final CursorPageParameters parameters) { Long memberId = 0L; List reviewCursorIdInfos = ReviewCursorIdInfoBuilder.buildMany(); + List reviewIds = reviewCursorIdInfos.stream().map(ReviewCursorIdInfo::reviewId).toList(); - List reviewCursorSummaries = ReviewCursorSummaryBuilder.buildMany(reviewIds); + List memberInfoWithReviewIds = MemberInfoWithReviewIdBuilder.buildMany(reviewIds); + + List reviewInfos = ReviewInfoBuilder.buildMany(reviewIds); + + List reviewImageInfos = ReviewImageInfoBuilder.buildMany(reviewIds); + + List reviewSummaries = ReviewSummaryBuilder.buildMany(reviewInfos, reviewImageInfos); + + List reviewCursorSummaries = ReviewCursorSummaryBuilder.buildMany( + reviewIds, reviewSummaries + ); CursorSummary expectedReviewCursorSummary = CursorUtils .getCursorSummaries(reviewCursorSummaries); - List memberInfoWithReviewIds = MemberInfoWithReviewIdBuilder.buildMany(reviewIds); - List reviewSummaries = ReviewSummaryBuilder.buildMany(reviewIds); int pageSize = parameters.size() == null ? DEFAULT_PAGE_SIZE : parameters.size(); given(reviewRepository.findAllByCursor( @@ -81,8 +94,11 @@ void readByCursor(final CursorPageParameters parameters) { given(reviewRepository.getMemberInfos(reviewIds)) .willReturn(memberInfoWithReviewIds); - given(reviewRepository.getReviewSummaries(reviewIds)) - .willReturn(reviewSummaries); + given(reviewRepository.getReviewInfo(reviewIds)) + .willReturn(reviewInfos); + + given(reviewRepository.getReviewImageInfos(reviewIds)) + .willReturn(reviewImageInfos); // when CursorSummary actualReviewCursorSummary = reviewCursorReader.readByCursor( diff --git a/lime-domain/src/testFixtures/java/com/programmers/lime/domains/review/ReviewCursorSummaryBuilder.java b/lime-domain/src/testFixtures/java/com/programmers/lime/domains/review/ReviewCursorSummaryBuilder.java index 34b4a94fb..65128aac4 100644 --- a/lime-domain/src/testFixtures/java/com/programmers/lime/domains/review/ReviewCursorSummaryBuilder.java +++ b/lime-domain/src/testFixtures/java/com/programmers/lime/domains/review/ReviewCursorSummaryBuilder.java @@ -1,10 +1,12 @@ package com.programmers.lime.domains.review; import java.util.List; +import java.util.stream.IntStream; import java.util.stream.LongStream; import com.programmers.lime.domains.member.domain.MemberInfoBuilder; import com.programmers.lime.domains.review.model.ReviewCursorSummary; +import com.programmers.lime.domains.review.model.ReviewSummary; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -12,18 +14,23 @@ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class ReviewCursorSummaryBuilder { - public static ReviewCursorSummary build(final Long reviewId) { + public static ReviewCursorSummary build(final Long reviewId, final ReviewSummary reviewSummary) { return ReviewCursorSummary.builder() .cursorId("202301010000000000000" + reviewId) - .reviewSummary(ReviewSummaryBuilder.build(reviewId)) + .reviewSummary(reviewSummary) .memberInfo(MemberInfoBuilder.build(reviewId)) .isReviewed(false) .build(); } - public static List buildMany(final List reviewIds) { - return reviewIds.stream() - .map(ReviewCursorSummaryBuilder::build) + public static List buildMany(final List reviewIds, final List reviewSummaries) { + return IntStream.range(0, reviewIds.size()) + .mapToObj( + idx -> build( + reviewIds.get(idx), + reviewSummaries.get(idx) + ) + ) .toList(); } } diff --git a/lime-domain/src/testFixtures/java/com/programmers/lime/domains/review/ReviewImageInfoBuilder.java b/lime-domain/src/testFixtures/java/com/programmers/lime/domains/review/ReviewImageInfoBuilder.java new file mode 100644 index 000000000..0ad036461 --- /dev/null +++ b/lime-domain/src/testFixtures/java/com/programmers/lime/domains/review/ReviewImageInfoBuilder.java @@ -0,0 +1,24 @@ +package com.programmers.lime.domains.review; + +import java.util.List; + +import com.programmers.lime.domains.review.model.ReviewImageInfo; + +import lombok.Builder; + +@Builder +public class ReviewImageInfoBuilder { + + public static ReviewImageInfo build(final Long reviewId, final List imageUrls) { + return ReviewImageInfo.builder() + .reviewId(reviewId) + .imageUrls(imageUrls) + .build(); + } + + public static List buildMany(final List reviewIds) { + return reviewIds.stream() + .map(reviewId -> build(reviewId, List.of("image" + reviewId))) + .toList(); + } +} diff --git a/lime-domain/src/testFixtures/java/com/programmers/lime/domains/review/ReviewInfoBuilder.java b/lime-domain/src/testFixtures/java/com/programmers/lime/domains/review/ReviewInfoBuilder.java new file mode 100644 index 000000000..b4d0b7b5c --- /dev/null +++ b/lime-domain/src/testFixtures/java/com/programmers/lime/domains/review/ReviewInfoBuilder.java @@ -0,0 +1,22 @@ +package com.programmers.lime.domains.review; + +import java.util.List; + +import com.programmers.lime.domains.review.model.ReviewInfo; + +public class ReviewInfoBuilder { + + public static ReviewInfo build(final Long reviewId) { + return ReviewInfo.builder() + .rate(reviewId.intValue() % 5 + 1) + .content("content" + reviewId) + .reviewId(reviewId) + .build(); + } + + public static List buildMany(final List reviewIds) { + return reviewIds.stream() + .map(ReviewInfoBuilder::build) + .toList(); + } +} diff --git a/lime-domain/src/testFixtures/java/com/programmers/lime/domains/review/ReviewSummaryBuilder.java b/lime-domain/src/testFixtures/java/com/programmers/lime/domains/review/ReviewSummaryBuilder.java index e0db58b88..e8d1fcc59 100644 --- a/lime-domain/src/testFixtures/java/com/programmers/lime/domains/review/ReviewSummaryBuilder.java +++ b/lime-domain/src/testFixtures/java/com/programmers/lime/domains/review/ReviewSummaryBuilder.java @@ -1,7 +1,10 @@ package com.programmers.lime.domains.review; import java.util.List; +import java.util.stream.IntStream; +import com.programmers.lime.domains.review.model.ReviewImageInfo; +import com.programmers.lime.domains.review.model.ReviewInfo; import com.programmers.lime.domains.review.model.ReviewSummary; import lombok.AccessLevel; @@ -10,17 +13,27 @@ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class ReviewSummaryBuilder { - public static ReviewSummary build(final Long reviewId) { + public static ReviewSummary build(final ReviewInfo reviewInfo, final List imageUrls) { + Long reviewId = reviewInfo.reviewId(); return ReviewSummary.builder() .rate(reviewId.intValue() % 5 + 1) .content("content" + reviewId) .reviewId(reviewId) + .imageUrls(imageUrls) .build(); } - public static List buildMany(final List reviewIds) { - return reviewIds.stream() - .map(ReviewSummaryBuilder::build) + public static List buildMany( + final List reviewInfos, + final List reviewImageInfos + ) { + return IntStream.range(0, reviewInfos.size()) + .mapToObj( + idx -> build( + reviewInfos.get(idx), + reviewImageInfos.get(idx).imageUrls() + ) + ) .toList(); } }