Skip to content

Commit

Permalink
Merge pull request #13 from ClothingStoreService/feature/changeDispla…
Browse files Browse the repository at this point in the history
…yStatus

Feature/change display status
  • Loading branch information
Ogu1208 authored Aug 14, 2024
2 parents 72f72cb + c73dae7 commit af54ef6
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 7 deletions.
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)
}
}

0 comments on commit af54ef6

Please sign in to comment.