Skip to content
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/change display status #13

Merged
merged 3 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ dependencies {
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
testImplementation("io.mockk:mockk:1.13.5")
testImplementation ("org.mockito.kotlin:mockito-kotlin:4.0.0")

//etc
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0") //swagger
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import org.springframework.web.bind.annotation.*
import org.springframework.web.multipart.MultipartFile
import org.store.clothstar.common.dto.MessageDTO
import org.store.clothstar.product.dto.request.ProductCreateRequest
import org.store.clothstar.product.dto.response.ProductResponse
import org.store.clothstar.product.dto.request.UpdateDisplayStatusRequest
import org.store.clothstar.product.service.ProductApplicationService
import org.store.clothstar.product.service.ProductService

@Tag(name = "Products", description = "Products(상품 옵션) 관련 API 입니다.")
@RequestMapping("/v3/products")
Expand Down Expand Up @@ -40,10 +41,34 @@ private class ProductController(

}

@GetMapping("/{productId}")
@Operation(summary = "상품 상세 조회", description = "상품 ID를 사용하여 특정 상품의 상세 정보를 조회한다.")
fun getProductDetails(@PathVariable productId: Long): ResponseEntity<ProductResponse> {
val productResponse = productApplicationService.getProductDetails(productId)
return ResponseEntity(productResponse, HttpStatus.OK)
@PatchMapping("/{productId}/displayStatus")
@Operation(summary = "상품 진열 상태 변경", description = "상품 ID를 사용하여 해당 상품의 진열 상태를 변경합니다.")
fun updateProductDisplayStatus(
@PathVariable productId: Long,
@RequestBody request: UpdateDisplayStatusRequest
): ResponseEntity<MessageDTO> {
productApplicationService.updateProductDisplayStatus(productId, request.displayStatus)

val messageDTO = MessageDTO(
HttpStatus.OK.value(),
"상품 진열 상태가 성공적으로 변경되었습니다."
)
return ResponseEntity(messageDTO, HttpStatus.OK)
}

@PatchMapping("/{productId}/items/{itemId}/displayStatus")
@Operation(summary = "아이템 진열 상태 변경", description = "상품 ID와 아이템 ID를 사용하여 해당 아이템의 진열 상태를 변경합니다.")
fun updateItemDisplayStatus(
@PathVariable productId: Long,
@PathVariable itemId: Long,
@RequestBody request: UpdateDisplayStatusRequest
): ResponseEntity<MessageDTO> {
productApplicationService.updateItemDisplayStatus(productId, itemId, request.displayStatus)

val messageDTO = MessageDTO(
HttpStatus.OK.value(),
"아이템 진열 상태가 성공적으로 변경되었습니다."
)
return ResponseEntity(messageDTO, HttpStatus.OK)
}
}
6 changes: 5 additions & 1 deletion src/main/kotlin/org/store/clothstar/product/domain/Item.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class Item(
var finalPrice: Int,
var stock: Int,
val saleStatus: SaleStatus,
val displayStatus: DisplayStatus,
var displayStatus: DisplayStatus,

@ElementCollection
@CollectionTable(
Expand All @@ -52,4 +52,8 @@ class Item(
fun updateStock(stock: Int) {
this.stock = stock
}

fun updateDisplayStatus(displayStatus: DisplayStatus) {
this.displayStatus = displayStatus
}
}
4 changes: 4 additions & 0 deletions src/main/kotlin/org/store/clothstar/product/domain/Product.kt
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,8 @@ class Product(
fun updateSaleStatus(saleStatus: SaleStatus) {
this.saleStatus = saleStatus
}

fun updateDisplayStatus(displayStatus: DisplayStatus) {
this.displayStatus = displayStatus
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.store.clothstar.product.dto.request


import jakarta.validation.constraints.NotNull
import org.store.clothstar.product.domain.type.DisplayStatus

class UpdateDisplayStatusRequest (

@NotNull(message = "진열 상태를 설정해주세요.")
val displayStatus: DisplayStatus,
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import org.springframework.data.jpa.repository.JpaRepository
import org.store.clothstar.product.domain.Item

interface ItemRepository : JpaRepository<Item, Long> {

fun findAllByItemId(productId: Long?): List<Item?>

fun findByItemIdIn(productIds: List<Long>): List<Item>

fun findByItemIdAndProduct_ProductId(itemId: Long, productId: Long): Item?
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.store.clothstar.product.service

import jakarta.persistence.EntityNotFoundException
import org.springframework.data.repository.findByIdOrNull
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Service
Expand Down Expand Up @@ -92,4 +93,9 @@ class ItemService(
val updatedStock: Int = item.stock - quantity
item.updateStock(updatedStock)
}

fun getItemByIdAndProductId(itemId: Long, productId: Long): Item {
return itemRepository.findByItemIdAndProduct_ProductId(itemId, productId)
?: throw EntityNotFoundException("Item not found with id: $itemId and productId: $productId")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import org.store.clothstar.member.domain.vo.MemberShoppingActivity
import org.store.clothstar.member.service.MemberService
import org.store.clothstar.product.domain.Product
import org.store.clothstar.product.domain.ProductImage
import org.store.clothstar.product.domain.type.DisplayStatus
import org.store.clothstar.product.domain.type.ImageType
import org.store.clothstar.product.dto.request.ProductCreateRequest
import org.store.clothstar.product.dto.response.ProductResponse
Expand Down Expand Up @@ -71,4 +72,16 @@ class ProductApplicationService(

return productService.getProductDetails(productId)
}

@Transactional
fun updateProductDisplayStatus(productId: Long, displayStatus: DisplayStatus) {
val product = productService.getProductById(productId)
product.updateDisplayStatus(displayStatus)
}

@Transactional
fun updateItemDisplayStatus(productId: Long, itemId: Long, displayStatus: DisplayStatus) {
val item = itemService.getItemByIdAndProductId(itemId, productId)
item.updateDisplayStatus(displayStatus)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package org.store.clothstar.product.service

import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.BDDMockito.given
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Mockito.*
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.whenever
import org.springframework.web.multipart.MultipartFile
import org.store.clothstar.member.service.MemberService
import org.store.clothstar.product.domain.Item
import org.store.clothstar.product.domain.Product
import org.store.clothstar.product.domain.type.DisplayStatus
import org.store.clothstar.product.domain.type.SaleStatus
import org.store.clothstar.product.dto.request.ProductCreateRequest

@ExtendWith(MockitoExtension::class)
class ProductApplicationServiceTest {

@Mock
private lateinit var productService: ProductService

@Mock
private lateinit var productOptionService: ProductOptionService

@Mock
private lateinit var itemService: ItemService

@Mock
private lateinit var memberService: MemberService

@InjectMocks
private lateinit var productApplicationService: ProductApplicationService

@Disabled
@DisplayName("유효한 ProductCreateRequest가 들어오면 상품 생성에 성공한다.")
@Test
fun givenValidProductCreateRequest_whenCreateProduct_ThenProductCreated() {
// given
val productCreateRequest = ProductCreateRequest(
memberId = 1L,
categoryId = 2L,
name = "오구 슬리퍼",
content = "푹신 푹신한 귀여운 오구 슬리퍼",
price = 19900,
displayStatus = DisplayStatus.VISIBLE,
saleStatus = SaleStatus.ON_SALE,
productOptions = listOf(),
items = listOf()
)

val product = productCreateRequest.toProductEntity()

whenever(productService.createProduct(any())).thenReturn(product)

// when
productApplicationService.createProduct(mock(MultipartFile::class.java), null, productCreateRequest)

// then
verify(productService, times(1)).createProduct(any(Product::class.java))
verify(product, times(1)).updateDisplayStatus(eq(DisplayStatus.VISIBLE))
}

@DisplayName("유효한 productId와 displayStatus가 주어지면 상품의 진열 상태 변경에 성공한다.")
@Test
fun givenValidProductIdAndDsplayStatus_whenUpdateProductDisplayStatusThenProductDisplayStatusUpdated() {
// given
val productId: Long = 1
val displayStatus = DisplayStatus.HIDDEN
val mockProduct = mock(Product::class.java)

given(productService.getProductById(productId)).willReturn(mockProduct)

// when
productApplicationService.updateProductDisplayStatus(productId, displayStatus)

// then
verify(productService).getProductById(productId)
verify(mockProduct).updateDisplayStatus(displayStatus)
}

@DisplayName("유효한 productId, itemId, displayStatus가 주어지면 아이템의 진열 상태 변경에 성공한다.")
@Test
fun givenValidProductIdItemIdAndDisplayStatus_whenUpdateItemDisplayStatus_thenItemDisplayStatusUpdated() {
// given
val productId: Long = 1
val itemId: Long = 2
val displayStatus = DisplayStatus.VISIBLE
val mockItem = mock(Item::class.java)

given(itemService.getItemByIdAndProductId(itemId, productId)).willReturn(mockItem)

// when
productApplicationService.updateItemDisplayStatus(productId, itemId, displayStatus)

// then
verify(itemService).getItemByIdAndProductId(itemId, productId)
verify(mockItem).updateDisplayStatus(displayStatus)
}
}