Skip to content

Commit

Permalink
feat: 회원의 리뷰 내역 조회 API 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
Combi153 committed May 9, 2024
1 parent 65e6360 commit 129d715
Show file tree
Hide file tree
Showing 4 changed files with 223 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.petqua.presentation.product

import com.petqua.application.product.dto.MemberProductReviewsResponse
import com.petqua.application.product.dto.ProductReviewStatisticsResponse
import com.petqua.application.product.dto.ProductReviewsResponse
import com.petqua.application.product.review.ProductReviewFacadeService
Expand All @@ -9,6 +10,7 @@ import com.petqua.domain.auth.LoginMember
import com.petqua.domain.auth.LoginMemberOrGuest
import com.petqua.presentation.product.dto.CreateReviewRequest
import com.petqua.presentation.product.dto.ReadAllProductReviewsRequest
import com.petqua.presentation.product.dto.ReadMemberProductReviewsRequest
import com.petqua.presentation.product.dto.UpdateReviewRecommendationRequest
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.responses.ApiResponse
Expand Down Expand Up @@ -63,14 +65,27 @@ class ProductReviewController(
@PathVariable productId: Long,
): ResponseEntity<ProductReviewsResponse> {
val responses = productReviewFacadeService.readAll(
request.toCommand(
request.toQuery(
productId = productId,
loginMemberOrGuest = loginMemberOrGuest,
)
)
return ResponseEntity.ok(responses)
}

@Operation(summary = "내 후기 조회 API", description = "내가 작성한 상품의 후기를 조회합니다")
@ApiResponse(responseCode = "200", description = "상품 후기 조건 조회 성공")
@SecurityRequirement(name = ACCESS_TOKEN_SECURITY_SCHEME_KEY)
@GetMapping("/product-reviews/me")
fun readMemberProductReviews(
@Auth loginMember: LoginMember,
request: ReadMemberProductReviewsRequest,
): ResponseEntity<MemberProductReviewsResponse> {
val query = request.toQuery(loginMember)
val response = productReviewFacadeService.readMemberProductReviews(query)
return ResponseEntity.ok(response)
}

@Operation(summary = "상품 후기 통계 조회 API", description = "상품의 후기 통계를 조회합니다")
@ApiResponse(responseCode = "200", description = "상품 후기 통계 조회 성공")
@GetMapping("/products/{productId}/review-statistics")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.petqua.presentation.product.dto

import com.petqua.application.product.dto.MemberProductReviewReadQuery
import com.petqua.application.product.dto.ProductReviewCreateCommand
import com.petqua.application.product.dto.ProductReviewReadQuery
import com.petqua.application.product.dto.UpdateReviewRecommendationCommand
import com.petqua.common.domain.dto.PAGING_LIMIT_CEILING
import com.petqua.domain.auth.LoginMember
import com.petqua.domain.auth.LoginMemberOrGuest
import com.petqua.domain.product.review.ProductReviewSorter
import com.petqua.domain.product.review.ProductReviewSorter.REVIEW_DATE_DESC
Expand Down Expand Up @@ -72,7 +74,7 @@ data class ReadAllProductReviewsRequest(
)
val limit: Int = PAGING_LIMIT_CEILING,
) {
fun toCommand(productId: Long, loginMemberOrGuest: LoginMemberOrGuest): ProductReviewReadQuery {
fun toQuery(productId: Long, loginMemberOrGuest: LoginMemberOrGuest): ProductReviewReadQuery {
return ProductReviewReadQuery(
productId = productId,
loginMemberOrGuest = loginMemberOrGuest,
Expand All @@ -85,6 +87,28 @@ data class ReadAllProductReviewsRequest(
}
}

data class ReadMemberProductReviewsRequest(
@Schema(
description = "마지막으로 조회한 후기의 Id",
example = "1",
)
val lastViewedId: Long,

@Schema(
description = "조회할 상품 개수",
defaultValue = "20"
)
val limit: Int = PAGING_LIMIT_CEILING,
) {
fun toQuery(loginMember: LoginMember): MemberProductReviewReadQuery {
return MemberProductReviewReadQuery(
memberId = loginMember.memberId,
lastViewedId = lastViewedId,
limit = limit
)
}
}

data class UpdateReviewRecommendationRequest(
@Schema(
description = "상품 후기 id",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,25 @@ fun requestUpdateReviewRecommendation(
response()
}
}

fun requestReadMemberProductReviews(
lastViewedId: Long = -1,
limit: Int = 20,
accessToken: String,
): Response {
return Given {
log().all()
contentType(APPLICATION_JSON_VALUE)
auth().preemptive().oauth2(accessToken)
params(
"lastViewedId", lastViewedId,
"limit", limit,
)
} When {
get("/product-reviews/me")
} Then {
log().all()
} Extract {
response()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,20 @@ package com.petqua.presentation.product

import com.amazonaws.services.s3.AmazonS3
import com.ninjasquad.springmockk.SpykBean
import com.petqua.application.product.dto.MemberProductReviewsResponse
import com.petqua.application.product.dto.ProductReviewStatisticsResponse
import com.petqua.application.product.dto.ProductReviewsResponse
import com.petqua.common.domain.findByIdOrThrow
import com.petqua.common.exception.ExceptionResponse
import com.petqua.domain.delivery.DeliveryMethod
import com.petqua.domain.member.MemberRepository
import com.petqua.domain.order.OrderNumber
import com.petqua.domain.order.OrderPayment
import com.petqua.domain.order.OrderPaymentRepository
import com.petqua.domain.order.OrderRepository
import com.petqua.domain.order.OrderStatus
import com.petqua.domain.product.ProductRepository
import com.petqua.domain.product.option.Sex
import com.petqua.domain.product.review.ProductReviewImageRepository
import com.petqua.domain.product.review.ProductReviewRepository
import com.petqua.domain.store.StoreRepository
Expand All @@ -18,6 +26,7 @@ import com.petqua.presentation.product.dto.CreateReviewRequest
import com.petqua.presentation.product.dto.UpdateReviewRecommendationRequest
import com.petqua.test.ApiTestConfig
import com.petqua.test.fixture.member
import com.petqua.test.fixture.order
import com.petqua.test.fixture.product
import com.petqua.test.fixture.productReview
import com.petqua.test.fixture.productReviewImage
Expand All @@ -32,6 +41,7 @@ import io.mockk.verify
import org.springframework.http.HttpStatus.BAD_REQUEST
import org.springframework.http.HttpStatus.CREATED
import org.springframework.http.HttpStatus.NO_CONTENT
import org.springframework.http.HttpStatus.OK
import org.springframework.http.MediaType.IMAGE_JPEG_VALUE
import org.springframework.mock.web.MockMultipartFile
import java.math.BigDecimal
Expand All @@ -42,6 +52,8 @@ class ProductReviewControllerTest(
private val storeRepository: StoreRepository,
private val productReviewRepository: ProductReviewRepository,
private val productReviewImageRepository: ProductReviewImageRepository,
private val orderRepository: OrderRepository,
private val orderPaymentRepository: OrderPaymentRepository,

@SpykBean
private val amazonS3: AmazonS3,
Expand Down Expand Up @@ -472,5 +484,153 @@ class ProductReviewControllerTest(
}
}
}

Given("사용자가 리뷰를 작성한 후 자신의 리뷰 내역을 조회할 때") {
val accessToken = signInAsMember().accessToken
val memberId = getMemberIdByAccessToken(accessToken)
val store = storeRepository.save(store(name = "펫쿠아"))
val productA = productRepository.save(
product(
name = "상품A",
storeId = store.id,
discountPrice = BigDecimal.ZERO,
reviewCount = 0,
reviewTotalScore = 0
)
)
val productB = productRepository.save(
product(
name = "상품B",
storeId = store.id,
discountPrice = BigDecimal.ZERO,
reviewCount = 0,
reviewTotalScore = 0
)
)
val orderA = orderRepository.save(
order(
orderNumber = OrderNumber.from("202402211607020ORDERNUMBER"),
memberId = memberId,
storeId = store.id,
storeName = store.name,
quantity = 1,
totalAmount = BigDecimal.ONE,
productId = productA.id,
productName = productA.name,
thumbnailUrl = productA.thumbnailUrl,
deliveryMethod = DeliveryMethod.SAFETY,
sex = Sex.FEMALE,
)
)
val orderB = orderRepository.save(
order(
orderNumber = OrderNumber.from("202402211607021ORDERNUMBER"),
memberId = memberId,
storeId = store.id,
storeName = store.name,
quantity = 1,
totalAmount = BigDecimal.ONE,
productId = productB.id,
productName = productB.name,
thumbnailUrl = productB.thumbnailUrl,
deliveryMethod = DeliveryMethod.SAFETY,
sex = Sex.FEMALE,
)
)
orderPaymentRepository.saveAll(
listOf(
OrderPayment(
orderId = orderA.id,
status = OrderStatus.PURCHASE_CONFIRMED
),
OrderPayment(
orderId = orderB.id,
status = OrderStatus.PURCHASE_CONFIRMED
)
)
)

val productReviewA = productReviewRepository.save(
productReview(
productId = productA.id,
reviewerId = memberId,
score = 5,
recommendCount = 1,
hasPhotos = true,
content = "상품A 정말 좋아요!"
)
)
val productReviewB = productReviewRepository.save(
productReview(
productId = productB.id,
reviewerId = memberId,
score = 5,
recommendCount = 1,
hasPhotos = false,
content = "상품B 정말 좋아요!"
)
)

productReviewImageRepository.saveAll(
listOf(
productReviewImage(imageUrl = "imageA1", productReviewId = productReviewA.id),
productReviewImage(imageUrl = "imageA2", productReviewId = productReviewA.id)
)
)

When("회원이 자신의 리뷰 내역을 조회하면") {
val response = requestReadMemberProductReviews(accessToken = accessToken)

Then("200 OK 로 응답한다") {
response.statusCode shouldBe OK.value()
}

Then("리뷰 내역을 응답한다") {
val memberProductReviewsResponse = response.`as`(MemberProductReviewsResponse::class.java)

val memberProductReviews = memberProductReviewsResponse.memberProductReviews

memberProductReviews.size shouldBe 2

val memberProductReviewB = memberProductReviews[0]
memberProductReviewB.reviewId shouldBe productReviewB.id
memberProductReviewB.memberId shouldBe productReviewB.memberId
memberProductReviewB.createdAt shouldBe orderB.createdAt
memberProductReviewB.orderStatus shouldBe OrderStatus.PURCHASE_CONFIRMED.name
memberProductReviewB.storeId shouldBe orderB.orderProduct.storeId
memberProductReviewB.storeId shouldBe orderB.orderProduct.storeId
memberProductReviewB.storeName shouldBe orderB.orderProduct.storeName
memberProductReviewB.productId shouldBe orderB.orderProduct.productId
memberProductReviewB.productName shouldBe orderB.orderProduct.productName
memberProductReviewB.productThumbnailUrl shouldBe orderB.orderProduct.thumbnailUrl
memberProductReviewB.quantity shouldBe orderB.orderProduct.quantity
memberProductReviewB.sex shouldBe orderB.orderProduct.sex.name
memberProductReviewB.deliveryMethod shouldBe orderB.orderProduct.deliveryMethod.name
memberProductReviewB.score shouldBe productReviewB.score.value
memberProductReviewB.content shouldBe productReviewB.content.value
memberProductReviewB.recommendCount shouldBe productReviewB.recommendCount
memberProductReviewB.reviewImages.size shouldBe 0

val memberProductReviewA = memberProductReviews[1]
memberProductReviewA.reviewId shouldBe productReviewA.id
memberProductReviewA.memberId shouldBe productReviewA.memberId
memberProductReviewA.createdAt shouldBe orderA.createdAt
memberProductReviewA.orderStatus shouldBe OrderStatus.PURCHASE_CONFIRMED.name
memberProductReviewA.storeId shouldBe orderA.orderProduct.storeId
memberProductReviewA.storeId shouldBe orderA.orderProduct.storeId
memberProductReviewA.storeName shouldBe orderA.orderProduct.storeName
memberProductReviewA.productId shouldBe orderA.orderProduct.productId
memberProductReviewA.productName shouldBe orderA.orderProduct.productName
memberProductReviewA.productThumbnailUrl shouldBe orderA.orderProduct.thumbnailUrl
memberProductReviewA.quantity shouldBe orderA.orderProduct.quantity
memberProductReviewA.sex shouldBe orderA.orderProduct.sex.name
memberProductReviewA.deliveryMethod shouldBe orderA.orderProduct.deliveryMethod.name
memberProductReviewA.score shouldBe productReviewA.score.value
memberProductReviewA.content shouldBe productReviewA.content.value
memberProductReviewA.recommendCount shouldBe productReviewA.recommendCount
memberProductReviewA.reviewImages shouldBe listOf("imageA1", "imageA2")
}
}
}
}
}

0 comments on commit 129d715

Please sign in to comment.