From 6f3a434b8394b71e923df38afbafc2213a9923fb Mon Sep 17 00:00:00 2001 From: Sangmin Lee Date: Thu, 28 Nov 2024 20:31:14 +0900 Subject: [PATCH 1/2] =?UTF-8?q?[DVK-123]=20refactor:=20=EC=A0=84=EC=9E=90?= =?UTF-8?q?=EC=B1=85=20=EB=A6=AC=EB=B7=B0=20DTO=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/devooks/backend/ebook/v1/dto/EbookDetailView.kt | 4 ++-- .../ebook/v1/dto/{ReviewView.kt => EbookReviewView.kt} | 2 +- src/main/kotlin/com/devooks/backend/ebook/v1/dto/EbookView.kt | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/main/kotlin/com/devooks/backend/ebook/v1/dto/{ReviewView.kt => EbookReviewView.kt} (89%) diff --git a/src/main/kotlin/com/devooks/backend/ebook/v1/dto/EbookDetailView.kt b/src/main/kotlin/com/devooks/backend/ebook/v1/dto/EbookDetailView.kt index 3752634..0fef11b 100644 --- a/src/main/kotlin/com/devooks/backend/ebook/v1/dto/EbookDetailView.kt +++ b/src/main/kotlin/com/devooks/backend/ebook/v1/dto/EbookDetailView.kt @@ -23,7 +23,7 @@ data class EbookDetailView( @Schema(description = "판매자 정보") val seller: EbookSellerView, @Schema(description = "리뷰 정보") - val review: ReviewView, + val review: EbookReviewView, @Schema(description = "페이지 개수") val pageCount: Int, @Schema(description = "PDF 식별자") @@ -45,7 +45,7 @@ data class EbookDetailView( descriptionImageList = this.descriptionImageList, wishlistId = this.wishlistId, title = this.title, - review = ReviewView( + review = EbookReviewView( rating = this.reviewRating, count = this.reviewCount, ), diff --git a/src/main/kotlin/com/devooks/backend/ebook/v1/dto/ReviewView.kt b/src/main/kotlin/com/devooks/backend/ebook/v1/dto/EbookReviewView.kt similarity index 89% rename from src/main/kotlin/com/devooks/backend/ebook/v1/dto/ReviewView.kt rename to src/main/kotlin/com/devooks/backend/ebook/v1/dto/EbookReviewView.kt index 9762062..d8fbef7 100644 --- a/src/main/kotlin/com/devooks/backend/ebook/v1/dto/ReviewView.kt +++ b/src/main/kotlin/com/devooks/backend/ebook/v1/dto/EbookReviewView.kt @@ -2,7 +2,7 @@ package com.devooks.backend.ebook.v1.dto import io.swagger.v3.oas.annotations.media.Schema -data class ReviewView( +data class EbookReviewView( @Schema(description = "평점") val rating: Double, @Schema(description = "개수", implementation = Int::class) diff --git a/src/main/kotlin/com/devooks/backend/ebook/v1/dto/EbookView.kt b/src/main/kotlin/com/devooks/backend/ebook/v1/dto/EbookView.kt index 7bea1da..5d622d8 100644 --- a/src/main/kotlin/com/devooks/backend/ebook/v1/dto/EbookView.kt +++ b/src/main/kotlin/com/devooks/backend/ebook/v1/dto/EbookView.kt @@ -17,7 +17,7 @@ data class EbookView( @Schema(description = "판매자 정보") val seller: EbookSellerView, @Schema(description = "리뷰 정보") - val review: ReviewView, + val review: EbookReviewView, @Schema(description = "생성 날짜") val createdDate: Instant, @Schema(description = "수정 날짜") @@ -39,7 +39,7 @@ data class EbookView( nickname = this.sellerNickname, profileImagePath = this.sellerProfileImagePath ?: "", ), - review = ReviewView( + review = EbookReviewView( rating = this.reviewRating, count = this.reviewCount, ), From 08c4ac0d6488b43c8c8a1dab88c9db383af2dcdd Mon Sep 17 00:00:00 2001 From: Sangmin Lee Date: Thu, 28 Nov 2024 21:02:42 +0900 Subject: [PATCH 2/2] =?UTF-8?q?[DVK-123]=20fix:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../review/v1/controller/ReviewController.kt | 12 ++++-- .../review/v1/dto/CreateReviewResponse.kt | 4 +- .../review/v1/dto/ModifyReviewResponse.kt | 6 ++- .../backend/review/v1/dto/ReviewRow.kt | 17 +++++++++ .../backend/review/v1/dto/ReviewView.kt | 21 +++++++++-- .../backend/review/v1/dto/WriterView.kt | 26 +++++++++++++ .../v1/repository/ReviewQueryRepository.kt | 37 ++++++++++++++----- .../review/v1/service/ReviewService.kt | 3 +- .../controller/ReviewCommentControllerTest.kt | 7 ++-- .../v1/controller/ReviewControllerTest.kt | 4 +- 10 files changed, 112 insertions(+), 25 deletions(-) create mode 100644 src/main/kotlin/com/devooks/backend/review/v1/dto/ReviewRow.kt create mode 100644 src/main/kotlin/com/devooks/backend/review/v1/dto/WriterView.kt diff --git a/src/main/kotlin/com/devooks/backend/review/v1/controller/ReviewController.kt b/src/main/kotlin/com/devooks/backend/review/v1/controller/ReviewController.kt index 2f9919b..467da98 100644 --- a/src/main/kotlin/com/devooks/backend/review/v1/controller/ReviewController.kt +++ b/src/main/kotlin/com/devooks/backend/review/v1/controller/ReviewController.kt @@ -5,6 +5,8 @@ import com.devooks.backend.auth.v1.service.TokenService import com.devooks.backend.common.dto.PageResponse import com.devooks.backend.common.dto.PageResponse.Companion.toResponse import com.devooks.backend.ebook.v1.service.EbookService +import com.devooks.backend.member.v1.domain.Member +import com.devooks.backend.member.v1.service.MemberService import com.devooks.backend.review.v1.controller.docs.ReviewControllerDocs import com.devooks.backend.review.v1.domain.Review import com.devooks.backend.review.v1.dto.CreateReviewCommand @@ -18,6 +20,7 @@ import com.devooks.backend.review.v1.dto.ModifyReviewCommand import com.devooks.backend.review.v1.dto.ModifyReviewRequest import com.devooks.backend.review.v1.dto.ModifyReviewResponse import com.devooks.backend.review.v1.dto.ModifyReviewResponse.Companion.toModifyReviewResponse +import com.devooks.backend.review.v1.dto.ReviewRow import com.devooks.backend.review.v1.dto.ReviewView import com.devooks.backend.review.v1.dto.ReviewView.Companion.toReviewView import com.devooks.backend.review.v1.service.ReviewEventService @@ -47,6 +50,7 @@ class ReviewController( private val transactionService: TransactionService, private val ebookService: EbookService, private val reviewEventService: ReviewEventService, + private val memberService: MemberService, ) : ReviewControllerDocs { @Transactional @@ -63,8 +67,9 @@ class ReviewController( ebookService.validate(command) transactionService.validate(command) val review: Review = reviewService.create(command) + val member: Member = memberService.findById(review.writerMemberId) reviewEventService.publish(review) - return review.toCreateReviewResponse() + return review.toCreateReviewResponse(member) } @GetMapping @@ -77,7 +82,7 @@ class ReviewController( count: Int, ): PageResponse { val command = GetReviewsCommand(ebookId, page, count) - val reviewList: Page = reviewService.get(command) + val reviewList: Page = reviewService.get(command) return reviewList.map { it.toReviewView() }.toResponse() } @@ -95,7 +100,8 @@ class ReviewController( val requesterId = tokenService.getMemberId(Authorization(authorization)) val command: ModifyReviewCommand = request.toCommand(reviewId, requesterId) val review: Review = reviewService.modify(command) - return review.toModifyReviewResponse() + val member: Member = memberService.findById(review.writerMemberId) + return review.toModifyReviewResponse(member) } @Transactional diff --git a/src/main/kotlin/com/devooks/backend/review/v1/dto/CreateReviewResponse.kt b/src/main/kotlin/com/devooks/backend/review/v1/dto/CreateReviewResponse.kt index 3a8615f..6dbeee8 100644 --- a/src/main/kotlin/com/devooks/backend/review/v1/dto/CreateReviewResponse.kt +++ b/src/main/kotlin/com/devooks/backend/review/v1/dto/CreateReviewResponse.kt @@ -1,5 +1,6 @@ package com.devooks.backend.review.v1.dto +import com.devooks.backend.member.v1.domain.Member import com.devooks.backend.review.v1.domain.Review import com.devooks.backend.review.v1.dto.ReviewView.Companion.toReviewView @@ -7,6 +8,7 @@ data class CreateReviewResponse( val review: ReviewView, ) { companion object { - fun Review.toCreateReviewResponse() = CreateReviewResponse(this.toReviewView()) + fun Review.toCreateReviewResponse(member: Member) = + CreateReviewResponse(this.toReviewView(member)) } } diff --git a/src/main/kotlin/com/devooks/backend/review/v1/dto/ModifyReviewResponse.kt b/src/main/kotlin/com/devooks/backend/review/v1/dto/ModifyReviewResponse.kt index 9d4af9b..22af27d 100644 --- a/src/main/kotlin/com/devooks/backend/review/v1/dto/ModifyReviewResponse.kt +++ b/src/main/kotlin/com/devooks/backend/review/v1/dto/ModifyReviewResponse.kt @@ -1,12 +1,14 @@ package com.devooks.backend.review.v1.dto +import com.devooks.backend.member.v1.domain.Member import com.devooks.backend.review.v1.domain.Review import com.devooks.backend.review.v1.dto.ReviewView.Companion.toReviewView class ModifyReviewResponse( - val review: ReviewView + val review: ReviewView, ) { companion object { - fun Review.toModifyReviewResponse() = ModifyReviewResponse(this.toReviewView()) + fun Review.toModifyReviewResponse(member: Member) = + ModifyReviewResponse(this.toReviewView(member)) } } diff --git a/src/main/kotlin/com/devooks/backend/review/v1/dto/ReviewRow.kt b/src/main/kotlin/com/devooks/backend/review/v1/dto/ReviewRow.kt new file mode 100644 index 0000000..c7c2a2d --- /dev/null +++ b/src/main/kotlin/com/devooks/backend/review/v1/dto/ReviewRow.kt @@ -0,0 +1,17 @@ +package com.devooks.backend.review.v1.dto + +import java.time.Instant +import java.util.* + +data class ReviewRow( + val reviewId: UUID, + val content: String, + val rating: Int, + val ebookId: UUID, + val memberId: UUID, + val nickname: String, + val profileImagePath: String?, + val writtenDate: Instant, + val modifiedDate: Instant, + val commentCount: Int, +) diff --git a/src/main/kotlin/com/devooks/backend/review/v1/dto/ReviewView.kt b/src/main/kotlin/com/devooks/backend/review/v1/dto/ReviewView.kt index b0719e8..d52f5f6 100644 --- a/src/main/kotlin/com/devooks/backend/review/v1/dto/ReviewView.kt +++ b/src/main/kotlin/com/devooks/backend/review/v1/dto/ReviewView.kt @@ -1,6 +1,8 @@ package com.devooks.backend.review.v1.dto +import com.devooks.backend.member.v1.domain.Member import com.devooks.backend.review.v1.domain.Review +import com.devooks.backend.review.v1.dto.WriterView.Companion.toWriterView import io.swagger.v3.oas.annotations.media.Schema import java.time.Instant import java.util.* @@ -14,21 +16,32 @@ data class ReviewView( val content: String, @Schema(description = "전자책 식별자") val ebookId: UUID, - @Schema(description = "작성자 회원 식별자") - val writerMemberId: UUID, + @Schema(description = "작성자") + val writer: WriterView, @Schema(description = "작성 날짜") val writtenDate: Instant, @Schema(description = "수정 날짜") val modifiedDate: Instant, ) { companion object { - fun Review.toReviewView(): ReviewView = + fun ReviewRow.toReviewView(): ReviewView = + ReviewView( + id = this.reviewId, + rating = this.rating, + content = this.content, + ebookId = this.ebookId, + writer = this.toWriterView(), + writtenDate = this.writtenDate, + modifiedDate = this.modifiedDate, + ) + + fun Review.toReviewView(writer: Member): ReviewView = ReviewView( id = this.id, rating = this.rating, content = this.content, ebookId = this.ebookId, - writerMemberId = this.writerMemberId, + writer = writer.toWriterView(), writtenDate = this.writtenDate, modifiedDate = this.modifiedDate, ) diff --git a/src/main/kotlin/com/devooks/backend/review/v1/dto/WriterView.kt b/src/main/kotlin/com/devooks/backend/review/v1/dto/WriterView.kt new file mode 100644 index 0000000..968b7ec --- /dev/null +++ b/src/main/kotlin/com/devooks/backend/review/v1/dto/WriterView.kt @@ -0,0 +1,26 @@ +package com.devooks.backend.review.v1.dto + +import com.devooks.backend.member.v1.domain.Member +import java.util.* + +data class WriterView( + val memberId: UUID, + val nickname: String, + val profileImagePath: String, +) { + companion object { + fun ReviewRow.toWriterView() = + WriterView( + memberId = this.memberId, + nickname = this.nickname, + profileImagePath = this.profileImagePath ?: "", + ) + + fun Member.toWriterView() = + WriterView( + memberId = this.id, + nickname = this.nickname, + profileImagePath = this.profileImagePath, + ) + } +} diff --git a/src/main/kotlin/com/devooks/backend/review/v1/repository/ReviewQueryRepository.kt b/src/main/kotlin/com/devooks/backend/review/v1/repository/ReviewQueryRepository.kt index 668c4fe..c7744d1 100644 --- a/src/main/kotlin/com/devooks/backend/review/v1/repository/ReviewQueryRepository.kt +++ b/src/main/kotlin/com/devooks/backend/review/v1/repository/ReviewQueryRepository.kt @@ -2,9 +2,11 @@ package com.devooks.backend.review.v1.repository import com.devooks.backend.common.config.database.JooqR2dbcRepository import com.devooks.backend.jooq.tables.Review.Companion.REVIEW -import com.devooks.backend.jooq.tables.references.EBOOK -import com.devooks.backend.review.v1.domain.Review +import com.devooks.backend.jooq.tables.references.MEMBER +import com.devooks.backend.jooq.tables.references.REVIEW_COMMENT import com.devooks.backend.review.v1.dto.GetReviewsCommand +import com.devooks.backend.review.v1.dto.ReviewRow +import java.util.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import org.jooq.impl.DSL @@ -13,25 +15,42 @@ import org.springframework.stereotype.Repository @Repository class ReviewQueryRepository : JooqR2dbcRepository() { - suspend fun findBy(command: GetReviewsCommand): Flow = + suspend fun findBy(command: GetReviewsCommand): Flow = query { + val reviewCountSubQuery = + select( + REVIEW_COMMENT.REVIEW_ID, + DSL.count().`as`("comment_count") + ).from( + REVIEW_COMMENT + ).groupBy( + REVIEW_COMMENT.REVIEW_ID, + ).asTable("review_comment") + select( - REVIEW.REVIEW_ID.`as`("id"), - REVIEW.RATING, + REVIEW.REVIEW_ID, REVIEW.CONTENT, + REVIEW.RATING, REVIEW.EBOOK_ID, - REVIEW.WRITER_MEMBER_ID, + MEMBER.MEMBER_ID, + MEMBER.NICKNAME, + MEMBER.PROFILE_IMAGE_PATH, REVIEW.WRITTEN_DATE, - REVIEW.MODIFIED_DATE + REVIEW.MODIFIED_DATE, + DSL.coalesce(reviewCountSubQuery.field("comment_count"), 0) + .`as`("comment_count") ).from( REVIEW - .join(EBOOK).on(EBOOK.EBOOK_ID.eq(REVIEW.EBOOK_ID)) + .join(MEMBER).on(REVIEW.WRITER_MEMBER_ID.eq(MEMBER.MEMBER_ID)) + .leftJoin(reviewCountSubQuery).on( + REVIEW.REVIEW_ID.eq(reviewCountSubQuery.field("review_id", UUID::class.java)) + ) ).where( REVIEW.EBOOK_ID.eq(command.ebookId) ).orderBy( REVIEW.WRITTEN_DATE.desc(), ).offset(command.offset).limit(command.limit) - }.map { it.into(Review::class.java) } + }.map { it.into(ReviewRow::class.java) } suspend fun countBy(command: GetReviewsCommand): Flow = query { diff --git a/src/main/kotlin/com/devooks/backend/review/v1/service/ReviewService.kt b/src/main/kotlin/com/devooks/backend/review/v1/service/ReviewService.kt index c877479..9fb5b32 100644 --- a/src/main/kotlin/com/devooks/backend/review/v1/service/ReviewService.kt +++ b/src/main/kotlin/com/devooks/backend/review/v1/service/ReviewService.kt @@ -6,6 +6,7 @@ import com.devooks.backend.review.v1.dto.CreateReviewCommentCommand import com.devooks.backend.review.v1.dto.DeleteReviewCommand import com.devooks.backend.review.v1.dto.GetReviewsCommand import com.devooks.backend.review.v1.dto.ModifyReviewCommand +import com.devooks.backend.review.v1.dto.ReviewRow import com.devooks.backend.review.v1.entity.ReviewEntity import com.devooks.backend.review.v1.error.ReviewError import com.devooks.backend.review.v1.repository.ReviewQueryRepository @@ -33,7 +34,7 @@ class ReviewService( return reviewRepository.save(entity).toDomain() } - suspend fun get(command: GetReviewsCommand): Page { + suspend fun get(command: GetReviewsCommand): Page { val reviews = reviewQueryRepository.findBy(command) val count = reviewQueryRepository.countBy(command) return PageImpl(reviews.toList(), command.pageable, count.first()) diff --git a/src/test/kotlin/com/devooks/backend/review/v1/controller/ReviewCommentControllerTest.kt b/src/test/kotlin/com/devooks/backend/review/v1/controller/ReviewCommentControllerTest.kt index 5c0aa16..773e0b0 100644 --- a/src/test/kotlin/com/devooks/backend/review/v1/controller/ReviewCommentControllerTest.kt +++ b/src/test/kotlin/com/devooks/backend/review/v1/controller/ReviewCommentControllerTest.kt @@ -144,12 +144,13 @@ internal class ReviewCommentControllerTest @Autowired constructor( assertThat(reviewComment.content).isEqualTo(request.content) delay(100) - val notification = notificationRepository.findAll().toList().find { it.receiverId == review.writerMemberId }!! + val notification = notificationRepository.findAll().toList() + .find { it.receiverId == review.writer.memberId }!! assertThat(notification.type).isEqualTo(NotificationType.REVIEW_COMMENT) - assertThat(notification.receiverId).isEqualTo(review.writerMemberId) + assertThat(notification.receiverId).isEqualTo(review.writer.memberId) assertThat(notification.note["ebookId"]).isEqualTo(review.ebookId.toString()) assertThat(notification.note["reviewId"]).isEqualTo(review.id.toString()) - assertThat(notification.note["receiverId"]).isEqualTo(review.writerMemberId.toString()) + assertThat(notification.note["receiverId"]).isEqualTo(review.writer.memberId.toString()) assertThat(notification.note["commenterName"]).isEqualTo(expectedMember2.nickname) } diff --git a/src/test/kotlin/com/devooks/backend/review/v1/controller/ReviewControllerTest.kt b/src/test/kotlin/com/devooks/backend/review/v1/controller/ReviewControllerTest.kt index fc7f382..6dc0e71 100644 --- a/src/test/kotlin/com/devooks/backend/review/v1/controller/ReviewControllerTest.kt +++ b/src/test/kotlin/com/devooks/backend/review/v1/controller/ReviewControllerTest.kt @@ -141,7 +141,7 @@ internal class ReviewControllerTest @Autowired constructor( assertThat(review.rating).isEqualTo(createReviewRequest.rating) assertThat(review.content).isEqualTo(createReviewRequest.content) assertThat(review.ebookId).isEqualTo(createReviewRequest.ebookId) - assertThat(review.writerMemberId).isEqualTo(expectedMember2.id) + assertThat(review.writer.memberId).isEqualTo(expectedMember2.id) delay(100) val notification = notificationRepository.findAll().toList()[0] @@ -174,7 +174,7 @@ internal class ReviewControllerTest @Autowired constructor( assertThat(review.ebookId).isEqualTo(createReviewResponse.ebookId) assertThat(review.content).isEqualTo(createReviewResponse.content) assertThat(review.rating).isEqualTo(createReviewResponse.rating) - assertThat(review.writerMemberId).isEqualTo(createReviewResponse.writerMemberId) + assertThat(review.writer).isEqualTo(createReviewResponse.writer) assertThat(review.writtenDate.toEpochMilli()).isEqualTo(createReviewResponse.writtenDate.toEpochMilli()) assertThat(review.modifiedDate.toEpochMilli()).isEqualTo(createReviewResponse.modifiedDate.toEpochMilli()) }