-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feature: 봉달 목록 조회 api #38
The head ref may contain hidden characters: "28-feature-\uBD09\uB2EC-\uBAA9\uB85D-\uC870\uD68C-api"
Changes from 7 commits
0b08c0e
8262f3a
19e6e83
d381460
69cac7d
4843bd4
cae10d1
713425c
9093334
a66f802
f7a2d80
5a50036
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,17 @@ | ||
package com.petqua.application.cart | ||
|
||
import com.petqua.application.cart.dto.CartProductResponse | ||
import com.petqua.application.cart.dto.DeleteCartProductCommand | ||
import com.petqua.application.cart.dto.SaveCartProductCommand | ||
import com.petqua.application.cart.dto.UpdateCartProductOptionCommand | ||
import com.petqua.common.domain.existByIdOrThrow | ||
import com.petqua.common.domain.findByIdOrThrow | ||
import com.petqua.domain.cart.CartProduct | ||
import com.petqua.domain.cart.CartProductRepository | ||
import com.petqua.domain.cart.DeliveryMethod | ||
import com.petqua.domain.member.MemberRepository | ||
import com.petqua.domain.product.ProductRepository | ||
import com.petqua.domain.product.dto.ProductResponse | ||
import com.petqua.exception.cart.CartProductException | ||
import com.petqua.exception.cart.CartProductExceptionType.DUPLICATED_PRODUCT | ||
import com.petqua.exception.cart.CartProductExceptionType.NOT_FOUND_CART_PRODUCT | ||
|
@@ -76,4 +79,21 @@ class CartProductService( | |
cartProduct.validateOwner(command.memberId) | ||
cartProductRepository.delete(cartProduct) | ||
} | ||
|
||
@Transactional(readOnly = true) | ||
fun readAll(memberId: Long): List<CartProductResponse> { | ||
memberRepository.existByIdOrThrow(memberId, MemberException(NOT_FOUND_MEMBER)) | ||
val cartProducts = cartProductRepository.findAllByMemberId(memberId) | ||
val products = productByIds(cartProducts) | ||
val findAll = productRepository.findAll() | ||
TaeyeonRoyce marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return cartProducts.map { | ||
products[it.id]?.let { product -> CartProductResponse.of(it, product) } | ||
?: CartProductResponse.fromDeletedProduct(it) | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 장바구니 전체 가격은 클라이언트에서 계산하는 걸까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 부분도 협의해보고 적용하겠습니다! |
||
|
||
private fun productByIds(cartProducts: List<CartProduct>): Map<Long, ProductResponse> { | ||
val productIdsFromCart = cartProducts.map { it.productId } | ||
return productRepository.findAllProductResponseByIdIn(productIdsFromCart).associateBy { it.id } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ package com.petqua.application.cart.dto | |
import com.petqua.domain.cart.CartProduct | ||
import com.petqua.domain.cart.CartProductQuantity | ||
import com.petqua.domain.cart.DeliveryMethod | ||
import com.petqua.domain.product.dto.ProductResponse | ||
|
||
data class SaveCartProductCommand( | ||
val memberId: Long, | ||
|
@@ -35,3 +36,56 @@ data class DeleteCartProductCommand( | |
val memberId: Long, | ||
val cartProductId: Long, | ||
) | ||
|
||
data class CartProductResponse( | ||
val id: Long, | ||
val storeName: String, | ||
val productId: Long, | ||
val productName: String, | ||
val productThumbnailUrl: String, | ||
val productPrice: Int, | ||
val productDiscountRate: Int, | ||
val productDiscountPrice: Int, | ||
val quantity: Int, | ||
val isMale: Boolean, | ||
val deliveryMethod: String, | ||
val isOnSale: Boolean, | ||
) { | ||
|
||
companion object { | ||
fun of(cartProduct: CartProduct, productResponse: ProductResponse): CartProductResponse { | ||
return CartProductResponse( | ||
id = cartProduct.id, | ||
storeName = productResponse.storeName, | ||
productId = productResponse.id, | ||
productName = productResponse.name, | ||
productThumbnailUrl = productResponse.thumbnailUrl, | ||
productPrice = productResponse.price, | ||
productDiscountRate = productResponse.discountRate, | ||
productDiscountPrice = productResponse.discountPrice, | ||
quantity = cartProduct.quantity.value, | ||
isMale = cartProduct.isMale, | ||
deliveryMethod = cartProduct.deliveryMethod.name, | ||
isOnSale = true, | ||
) | ||
} | ||
|
||
|
||
fun fromDeletedProduct(cartProduct: CartProduct): CartProductResponse { | ||
return CartProductResponse( | ||
id = cartProduct.id, | ||
storeName = "", | ||
productId = cartProduct.productId, | ||
productName = "", | ||
productThumbnailUrl = "", | ||
productPrice = 0, | ||
productDiscountRate = 0, | ||
productDiscountPrice = 0, | ||
quantity = cartProduct.quantity.value, | ||
isMale = cartProduct.isMale, | ||
deliveryMethod = cartProduct.deliveryMethod.name, | ||
isOnSale = false, | ||
) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이거 디자인이 아직 안정해졌다고 해서 임의로 보내는 값인가요?.? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 경우에는 [품절]로 표기 하신다고 했어요! |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -96,4 +96,26 @@ class ProductCustomRepositoryImpl( | |
jpqlRenderer | ||
) | ||
} | ||
|
||
override fun findAllProductResponseByIdIn(ids: List<Long>): List<ProductResponse> { | ||
val query = jpql { | ||
selectNew<ProductResponse>( | ||
entity(Product::class), | ||
path(Store::name) | ||
).from( | ||
entity(Product::class), | ||
join(Store::class).on(path(Product::storeId).eq(path(Store::id))), | ||
).where( | ||
predicateByIds(ids) | ||
) | ||
} | ||
Comment on lines
+101
to
+111
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 멋지게 잘 쓰셨네요! |
||
|
||
return entityManager.createQuery( | ||
query, | ||
jpqlRenderContext, | ||
jpqlRenderer | ||
) | ||
} | ||
|
||
private fun Jpql.predicateByIds(ids: List<Long>) = if (ids.isEmpty()) null else path(Product::id).`in`(ids) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,7 @@ import com.petqua.domain.cart.DeliveryMethod.COMMON | |
import com.petqua.domain.cart.DeliveryMethod.SAFETY | ||
import com.petqua.domain.member.MemberRepository | ||
import com.petqua.domain.product.ProductRepository | ||
import com.petqua.domain.store.StoreRepository | ||
import com.petqua.exception.cart.CartProductException | ||
import com.petqua.exception.cart.CartProductExceptionType.DUPLICATED_PRODUCT | ||
import com.petqua.exception.cart.CartProductExceptionType.FORBIDDEN_CART_PRODUCT | ||
|
@@ -22,6 +23,7 @@ import com.petqua.test.DataCleaner | |
import com.petqua.test.fixture.cartProduct | ||
import com.petqua.test.fixture.member | ||
import com.petqua.test.fixture.product | ||
import com.petqua.test.fixture.store | ||
import io.kotest.assertions.assertSoftly | ||
import io.kotest.assertions.throwables.shouldThrow | ||
import io.kotest.core.spec.style.BehaviorSpec | ||
|
@@ -35,6 +37,7 @@ class CartProductServiceTest( | |
private val cartProductRepository: CartProductRepository, | ||
private val productRepository: ProductRepository, | ||
private val memberRepository: MemberRepository, | ||
private val storeRepository: StoreRepository, | ||
private val dataCleaner: DataCleaner, | ||
) : BehaviorSpec({ | ||
|
||
|
@@ -173,16 +176,16 @@ class CartProductServiceTest( | |
cartProduct( | ||
memberId = memberId, | ||
productId = productId, | ||
isMale = true, | ||
deliveryMethod = COMMON | ||
isMale = false, | ||
deliveryMethod = SAFETY | ||
) | ||
) | ||
val command = UpdateCartProductOptionCommand( | ||
cartProductId = cartProduct.id, | ||
memberId = memberId, | ||
quantity = CartProductQuantity(3), | ||
isMale = true, | ||
deliveryMethod = COMMON, | ||
isMale = false, | ||
deliveryMethod = SAFETY, | ||
) | ||
Then("예외가 발생 한다") { | ||
shouldThrow<CartProductException> { | ||
|
@@ -251,6 +254,58 @@ class CartProductServiceTest( | |
} | ||
} | ||
|
||
Given("봉달 상품 조회시") { | ||
val store = storeRepository.save(store(name = "store")) | ||
val productAId = productRepository.save(product(storeId = store.id)).id | ||
val productBId = productRepository.save(product(storeId = store.id)).id | ||
val productCId = productRepository.save(product(storeId = store.id)).id | ||
val memberId = memberRepository.save(member()).id | ||
cartProductRepository.saveAll( | ||
listOf( | ||
cartProduct(memberId = memberId, productId = productAId), | ||
cartProduct(memberId = memberId, productId = productBId), | ||
cartProduct(memberId = memberId, productId = productCId), | ||
) | ||
) | ||
|
||
When("봉달 상품이 있는 회원이 조회 하는 경우") { | ||
val result = cartProductService.readAll(memberId) | ||
|
||
Then("봉달 상품 리스트를 반환 한다") { | ||
result.size shouldBe 3 | ||
} | ||
} | ||
|
||
When("봉달 상품이 없는 회원이 조회 하는 경우") { | ||
val newMemberId = memberRepository.save(member()).id | ||
val results = cartProductService.readAll(newMemberId) | ||
|
||
Then("빈 리스트를 반환 한다") { | ||
results.size shouldBe 0 | ||
} | ||
} | ||
|
||
When("봉달에 담아둔 상품이 삭제된 경우") { | ||
productRepository.deleteById(productAId) | ||
val results = cartProductService.readAll(memberId) | ||
|
||
Then("빈 리스트를 반환 한다") { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기 Then 글 잘못된 것 같아요! |
||
assertSoftly(results) { | ||
size shouldBe 3 | ||
find { it.productId == productAId }!!.isOnSale shouldBe false | ||
} | ||
} | ||
} | ||
|
||
When("존재 하지 않는 회원이 조회 하는 경우") { | ||
Then("예외가 발생 한다") { | ||
shouldThrow<MemberException> { | ||
cartProductService.readAll(Long.MIN_VALUE) | ||
}.exceptionType() shouldBe NOT_FOUND_MEMBER | ||
} | ||
} | ||
} | ||
|
||
afterContainer { | ||
dataCleaner.clean() | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -177,6 +177,22 @@ class ProductCustomRepositoryImplTest( | |
} | ||
} | ||
|
||
Given("다중 id로 ProductResponse를 조회 할 때") { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
val product1 = productRepository.save(product(name = "상품1", storeId = store.id)) | ||
val product2 = productRepository.save(product(name = "상품2", storeId = store.id)) | ||
|
||
When("id 목록을 입력하면") { | ||
val products = productRepository.findAllProductResponseByIdIn(listOf(product1.id, product2.id)) | ||
|
||
Then("해당 id의 ProductResponse를 반환한다") { | ||
products shouldContainExactly listOf( | ||
ProductResponse(product1, store.name), | ||
ProductResponse(product2, store.name), | ||
) | ||
} | ||
} | ||
} | ||
|
||
afterContainer { | ||
dataCleaner.clean() | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
헛 저 찜 목록 조회할 때 이거까지 한 번에 조인해서 가져오게했는데, 혹시 cartProducts를 미리 조회해오신 이유가 있으신가요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Product가 제거된 cartProduct가 조회 되지 않을 것 같아서 분리했었습니다.
근데 outer join 하고 product가 null인 걸로 삭제 판단해도 될 것 같네요!
커넥션을 줄이는 방향으로 수정해보겠습니다!