From d7aaabeabb49ab8320fde563b831f465316a1ad0 Mon Sep 17 00:00:00 2001 From: Ogu1208 Date: Thu, 15 Aug 2024 22:35:43 +0900 Subject: [PATCH 01/13] =?UTF-8?q?refactor:=20Product=20-=20Seller,=20User?= =?UTF-8?q?=20Controller=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../product/controller/ProductController.kt | 43 +++---------------- 1 file changed, 6 insertions(+), 37 deletions(-) diff --git a/src/main/kotlin/org/store/clothstar/product/controller/ProductController.kt b/src/main/kotlin/org/store/clothstar/product/controller/ProductController.kt index 19e0d4c..0e171cc 100644 --- a/src/main/kotlin/org/store/clothstar/product/controller/ProductController.kt +++ b/src/main/kotlin/org/store/clothstar/product/controller/ProductController.kt @@ -4,12 +4,10 @@ import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.tags.Tag import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity -import org.springframework.validation.annotation.Validated -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.request.UpdateDisplayStatusRequest +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController import org.store.clothstar.product.dto.response.ProductResponse import org.store.clothstar.product.service.ProductApplicationService @@ -39,6 +37,8 @@ private class ProductController( return ResponseEntity(messageDTO, HttpStatus.CREATED) } + + */ @GetMapping("/{productId}") @Operation(summary = "상품 상세 조회", description = "상품 ID를 사용하여 특정 상품의 상세 정보를 조회한다.") @@ -46,35 +46,4 @@ private class ProductController( 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 { - 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 { - productApplicationService.updateItemDisplayStatus(productId, itemId, request.displayStatus) - - val messageDTO = MessageDTO( - HttpStatus.OK.value(), - "아이템 진열 상태가 성공적으로 변경되었습니다." - ) - return ResponseEntity(messageDTO, HttpStatus.OK) - } } \ No newline at end of file From 3c70ff87c43df8254ea17f234a3bd2a3c9f267e5 Mon Sep 17 00:00:00 2001 From: Ogu1208 Date: Thu, 15 Aug 2024 23:46:44 +0900 Subject: [PATCH 02/13] =?UTF-8?q?refactor:=20Product=20Seller=20Controllr?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC,=20Member=20Role=20=EA=B2=80=EC=82=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../store/clothstar/common/error/ErrorCode.kt | 7 ++ .../exception/order/BadRequestException.kt | 4 +- .../clothstar/member/AccountValidateUtil.kt | 19 ++++ .../product/controller/ProductController.kt | 4 + .../controller/ProductSellerController.kt | 92 +++++++++++++++++++ .../product/dto/request/UpdateStockRequest.kt | 11 +++ .../repository/ProductRepositoryCustom.kt | 22 +++++ .../repository/ProductRepositoryImpl.kt | 4 + .../service/ProductApplicationService.kt | 22 +++-- 9 files changed, 178 insertions(+), 7 deletions(-) create mode 100644 src/main/kotlin/org/store/clothstar/member/AccountValidateUtil.kt create mode 100644 src/main/kotlin/org/store/clothstar/product/controller/ProductSellerController.kt create mode 100644 src/main/kotlin/org/store/clothstar/product/dto/request/UpdateStockRequest.kt create mode 100644 src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustom.kt create mode 100644 src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryImpl.kt diff --git a/src/main/kotlin/org/store/clothstar/common/error/ErrorCode.kt b/src/main/kotlin/org/store/clothstar/common/error/ErrorCode.kt index 7998a21..94475c9 100644 --- a/src/main/kotlin/org/store/clothstar/common/error/ErrorCode.kt +++ b/src/main/kotlin/org/store/clothstar/common/error/ErrorCode.kt @@ -20,6 +20,8 @@ enum class ErrorCode( INVALID_REFRESH_TOKEN(HttpStatus.BAD_REQUEST, "refresh 토큰이 만료되었거나 유효하지 않습니다."), INVALID_AUTH_CERTIFY_NUM(HttpStatus.BAD_REQUEST, "인증번호가 잘못 되었습니다."), + INVALID_ROLE(HttpStatus.BAD_REQUEST, "계정 ID: %d - 이 계정은 요청된 권한(%s)을 가지고 있지 않습니다."), + // Order 관련 에러코드 NOT_FOUND_ORDER(HttpStatus.NOT_FOUND, "존재하지 않는 주문번호입니다."), INVALID_ORDER_STATUS_CONFIRMED(HttpStatus.BAD_REQUEST, "주문이 '입금확인' 상태가 아니므로 요청을 처리할 수 없습니다."), @@ -35,4 +37,9 @@ enum class ErrorCode( this.status = status this.message = message } + + fun withDynamicMessage(vararg args: Any): ErrorCode { + this.message = String.format(this.message, *args) + return this + } } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/common/error/exception/order/BadRequestException.kt b/src/main/kotlin/org/store/clothstar/common/error/exception/order/BadRequestException.kt index 4d7aebe..56b18d2 100644 --- a/src/main/kotlin/org/store/clothstar/common/error/exception/order/BadRequestException.kt +++ b/src/main/kotlin/org/store/clothstar/common/error/exception/order/BadRequestException.kt @@ -4,8 +4,10 @@ import org.springframework.http.HttpStatus import org.store.clothstar.common.error.ErrorCode class BadRequestException( - val code: ErrorCode + val code: ErrorCode, + vararg args: Any ) : RuntimeException(code.message) { + val httpStatus: HttpStatus get() = code.status diff --git a/src/main/kotlin/org/store/clothstar/member/AccountValidateUtil.kt b/src/main/kotlin/org/store/clothstar/member/AccountValidateUtil.kt new file mode 100644 index 0000000..37a0ae8 --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/member/AccountValidateUtil.kt @@ -0,0 +1,19 @@ +package org.store.clothstar.member + +import org.springframework.stereotype.Component +import org.store.clothstar.common.error.ErrorCode +import org.store.clothstar.common.error.exception.order.BadRequestException +import org.store.clothstar.member.domain.Account +import org.store.clothstar.member.domain.MemberRole + +@Component +class AccountValidateUtil ( + +) { + +// fun validateRole(accout: Account, memberRole: MemberRole) { +// if (!accout.role.equals(memberRole)) { +//// throw BadRequestException(ErrorCode.INVALID_ROLE, accout.accountId, memberRole) +// } +// } +} \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/product/controller/ProductController.kt b/src/main/kotlin/org/store/clothstar/product/controller/ProductController.kt index 0e171cc..6f4c847 100644 --- a/src/main/kotlin/org/store/clothstar/product/controller/ProductController.kt +++ b/src/main/kotlin/org/store/clothstar/product/controller/ProductController.kt @@ -46,4 +46,8 @@ private class ProductController( val productResponse = productApplicationService.getProductDetails(productId) return ResponseEntity(productResponse, HttpStatus.OK) } + + // 카테고리별 Offset 상품 조회 + + // 카테고리별 Pagination 상품 조회 } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/product/controller/ProductSellerController.kt b/src/main/kotlin/org/store/clothstar/product/controller/ProductSellerController.kt new file mode 100644 index 0000000..271f2f0 --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/product/controller/ProductSellerController.kt @@ -0,0 +1,92 @@ +package org.store.clothstar.product.controller + +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.tags.Tag +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity +import org.springframework.validation.annotation.Validated +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.request.UpdateDisplayStatusRequest +import org.store.clothstar.product.dto.request.UpdateStockRequest +import org.store.clothstar.product.dto.response.ProductResponse +import org.store.clothstar.product.service.ProductApplicationService + +@Tag(name = "ProductSellers", description = "ProductSellers(판매자) 관련 API 입니다.") +@RestController +@RequestMapping("/v3/products/sellers") +class ProductSellerController( + private val productApplicationService: ProductApplicationService +) { + // 판매자 상품 등록 + @PostMapping + @Operation( + summary = "상품 등록", + description = "카테고리 아이디, 상품 이름, 내용, 가격, 상태를 입력하여 상품을 신규 등록한다." + ) + fun createProduct( + @RequestPart(value = "mainImage", required = false) mainImage: MultipartFile, + @RequestPart(value = "subImages", required = false) subImages: List?, + @RequestPart(value = "dto") @Validated productCreateRequest: ProductCreateRequest + ): ResponseEntity { + // 상품 등록 + productApplicationService.createProduct(mainImage, subImages, productCreateRequest); + + val messageDTO = MessageDTO( + HttpStatus.CREATED.value(), + "상품 생성이 정상적으로 처리됐습니다." + ) + + return ResponseEntity(messageDTO, HttpStatus.CREATED) + } + + @PatchMapping("/{productId}/displayStatus") + @Operation(summary = "상품 진열 상태 변경", description = "상품 ID를 사용하여 해당 상품의 진열 상태를 변경합니다.") + fun updateProductDisplayStatus( + @PathVariable productId: Long, + @RequestBody request: UpdateDisplayStatusRequest + ): ResponseEntity { + 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 { + productApplicationService.updateItemDisplayStatus(productId, itemId, request.displayStatus) + + val messageDTO = MessageDTO( + HttpStatus.OK.value(), + "아이템 진열 상태가 성공적으로 변경되었습니다." + ) + return ResponseEntity(messageDTO, HttpStatus.OK) + } + + // 상품 재고 수량 변경 + @PatchMapping("/{productId}/items/{itemId}/stock") + @Operation(summary = "아이템 재고 수량 변경", description = "상품 ID와 아이템 ID를 사용하여 해당 아이템의 재고 수량을 변경합니다.") + fun updateItemStock( + @PathVariable productId: Long, + @PathVariable itemId: Long, + @RequestBody request: UpdateStockRequest + ): ResponseEntity { + productApplicationService.updateItemStock(productId, itemId, request.stock) + + val messageDTO = MessageDTO( + HttpStatus.OK.value(), + "아이템 재고 수량이 성공적으로 변경되었습니다." + ) + return ResponseEntity(messageDTO, HttpStatus.OK) + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/product/dto/request/UpdateStockRequest.kt b/src/main/kotlin/org/store/clothstar/product/dto/request/UpdateStockRequest.kt new file mode 100644 index 0000000..2375090 --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/product/dto/request/UpdateStockRequest.kt @@ -0,0 +1,11 @@ +package org.store.clothstar.product.dto.request + +import jakarta.validation.constraints.NotNull +import jakarta.validation.constraints.PositiveOrZero + +class UpdateStockRequest ( + + @field:NotNull(message = "재고 수량은 필수 입력 사항입니다.") + @field:PositiveOrZero(message = "재고 수량은 0 이상이어야 합니다.") + val stock: Int +) \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustom.kt b/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustom.kt new file mode 100644 index 0000000..2fe9214 --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustom.kt @@ -0,0 +1,22 @@ +package org.store.clothstar.product.repository + +import org.springframework.data.domain.Page +import org.springframework.data.domain.Pageable +import org.springframework.stereotype.Repository +import org.store.clothstar.product.domain.Product + +@Repository +interface ProductRepositoryCustom { + + fun getProductLists(pageable: Pageable?): Page? + + fun findWithDetailsByProductId(productId: Long): Product? + + fun findAllOffsetPaging(pageable: Pageable): Page + + fun findAllSlicePaging(pageable: Pageable): List + + + + +} \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryImpl.kt b/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryImpl.kt new file mode 100644 index 0000000..a25605a --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryImpl.kt @@ -0,0 +1,4 @@ +package org.store.clothstar.product.repository + +interface ProductRepositoryImpl { +} \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/product/service/ProductApplicationService.kt b/src/main/kotlin/org/store/clothstar/product/service/ProductApplicationService.kt index d670f94..4c78baa 100644 --- a/src/main/kotlin/org/store/clothstar/product/service/ProductApplicationService.kt +++ b/src/main/kotlin/org/store/clothstar/product/service/ProductApplicationService.kt @@ -1,10 +1,10 @@ package org.store.clothstar.product.service +import org.springframework.security.core.context.SecurityContextHolder import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import org.springframework.web.multipart.MultipartFile -import org.store.clothstar.member.domain.Member -import org.store.clothstar.member.domain.vo.MemberShoppingActivity +import org.store.clothstar.member.authentication.domain.CustomUserDetails import org.store.clothstar.member.service.MemberService import org.store.clothstar.product.domain.ProductImage import org.store.clothstar.product.domain.type.DisplayStatus @@ -12,6 +12,7 @@ import org.store.clothstar.product.domain.type.ImageType import org.store.clothstar.product.dto.request.ProductCreateRequest import org.store.clothstar.product.dto.response.ProductResponse + @Service class ProductApplicationService( private val productService: ProductService, @@ -25,10 +26,11 @@ class ProductApplicationService( subImages: List?, productCreateRequest: ProductCreateRequest ) { - // get current member (principal) - val memberId: Long = 1 // currentMember.getId() -// val member: Member = memberService.getMemberByMemberId(memberId) - val member = Member(memberId, "010-1234-5678", "Ogu", MemberShoppingActivity.init()) + val member = SecurityContextHolder.getContext().authentication.principal as CustomUserDetails + +// if(memberService.isSeller(member.id)) { +// throw IllegalArgumentException("판매자만 상품을 등록할 수 있습니다.") +// } // 1. 상품 생성 val product = productCreateRequest.toProductEntity() @@ -83,4 +85,12 @@ class ProductApplicationService( val item = itemService.getItemByIdAndProductId(itemId, productId) item.updateDisplayStatus(displayStatus) } + + @Transactional + fun updateItemStock(productId: Long, itemId: Long, stock: Int) { +// val Member = getPrin + + val item = itemService.getItemByIdAndProductId(itemId, productId) + item.updateStock(stock) + } } \ No newline at end of file From c0915ee17ec383952055692b685dac45871e58e5 Mon Sep 17 00:00:00 2001 From: hyunsu Date: Sat, 17 Aug 2024 01:04:35 +0900 Subject: [PATCH 03/13] =?UTF-8?q?feat:=20category=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=95=20=EC=A1=B0=ED=9A=8C=20WIP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../category/controller/CategoryController.kt | 39 ++++++++- .../common/config/JpaQueryFactoryConfig.kt | 13 ++- .../dto/response/SellerSimpleResponse.kt | 14 +++- .../dto/response/ProductDetailResponse.kt | 48 +++++++++++ .../product/repository/ProductRepository.kt | 5 +- .../repository/ProductRepositoryCustom.kt | 17 ++-- .../repository/ProductRepositoryCustomImpl.kt | 84 +++++++++++++++++++ .../product/service/ProductService.kt | 43 +++++++++- 8 files changed, 242 insertions(+), 21 deletions(-) rename src/{test => main}/kotlin/org/store/clothstar/common/config/JpaQueryFactoryConfig.kt (55%) create mode 100644 src/main/kotlin/org/store/clothstar/product/dto/response/ProductDetailResponse.kt create mode 100644 src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustomImpl.kt diff --git a/src/main/kotlin/org/store/clothstar/category/controller/CategoryController.kt b/src/main/kotlin/org/store/clothstar/category/controller/CategoryController.kt index 9a4d2f5..60e492e 100644 --- a/src/main/kotlin/org/store/clothstar/category/controller/CategoryController.kt +++ b/src/main/kotlin/org/store/clothstar/category/controller/CategoryController.kt @@ -1,6 +1,10 @@ package org.store.clothstar.category.controller import io.swagger.v3.oas.annotations.Operation +import org.springframework.data.domain.Page +import org.springframework.data.domain.Pageable +import org.springframework.data.domain.Slice +import org.springframework.data.web.PageableDefault import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity import org.springframework.validation.annotation.Validated @@ -11,14 +15,15 @@ import org.store.clothstar.category.dto.response.CategoryResponse import org.store.clothstar.category.service.CategoryService import org.store.clothstar.common.dto.MessageDTO import org.store.clothstar.common.util.URIBuilder +import org.store.clothstar.product.dto.response.ProductDetailResponse +import org.store.clothstar.product.service.ProductService @RestController @RequestMapping("/v1/categories") class CategoryController( private val categoryService: CategoryService, -// private val productLineService: ProductLineService + private val productService: ProductService ) { - @Operation(summary = "전체 카테고리 조회", description = "모든 카테고리를 조회한다.") @GetMapping fun getAllCategories(): ResponseEntity> { @@ -57,4 +62,34 @@ class CategoryController( categoryService.deleteCategory(categoryId) return ResponseEntity.ok(MessageDTO(HttpStatus.OK.value(), "Category deleted successfully")) } + + @Operation( + summary = "카테고리별 상품 조회 (Offset Paging)", + description = "카테고리 ID로 해당 카테고리에 속하는 모든 상품을 Offset Paging을 통해 조회한다." + ) + @GetMapping("/{categoryId}/productLines/offset") + fun getProductLinesByCategory( + @PathVariable categoryId: Long, + @PageableDefault(size = 18) pageable: Pageable, + @RequestParam(required = false) keyword: String + ): ResponseEntity> { + val productResponses: Page = + productService.getProductLinesByCategoryWithOffsetPaging(categoryId, pageable, keyword) + return ResponseEntity.ok().body>(productResponses) + } + + @Operation( + summary = "카테고리별 상품 조회 (Slice Paging)", + description = "카테고리 ID로 해당 카테고리에 속하는 모든 상품을 Slice Paging을 통해 조회한다." + ) + @GetMapping("/{categoryId}/productLines/slice") + fun getProductLinesByCategorySlice( + @PathVariable categoryId: Long, + @PageableDefault(size = 18) pageable: Pageable, + @RequestParam(required = false) keyword: String + ): ResponseEntity> { + val productResponses: Slice = + productService.getProductLinesByCategoryWithSlicePaging(categoryId, pageable, keyword) + return ResponseEntity.ok().body>(productResponses) + } } \ No newline at end of file diff --git a/src/test/kotlin/org/store/clothstar/common/config/JpaQueryFactoryConfig.kt b/src/main/kotlin/org/store/clothstar/common/config/JpaQueryFactoryConfig.kt similarity index 55% rename from src/test/kotlin/org/store/clothstar/common/config/JpaQueryFactoryConfig.kt rename to src/main/kotlin/org/store/clothstar/common/config/JpaQueryFactoryConfig.kt index 6922faa..ed7038f 100644 --- a/src/test/kotlin/org/store/clothstar/common/config/JpaQueryFactoryConfig.kt +++ b/src/main/kotlin/org/store/clothstar/common/config/JpaQueryFactoryConfig.kt @@ -2,16 +2,15 @@ package org.store.clothstar.common.config import com.querydsl.jpa.impl.JPAQueryFactory import jakarta.persistence.EntityManager -import jakarta.persistence.PersistenceContext import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration @Configuration -class JpaQueryFactoryConfig { - - @PersistenceContext - private lateinit var entityManager: EntityManager - +class JpaQueryFactoryConfig( + private val em: EntityManager, +) { @Bean - fun jpaQueryFactory() = JPAQueryFactory(entityManager) + fun jpaQueryFactory(): JPAQueryFactory { + return JPAQueryFactory(em) + } } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/member/dto/response/SellerSimpleResponse.kt b/src/main/kotlin/org/store/clothstar/member/dto/response/SellerSimpleResponse.kt index c94bc97..00e9011 100644 --- a/src/main/kotlin/org/store/clothstar/member/dto/response/SellerSimpleResponse.kt +++ b/src/main/kotlin/org/store/clothstar/member/dto/response/SellerSimpleResponse.kt @@ -1,7 +1,19 @@ package org.store.clothstar.member.dto.response +import org.store.clothstar.member.domain.Seller + class SellerSimpleResponse( var memberId: Long, val brandName: String, val bizNo: String, -) \ No newline at end of file +) { + companion object { + fun getSellerSimpleResponseBySeller(seller: Seller): SellerSimpleResponse { + return SellerSimpleResponse( + memberId = seller.memberId, + bizNo = seller.bizNo, + brandName = seller.brandName, + ) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/product/dto/response/ProductDetailResponse.kt b/src/main/kotlin/org/store/clothstar/product/dto/response/ProductDetailResponse.kt new file mode 100644 index 0000000..5521bd4 --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/product/dto/response/ProductDetailResponse.kt @@ -0,0 +1,48 @@ +package org.store.clothstar.product.dto.response + +import io.swagger.v3.oas.annotations.media.Schema +import org.store.clothstar.member.dto.response.SellerSimpleResponse +import org.store.clothstar.product.domain.Item +import org.store.clothstar.product.domain.Product +import org.store.clothstar.product.domain.type.SaleStatus + +class ProductDetailResponse( + @Schema(description = "상품 id", example = "1") + val productId: Long, + + @Schema(description = "상품 이름", example = "우유 모자") + val name: String, + + @Schema(description = "상품 설명", example = "우유 모자입니다.") + val content: String, + + @Schema(description = "상품 가격", example = "10000") + val price: Int = 0, + + @Schema(description = "상품 상태", example = "FOR_SALE") + val saleStatus: SaleStatus, + + @Schema(description = "상품 판매량", example = "10") + val saleCount: Long, // ~개 판매중 + + @Schema(description = "상품 옵션") + val itemList: MutableList, + + @Schema(description = "판매자 정보") + val seller: SellerSimpleResponse, +) { + companion object { + fun getProductDetailResponseByProduct(product: Product, seller: SellerSimpleResponse): ProductDetailResponse { + return ProductDetailResponse( + productId = product.productId!!, + name = product.name, + price = product.price, + saleCount = product.saleCount, + content = product.content, + itemList = product.items, + saleStatus = product.saleStatus, + seller = seller + ) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/product/repository/ProductRepository.kt b/src/main/kotlin/org/store/clothstar/product/repository/ProductRepository.kt index ebe40e4..6900861 100644 --- a/src/main/kotlin/org/store/clothstar/product/repository/ProductRepository.kt +++ b/src/main/kotlin/org/store/clothstar/product/repository/ProductRepository.kt @@ -3,11 +3,8 @@ package org.store.clothstar.product.repository import org.springframework.data.jpa.repository.JpaRepository import org.springframework.stereotype.Repository import org.store.clothstar.product.domain.Product -import java.util.* @Repository -interface ProductRepository : JpaRepository { +interface ProductRepository : JpaRepository, ProductRepositoryCustom { fun findByProductIdIn(productLineIds: List): List - - fun findWithDetailsByProductId(productId: Long): Optional } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustom.kt b/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustom.kt index 2fe9214..41c8444 100644 --- a/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustom.kt +++ b/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustom.kt @@ -2,6 +2,7 @@ package org.store.clothstar.product.repository import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable +import org.springframework.data.domain.Slice import org.springframework.stereotype.Repository import org.store.clothstar.product.domain.Product @@ -10,13 +11,19 @@ interface ProductRepositoryCustom { fun getProductLists(pageable: Pageable?): Page? - fun findWithDetailsByProductId(productId: Long): Product? - fun findAllOffsetPaging(pageable: Pageable): Page fun findAllSlicePaging(pageable: Pageable): List - - - + fun findEntitiesByCategoryWithOffsetPaging( + categoryId: Long, + pageable: Pageable, + keyword: String, + ): Page + + fun findEntitiesByCategoryWithSlicePaging( + categoryId: Long, + pageable: Pageable, + keyword: String, + ): Slice } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustomImpl.kt b/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustomImpl.kt new file mode 100644 index 0000000..fcd87a2 --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustomImpl.kt @@ -0,0 +1,84 @@ +package org.store.clothstar.product.repository + +import com.querydsl.jpa.impl.JPAQueryFactory +import org.springframework.data.domain.Page +import org.springframework.data.domain.Pageable +import org.springframework.data.domain.Slice +import org.springframework.stereotype.Component +import org.store.clothstar.product.domain.Product + +@Component +class ProductRepositoryCustomImpl( + private val jpaQueryFactory: JPAQueryFactory, +) : ProductRepositoryCustom { + override fun getProductLists(pageable: Pageable?): Page? { + TODO("Not yet implemented") + } + + override fun findAllOffsetPaging(pageable: Pageable): Page { + TODO("Not yet implemented") + } + + override fun findAllSlicePaging(pageable: Pageable): List { + TODO("Not yet implemented") + } + + override fun findEntitiesByCategoryWithOffsetPaging( + categoryId: Long, + pageable: Pageable, + keyword: String + ): Page { +// val content = getProductLineEntitiesByCategory(categoryId, pageable, keyword) +// +// var hasNext = false +// if (content.size > pageable.pageSize) { +// content.removeAt(content.size - 1) +// hasNext = true +// } +// +// val totalCount: JPAQuery = jpaQueryFactory +// .select(qProductLine.countDistinct()) +// .from(qProductLine) +// .where( +// qProductLine.category.categoryId.eq(categoryId) +// .and(qProductLine.deletedAt.isNull()) +// .and(getSearchCondition(keyword)) +// ) +// +// return PageableExecutionUtils.getPage(content, pageable, LongSupplier { totalCount.fetchOne()!! }) + TODO("Not yet implemented") + + } + + override fun findEntitiesByCategoryWithSlicePaging( + categoryId: Long, + pageable: Pageable, + keyword: String + ): Slice { + TODO("Not yet implemented") + } + + private fun getProductLineEntitiesByCategory( + categoryId: Long, + pageable: Pageable, + keyword: String + ): List { + TODO("Not yet implemented") + +// val orderSpecifiers: List> = getOrderSpecifiers(pageable.sort) +// +// // 카테고리별로 ProductLine 엔티티를 가져옴 +// return jpaQueryFactory +// .selectDistinct(qProductLine) +// .from(qProductLine) +// .where( +// qProductLine.category.categoryId.eq(categoryId) +// .and(qProductLine.deletedAt.isNull()) +// .and(getSearchCondition(keyword)) +// ) +// .orderBy(orderSpecifiers.toTypedArray()) +// .offset(pageable.offset) +// .limit((pageable.pageSize + 1).toLong()) +// .fetch() + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/product/service/ProductService.kt b/src/main/kotlin/org/store/clothstar/product/service/ProductService.kt index 6cc9f83..08251c6 100644 --- a/src/main/kotlin/org/store/clothstar/product/service/ProductService.kt +++ b/src/main/kotlin/org/store/clothstar/product/service/ProductService.kt @@ -1,18 +1,25 @@ package org.store.clothstar.product.service import jakarta.persistence.EntityNotFoundException +import org.springframework.data.domain.Page +import org.springframework.data.domain.Pageable +import org.springframework.data.domain.Slice import org.springframework.data.repository.findByIdOrNull import org.springframework.http.HttpStatus import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import org.springframework.web.server.ResponseStatusException +import org.store.clothstar.member.dto.response.SellerSimpleResponse +import org.store.clothstar.member.service.SellerService import org.store.clothstar.product.domain.Product +import org.store.clothstar.product.dto.response.ProductDetailResponse import org.store.clothstar.product.dto.response.ProductResponse import org.store.clothstar.product.repository.ProductRepository @Service class ProductService( private val productRepository: ProductRepository, + private val sellerService: SellerService, ) { @Transactional fun createProduct(product: Product): Product { @@ -21,12 +28,44 @@ class ProductService( @Transactional(readOnly = true) fun getProductDetails(productId: Long): ProductResponse { - val product = productRepository.findWithDetailsByProductId(productId) - .orElseThrow { EntityNotFoundException("상품을 찾을 수 없습니다.") } + val product = productRepository.findByIdOrNull(productId) + ?: throw EntityNotFoundException("상품을 찾을 수 없습니다.") return ProductResponse.from(product) } + @Transactional + fun getProductLinesByCategoryWithOffsetPaging( + categoryId: Long, + pageable: Pageable, + keyword: String, + ): Page { + val allOffsetPagingByCategory = + productRepository.findEntitiesByCategoryWithOffsetPaging(categoryId, pageable, keyword) + + return allOffsetPagingByCategory.map { product -> + val seller = sellerService.getSellerById(product.memberId) + val sellerSimpleResponse = SellerSimpleResponse.getSellerSimpleResponseBySeller(seller) + ProductDetailResponse.getProductDetailResponseByProduct(product, sellerSimpleResponse) + } + } + + @Transactional + fun getProductLinesByCategoryWithSlicePaging( + categoryId: Long, + pageable: Pageable, + keyword: String, + ): Slice { + val allSlicePagingByCategory = + productRepository.findEntitiesByCategoryWithSlicePaging(categoryId, pageable, keyword) + + return allSlicePagingByCategory.map { product -> + val seller = sellerService.getSellerById(product.memberId) + val sellerSimpleResponse = SellerSimpleResponse.getSellerSimpleResponseBySeller(seller) + ProductDetailResponse.getProductDetailResponseByProduct(product, sellerSimpleResponse) + } + } + fun findByProductIdIn(productIds: List): List { return productRepository.findByProductIdIn(productIds).map { it ?: throw IllegalArgumentException("상품을 조회할 수 없습니다.") From a32b25910fb0646be488121a1c9169a022478fb9 Mon Sep 17 00:00:00 2001 From: hyunsu Date: Sat, 17 Aug 2024 02:39:42 +0900 Subject: [PATCH 04/13] =?UTF-8?q?feat:=20category=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=95=20=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../category/controller/CategoryController.kt | 4 +- .../store/clothstar/product/domain/Product.kt | 5 +- .../repository/ProductRepositoryCustom.kt | 4 +- .../repository/ProductRepositoryCustomImpl.kt | 118 ++++++++++++------ .../product/service/ProductService.kt | 4 +- 5 files changed, 86 insertions(+), 49 deletions(-) diff --git a/src/main/kotlin/org/store/clothstar/category/controller/CategoryController.kt b/src/main/kotlin/org/store/clothstar/category/controller/CategoryController.kt index 60e492e..663ef47 100644 --- a/src/main/kotlin/org/store/clothstar/category/controller/CategoryController.kt +++ b/src/main/kotlin/org/store/clothstar/category/controller/CategoryController.kt @@ -71,7 +71,7 @@ class CategoryController( fun getProductLinesByCategory( @PathVariable categoryId: Long, @PageableDefault(size = 18) pageable: Pageable, - @RequestParam(required = false) keyword: String + @RequestParam(required = false) keyword: String? ): ResponseEntity> { val productResponses: Page = productService.getProductLinesByCategoryWithOffsetPaging(categoryId, pageable, keyword) @@ -86,7 +86,7 @@ class CategoryController( fun getProductLinesByCategorySlice( @PathVariable categoryId: Long, @PageableDefault(size = 18) pageable: Pageable, - @RequestParam(required = false) keyword: String + @RequestParam(required = false) keyword: String? ): ResponseEntity> { val productResponses: Slice = productService.getProductLinesByCategoryWithSlicePaging(categoryId, pageable, keyword) diff --git a/src/main/kotlin/org/store/clothstar/product/domain/Product.kt b/src/main/kotlin/org/store/clothstar/product/domain/Product.kt index 066e501..70a2918 100644 --- a/src/main/kotlin/org/store/clothstar/product/domain/Product.kt +++ b/src/main/kotlin/org/store/clothstar/product/domain/Product.kt @@ -1,5 +1,6 @@ package org.store.clothstar.product.domain +import com.fasterxml.jackson.annotation.JsonIgnore import jakarta.persistence.* import org.hibernate.annotations.Fetch import org.hibernate.annotations.FetchMode @@ -46,16 +47,16 @@ class Product( var saleStatus: SaleStatus, // 판매 상태 // 연관 관계 (1:N) + @JsonIgnore @Fetch(FetchMode.SUBSELECT) @OneToMany(mappedBy = "product", cascade = [CascadeType.ALL], fetch = FetchType.LAZY) var productOptions: MutableList = mutableListOf(), + @JsonIgnore @Fetch(FetchMode.SUBSELECT) @OneToMany(mappedBy = "product", fetch = FetchType.LAZY, cascade = [CascadeType.ALL]) var items: MutableList = mutableListOf(), ) : BaseEntity() { - - fun updateSaleStatus(saleStatus: SaleStatus) { this.saleStatus = saleStatus } diff --git a/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustom.kt b/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustom.kt index 41c8444..eba75a0 100644 --- a/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustom.kt +++ b/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustom.kt @@ -18,12 +18,12 @@ interface ProductRepositoryCustom { fun findEntitiesByCategoryWithOffsetPaging( categoryId: Long, pageable: Pageable, - keyword: String, + keyword: String?, ): Page fun findEntitiesByCategoryWithSlicePaging( categoryId: Long, pageable: Pageable, - keyword: String, + keyword: String?, ): Slice } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustomImpl.kt b/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustomImpl.kt index fcd87a2..27b5d47 100644 --- a/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustomImpl.kt +++ b/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustomImpl.kt @@ -1,16 +1,22 @@ package org.store.clothstar.product.repository +import com.querydsl.core.types.dsl.BooleanExpression import com.querydsl.jpa.impl.JPAQueryFactory import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.data.domain.Slice +import org.springframework.data.domain.SliceImpl +import org.springframework.data.support.PageableExecutionUtils import org.springframework.stereotype.Component import org.store.clothstar.product.domain.Product +import org.store.clothstar.product.domain.QProduct.product +import java.util.function.LongSupplier @Component class ProductRepositoryCustomImpl( - private val jpaQueryFactory: JPAQueryFactory, + private val queryFactory: JPAQueryFactory, ) : ProductRepositoryCustom { + override fun getProductLists(pageable: Pageable?): Page? { TODO("Not yet implemented") } @@ -26,59 +32,89 @@ class ProductRepositoryCustomImpl( override fun findEntitiesByCategoryWithOffsetPaging( categoryId: Long, pageable: Pageable, - keyword: String + keyword: String? ): Page { -// val content = getProductLineEntitiesByCategory(categoryId, pageable, keyword) -// -// var hasNext = false -// if (content.size > pageable.pageSize) { -// content.removeAt(content.size - 1) -// hasNext = true -// } -// -// val totalCount: JPAQuery = jpaQueryFactory -// .select(qProductLine.countDistinct()) -// .from(qProductLine) -// .where( -// qProductLine.category.categoryId.eq(categoryId) -// .and(qProductLine.deletedAt.isNull()) -// .and(getSearchCondition(keyword)) -// ) -// -// return PageableExecutionUtils.getPage(content, pageable, LongSupplier { totalCount.fetchOne()!! }) - TODO("Not yet implemented") + val content = getProductsByCategory(categoryId, pageable, keyword) + + var hasNext = false + if (content.size > pageable.pageSize) { + content.removeAt(content.size - 1) + hasNext = true + } + + val totalCount = queryFactory + .select(product.countDistinct()) + .from(product) + .where( + product.categoryId.eq(categoryId) + .and(product.deletedAt.isNull()) + .and(getSearchCondition(keyword)) + ) + return PageableExecutionUtils.getPage(content, pageable, LongSupplier { totalCount.fetchOne()!! }) } override fun findEntitiesByCategoryWithSlicePaging( categoryId: Long, pageable: Pageable, - keyword: String + keyword: String? ): Slice { - TODO("Not yet implemented") + val content = getProductsByCategory(categoryId, pageable, keyword) + + var hasNext = false + if (content.size > pageable.pageSize) { + content.removeAt(content.size - 1) + hasNext = true + } + + return SliceImpl(content, pageable, hasNext) } - private fun getProductLineEntitiesByCategory( + private fun getProductsByCategory( categoryId: Long, pageable: Pageable, - keyword: String - ): List { - TODO("Not yet implemented") + keyword: String? + ): MutableList { +// val orderSpecifiers = getOrderSpecifiers(pageable.sort) -// val orderSpecifiers: List> = getOrderSpecifiers(pageable.sort) -// -// // 카테고리별로 ProductLine 엔티티를 가져옴 -// return jpaQueryFactory -// .selectDistinct(qProductLine) -// .from(qProductLine) -// .where( -// qProductLine.category.categoryId.eq(categoryId) -// .and(qProductLine.deletedAt.isNull()) -// .and(getSearchCondition(keyword)) -// ) + // 카테고리별로 Product 엔티티를 가져옴 + return queryFactory + .selectDistinct(product) + .from(product) + .where( + product.categoryId.eq(categoryId) + .and(product.deletedAt.isNull()) + .and(getSearchCondition(keyword)) + ) // .orderBy(orderSpecifiers.toTypedArray()) -// .offset(pageable.offset) -// .limit((pageable.pageSize + 1).toLong()) -// .fetch() + .offset(pageable.offset) + .limit((pageable.pageSize + 1).toLong()) + .fetch() + } + + private fun getSearchCondition(keyword: String?): BooleanExpression { + if (keyword == null || keyword.isEmpty()) { + return product.isNotNull() // 조건이 없을 경우 항상 true를 반환 + } + return product.name.containsIgnoreCase(keyword) + .or(product.content.containsIgnoreCase(keyword)) } + +// private fun getOrderSpecifiers(sort: Sort): List> { +// val orderSpecifiers: List> = ArrayList() +// +// for (order in sort) { +// when (order.property) { +// "saleCount" -> { +// orderSpecifiers.add(if (order.isAscending) product.saleCount.asc() else product.saleCount.desc()) +// } +// +// "createdAt" -> orderSpecifiers.add(if (order.isAscending) product.createdAt.asc() else product.createdAt.desc()) +// "price" -> orderSpecifiers.add(if (order.isAscending) product.price.asc() else product.price.desc()) +// else -> {} +// } +// } +// +// return orderSpecifiers +// } } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/product/service/ProductService.kt b/src/main/kotlin/org/store/clothstar/product/service/ProductService.kt index 08251c6..3856fb5 100644 --- a/src/main/kotlin/org/store/clothstar/product/service/ProductService.kt +++ b/src/main/kotlin/org/store/clothstar/product/service/ProductService.kt @@ -38,7 +38,7 @@ class ProductService( fun getProductLinesByCategoryWithOffsetPaging( categoryId: Long, pageable: Pageable, - keyword: String, + keyword: String?, ): Page { val allOffsetPagingByCategory = productRepository.findEntitiesByCategoryWithOffsetPaging(categoryId, pageable, keyword) @@ -54,7 +54,7 @@ class ProductService( fun getProductLinesByCategoryWithSlicePaging( categoryId: Long, pageable: Pageable, - keyword: String, + keyword: String?, ): Slice { val allSlicePagingByCategory = productRepository.findEntitiesByCategoryWithSlicePaging(categoryId, pageable, keyword) From bfa8d303ad12808057fac782f20594841f34d461 Mon Sep 17 00:00:00 2001 From: Ogu1208 Date: Sat, 17 Aug 2024 11:59:45 +0900 Subject: [PATCH 05/13] =?UTF-8?q?feat:=20=EC=83=81=ED=92=88=20=EC=A0=84?= =?UTF-8?q?=EC=B2=B4=20=ED=8E=98=EC=9D=B4=EC=A7=95=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20-=20Slice,=20Offset?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 정렬 옵션 구현 (price, saleCount, createdAt) - 키워드 검색 필터링 구현 --- .../category/controller/CategoryController.kt | 14 +-- .../product/controller/ProductController.kt | 36 +++++-- ...tailResponse.kt => ProductListResponse.kt} | 10 +- .../product/dto/response/ProductResponse.kt | 10 +- .../repository/ProductRepositoryCustom.kt | 6 +- .../repository/ProductRepositoryCustomImpl.kt | 94 +++++++++++++------ .../service/ProductApplicationService.kt | 15 ++- .../product/service/ProductService.kt | 38 ++++++-- src/main/resources/application-db.yml | 2 +- 9 files changed, 160 insertions(+), 65 deletions(-) rename src/main/kotlin/org/store/clothstar/product/dto/response/{ProductDetailResponse.kt => ProductListResponse.kt} (83%) diff --git a/src/main/kotlin/org/store/clothstar/category/controller/CategoryController.kt b/src/main/kotlin/org/store/clothstar/category/controller/CategoryController.kt index 663ef47..3baa4e1 100644 --- a/src/main/kotlin/org/store/clothstar/category/controller/CategoryController.kt +++ b/src/main/kotlin/org/store/clothstar/category/controller/CategoryController.kt @@ -15,7 +15,7 @@ import org.store.clothstar.category.dto.response.CategoryResponse import org.store.clothstar.category.service.CategoryService import org.store.clothstar.common.dto.MessageDTO import org.store.clothstar.common.util.URIBuilder -import org.store.clothstar.product.dto.response.ProductDetailResponse +import org.store.clothstar.product.dto.response.ProductListResponse import org.store.clothstar.product.service.ProductService @RestController @@ -72,10 +72,10 @@ class CategoryController( @PathVariable categoryId: Long, @PageableDefault(size = 18) pageable: Pageable, @RequestParam(required = false) keyword: String? - ): ResponseEntity> { - val productResponses: Page = + ): ResponseEntity> { + val productResponses: Page = productService.getProductLinesByCategoryWithOffsetPaging(categoryId, pageable, keyword) - return ResponseEntity.ok().body>(productResponses) + return ResponseEntity.ok().body>(productResponses) } @Operation( @@ -87,9 +87,9 @@ class CategoryController( @PathVariable categoryId: Long, @PageableDefault(size = 18) pageable: Pageable, @RequestParam(required = false) keyword: String? - ): ResponseEntity> { - val productResponses: Slice = + ): ResponseEntity> { + val productResponses: Slice = productService.getProductLinesByCategoryWithSlicePaging(categoryId, pageable, keyword) - return ResponseEntity.ok().body>(productResponses) + return ResponseEntity.ok().body>(productResponses) } } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/product/controller/ProductController.kt b/src/main/kotlin/org/store/clothstar/product/controller/ProductController.kt index 6f4c847..d7d43c5 100644 --- a/src/main/kotlin/org/store/clothstar/product/controller/ProductController.kt +++ b/src/main/kotlin/org/store/clothstar/product/controller/ProductController.kt @@ -2,12 +2,14 @@ package org.store.clothstar.product.controller import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.tags.Tag +import org.springframework.data.domain.Page +import org.springframework.data.domain.Pageable +import org.springframework.data.domain.Slice +import org.springframework.data.web.PageableDefault import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.PathVariable -import org.springframework.web.bind.annotation.RequestMapping -import org.springframework.web.bind.annotation.RestController +import org.springframework.web.bind.annotation.* +import org.store.clothstar.product.dto.response.ProductListResponse import org.store.clothstar.product.dto.response.ProductResponse import org.store.clothstar.product.service.ProductApplicationService @@ -40,14 +42,32 @@ private class ProductController( */ + // 상품 전체 Offset 페이징 조회 + @GetMapping("/offset") + @Operation(summary = "상품 전체 Offset 페이징 조회", description = "상품 전체 리스트를 Offset 페이징 형식으로 조회한다.") + fun getAllProductsOffsetPaging( + @PageableDefault(size = 18) pageable: Pageable, + @RequestParam(required = false) keyword: String? + ): ResponseEntity> { + val productPages = productApplicationService.getAllProductsOffsetPaging(pageable, keyword) + return ResponseEntity.ok().body(productPages) + } + + // 상품 전체 Slice 페이징 조회 + @GetMapping("/slice") + @Operation(summary = "상품 전체 Slice 페이징 조회", description = "상품 전체 리스트를 Slice 페이징 형식으로 조회한다.") + fun getAllProductsSlicePaging( + @PageableDefault(size = 18) pageable: Pageable, + @RequestParam(required = false) keyword: String? + ): ResponseEntity> { + val productPages = productApplicationService.getAllProductsSlicePaging(pageable, keyword) + return ResponseEntity.ok().body(productPages) + } + @GetMapping("/{productId}") @Operation(summary = "상품 상세 조회", description = "상품 ID를 사용하여 특정 상품의 상세 정보를 조회한다.") fun getProductDetails(@PathVariable productId: Long): ResponseEntity { val productResponse = productApplicationService.getProductDetails(productId) return ResponseEntity(productResponse, HttpStatus.OK) } - - // 카테고리별 Offset 상품 조회 - - // 카테고리별 Pagination 상품 조회 } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/product/dto/response/ProductDetailResponse.kt b/src/main/kotlin/org/store/clothstar/product/dto/response/ProductListResponse.kt similarity index 83% rename from src/main/kotlin/org/store/clothstar/product/dto/response/ProductDetailResponse.kt rename to src/main/kotlin/org/store/clothstar/product/dto/response/ProductListResponse.kt index 5521bd4..c3330e5 100644 --- a/src/main/kotlin/org/store/clothstar/product/dto/response/ProductDetailResponse.kt +++ b/src/main/kotlin/org/store/clothstar/product/dto/response/ProductListResponse.kt @@ -6,7 +6,7 @@ import org.store.clothstar.product.domain.Item import org.store.clothstar.product.domain.Product import org.store.clothstar.product.domain.type.SaleStatus -class ProductDetailResponse( +class ProductListResponse( @Schema(description = "상품 id", example = "1") val productId: Long, @@ -26,20 +26,20 @@ class ProductDetailResponse( val saleCount: Long, // ~개 판매중 @Schema(description = "상품 옵션") - val itemList: MutableList, + val itemList: List, @Schema(description = "판매자 정보") val seller: SellerSimpleResponse, ) { companion object { - fun getProductDetailResponseByProduct(product: Product, seller: SellerSimpleResponse): ProductDetailResponse { - return ProductDetailResponse( + fun from(product: Product, seller: SellerSimpleResponse): ProductListResponse { + return ProductListResponse( productId = product.productId!!, name = product.name, price = product.price, saleCount = product.saleCount, content = product.content, - itemList = product.items, + itemList = product.items.map { ItemResponse.from(it) }, saleStatus = product.saleStatus, seller = seller ) diff --git a/src/main/kotlin/org/store/clothstar/product/dto/response/ProductResponse.kt b/src/main/kotlin/org/store/clothstar/product/dto/response/ProductResponse.kt index f681adb..96beae7 100644 --- a/src/main/kotlin/org/store/clothstar/product/dto/response/ProductResponse.kt +++ b/src/main/kotlin/org/store/clothstar/product/dto/response/ProductResponse.kt @@ -1,5 +1,7 @@ package org.store.clothstar.product.dto.response +import org.store.clothstar.member.domain.Seller +import org.store.clothstar.member.dto.response.SellerSimpleResponse import org.store.clothstar.product.domain.* import org.store.clothstar.product.domain.type.DisplayStatus import org.store.clothstar.product.domain.type.ImageType @@ -33,10 +35,11 @@ class ProductResponse( val productColors: Set, val imageList: List, val productOptions: List, - val items: List + val items: List, + val seller: SellerSimpleResponse ) { companion object { - fun from(product: Product): ProductResponse { + fun from(product: Product, seller: SellerSimpleResponse): ProductResponse { return ProductResponse( id = product.productId!!, name = product.name, @@ -47,7 +50,8 @@ class ProductResponse( displayStatus = product.displayStatus, saleStatus = product.saleStatus, productOptions = product.productOptions.map { ProductOptionResponse.from(it) }, - items = product.items.map { ItemResponse.from(it) } + items = product.items.map { ItemResponse.from(it) }, + seller = seller ) } } diff --git a/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustom.kt b/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustom.kt index eba75a0..7429e17 100644 --- a/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustom.kt +++ b/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustom.kt @@ -9,11 +9,9 @@ import org.store.clothstar.product.domain.Product @Repository interface ProductRepositoryCustom { - fun getProductLists(pageable: Pageable?): Page? + fun findAllOffsetPaging(pageable: Pageable, keyword: String?): Page - fun findAllOffsetPaging(pageable: Pageable): Page - - fun findAllSlicePaging(pageable: Pageable): List + fun findAllSlicePaging(pageable: Pageable, keyword: String?): Slice fun findEntitiesByCategoryWithOffsetPaging( categoryId: Long, diff --git a/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustomImpl.kt b/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustomImpl.kt index 27b5d47..262f453 100644 --- a/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustomImpl.kt +++ b/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustomImpl.kt @@ -1,11 +1,10 @@ package org.store.clothstar.product.repository +import com.querydsl.core.types.OrderSpecifier import com.querydsl.core.types.dsl.BooleanExpression +import com.querydsl.jpa.impl.JPAQuery import com.querydsl.jpa.impl.JPAQueryFactory -import org.springframework.data.domain.Page -import org.springframework.data.domain.Pageable -import org.springframework.data.domain.Slice -import org.springframework.data.domain.SliceImpl +import org.springframework.data.domain.* import org.springframework.data.support.PageableExecutionUtils import org.springframework.stereotype.Component import org.store.clothstar.product.domain.Product @@ -17,16 +16,53 @@ class ProductRepositoryCustomImpl( private val queryFactory: JPAQueryFactory, ) : ProductRepositoryCustom { - override fun getProductLists(pageable: Pageable?): Page? { - TODO("Not yet implemented") + override fun findAllOffsetPaging(pageable: Pageable, keyword: String?): Page { + val content: MutableList = getProductLines(pageable, keyword) + + var hasNext = false + if (content.size > pageable.pageSize) { + content.removeAt(content.size - 1) + hasNext = true + } + + val totalCount: JPAQuery = queryFactory + .select(product.countDistinct()) + .from(product) + .where( + product.deletedAt.isNull() + .and(getSearchCondition(keyword)) + ) + + return PageableExecutionUtils.getPage(content, pageable) { totalCount.fetchOne()!! } } - override fun findAllOffsetPaging(pageable: Pageable): Page { - TODO("Not yet implemented") + override fun findAllSlicePaging(pageable: Pageable, keyword: String?): Slice { + val content: MutableList = getProductLines(pageable, keyword) + + var hasNext = false + if (content.size > pageable.pageSize) { + content.removeAt(content.size - 1) + hasNext = true + } + + return SliceImpl(content, pageable, hasNext) } - override fun findAllSlicePaging(pageable: Pageable): List { - TODO("Not yet implemented") + private fun getProductLines(pageable: Pageable, keyword: String?): MutableList { + val orderSpecifiers = getOrderSpecifiers(pageable.sort) + + // 카테고리별로 ProductLine 엔티티를 가져옴 + return queryFactory + .selectDistinct(product) + .from(product) + .where( + product.deletedAt.isNull() + .and(getSearchCondition(keyword)) + ) + .orderBy(*orderSpecifiers.toTypedArray()) // * -> 스프레드 연산자 : 배열의 각 요소를 개별 인자로 전달 + .offset(pageable.offset) + .limit((pageable.pageSize + 1).toLong()) + .fetch() } override fun findEntitiesByCategoryWithOffsetPaging( @@ -75,7 +111,7 @@ class ProductRepositoryCustomImpl( pageable: Pageable, keyword: String? ): MutableList { -// val orderSpecifiers = getOrderSpecifiers(pageable.sort) + val orderSpecifiers = getOrderSpecifiers(pageable.sort) // 카테고리별로 Product 엔티티를 가져옴 return queryFactory @@ -86,35 +122,33 @@ class ProductRepositoryCustomImpl( .and(product.deletedAt.isNull()) .and(getSearchCondition(keyword)) ) -// .orderBy(orderSpecifiers.toTypedArray()) + .orderBy(*orderSpecifiers.toTypedArray()) .offset(pageable.offset) .limit((pageable.pageSize + 1).toLong()) .fetch() } private fun getSearchCondition(keyword: String?): BooleanExpression { - if (keyword == null || keyword.isEmpty()) { + if (keyword.isNullOrEmpty()) { return product.isNotNull() // 조건이 없을 경우 항상 true를 반환 } return product.name.containsIgnoreCase(keyword) .or(product.content.containsIgnoreCase(keyword)) } -// private fun getOrderSpecifiers(sort: Sort): List> { -// val orderSpecifiers: List> = ArrayList() -// -// for (order in sort) { -// when (order.property) { -// "saleCount" -> { -// orderSpecifiers.add(if (order.isAscending) product.saleCount.asc() else product.saleCount.desc()) -// } -// -// "createdAt" -> orderSpecifiers.add(if (order.isAscending) product.createdAt.asc() else product.createdAt.desc()) -// "price" -> orderSpecifiers.add(if (order.isAscending) product.price.asc() else product.price.desc()) -// else -> {} -// } -// } -// -// return orderSpecifiers -// } + private fun getOrderSpecifiers(sort: Sort?): List> { + val orderSpecifiers = mutableListOf>() + + sort?.forEach { order -> + val orderSpecifier = when (order.property) { + "saleCount" -> if (order.isAscending) product.saleCount.asc() else product.saleCount.desc() + "createdAt" -> if (order.isAscending) product.createdAt.asc() else product.createdAt.desc() + "price" -> if (order.isAscending) product.price.asc() else product.price.desc() + else -> null + } + orderSpecifier?.let { orderSpecifiers.add(it) } + } + + return orderSpecifiers + } } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/product/service/ProductApplicationService.kt b/src/main/kotlin/org/store/clothstar/product/service/ProductApplicationService.kt index 4c78baa..31804ba 100644 --- a/src/main/kotlin/org/store/clothstar/product/service/ProductApplicationService.kt +++ b/src/main/kotlin/org/store/clothstar/product/service/ProductApplicationService.kt @@ -1,5 +1,8 @@ package org.store.clothstar.product.service +import org.springframework.data.domain.Page +import org.springframework.data.domain.Pageable +import org.springframework.data.domain.Slice import org.springframework.security.core.context.SecurityContextHolder import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -10,6 +13,7 @@ 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.ProductListResponse import org.store.clothstar.product.dto.response.ProductResponse @@ -70,10 +74,19 @@ class ProductApplicationService( @Transactional(readOnly = true) fun getProductDetails(productId: Long): ProductResponse { - return productService.getProductDetails(productId) } + @Transactional(readOnly = true) + fun getAllProductsOffsetPaging(pageable: Pageable, keyword: String?): Page { + return productService.getAllProductsOffsetPaging(pageable, keyword) + } + + @Transactional(readOnly = true) + fun getAllProductsSlicePaging(pageable: Pageable, keyword: String?): Slice { + return productService.getAllProductsSlicePaging(pageable, keyword) + } + @Transactional fun updateProductDisplayStatus(productId: Long, displayStatus: DisplayStatus) { val product = productService.getProductById(productId) diff --git a/src/main/kotlin/org/store/clothstar/product/service/ProductService.kt b/src/main/kotlin/org/store/clothstar/product/service/ProductService.kt index 3856fb5..2e06537 100644 --- a/src/main/kotlin/org/store/clothstar/product/service/ProductService.kt +++ b/src/main/kotlin/org/store/clothstar/product/service/ProductService.kt @@ -12,7 +12,7 @@ import org.springframework.web.server.ResponseStatusException import org.store.clothstar.member.dto.response.SellerSimpleResponse import org.store.clothstar.member.service.SellerService import org.store.clothstar.product.domain.Product -import org.store.clothstar.product.dto.response.ProductDetailResponse +import org.store.clothstar.product.dto.response.ProductListResponse import org.store.clothstar.product.dto.response.ProductResponse import org.store.clothstar.product.repository.ProductRepository @@ -31,7 +31,9 @@ class ProductService( val product = productRepository.findByIdOrNull(productId) ?: throw EntityNotFoundException("상품을 찾을 수 없습니다.") - return ProductResponse.from(product) + val seller = sellerService.getSellerById(product.memberId) + val sellerSimpleResponse = SellerSimpleResponse.getSellerSimpleResponseBySeller(seller) + return ProductResponse.from(product, sellerSimpleResponse) } @Transactional @@ -39,14 +41,14 @@ class ProductService( categoryId: Long, pageable: Pageable, keyword: String?, - ): Page { + ): Page { val allOffsetPagingByCategory = productRepository.findEntitiesByCategoryWithOffsetPaging(categoryId, pageable, keyword) return allOffsetPagingByCategory.map { product -> val seller = sellerService.getSellerById(product.memberId) val sellerSimpleResponse = SellerSimpleResponse.getSellerSimpleResponseBySeller(seller) - ProductDetailResponse.getProductDetailResponseByProduct(product, sellerSimpleResponse) + ProductListResponse.from(product, sellerSimpleResponse) } } @@ -55,14 +57,38 @@ class ProductService( categoryId: Long, pageable: Pageable, keyword: String?, - ): Slice { + ): Slice { val allSlicePagingByCategory = productRepository.findEntitiesByCategoryWithSlicePaging(categoryId, pageable, keyword) return allSlicePagingByCategory.map { product -> val seller = sellerService.getSellerById(product.memberId) val sellerSimpleResponse = SellerSimpleResponse.getSellerSimpleResponseBySeller(seller) - ProductDetailResponse.getProductDetailResponseByProduct(product, sellerSimpleResponse) + ProductListResponse.from(product, sellerSimpleResponse) + } + } + + fun getAllProductsOffsetPaging(pageable: Pageable, keyword: String? + ): Page { + + val productPages = productRepository.findAllOffsetPaging(pageable, keyword) + + return productPages.map { product -> + val seller = sellerService.getSellerById(product.memberId) + val sellerSimpleResponse = SellerSimpleResponse.getSellerSimpleResponseBySeller(seller) + ProductListResponse.from(product, sellerSimpleResponse) + } + } + + fun getAllProductsSlicePaging(pageable: Pageable, keyword: String? + ): Slice { + + val productPages = productRepository.findAllSlicePaging(pageable, keyword) + + return productPages.map { product -> + val seller = sellerService.getSellerById(product.memberId) + val sellerSimpleResponse = SellerSimpleResponse.getSellerSimpleResponseBySeller(seller) + ProductListResponse.from(product, sellerSimpleResponse) } } diff --git a/src/main/resources/application-db.yml b/src/main/resources/application-db.yml index 4e2c36a..708ba4f 100644 --- a/src/main/resources/application-db.yml +++ b/src/main/resources/application-db.yml @@ -30,7 +30,7 @@ spring: jpa: properties: hibernate: - default_batch_fetch_size: 200 + default_batch_fetch_size: 100 --- # local 공통 설정 spring: From 430589f5b0abf15ab7494c98dbed56e4a8a7d1e2 Mon Sep 17 00:00:00 2001 From: Ogu1208 Date: Sat, 17 Aug 2024 15:21:10 +0900 Subject: [PATCH 06/13] =?UTF-8?q?refactor:=20category=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=95=20=EC=A1=B0=ED=9A=8C=20API=20=EC=97=94=EB=93=9C?= =?UTF-8?q?=ED=8F=AC=EC=9D=B8=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../category/controller/CategoryController.kt | 61 ++++++++++--------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/src/main/kotlin/org/store/clothstar/category/controller/CategoryController.kt b/src/main/kotlin/org/store/clothstar/category/controller/CategoryController.kt index 3baa4e1..7b7adeb 100644 --- a/src/main/kotlin/org/store/clothstar/category/controller/CategoryController.kt +++ b/src/main/kotlin/org/store/clothstar/category/controller/CategoryController.kt @@ -31,6 +31,37 @@ class CategoryController( return ResponseEntity.ok(categoryResponses) } + + @Operation( + summary = "카테고리별 상품 조회 (Offset Paging)", + description = "카테고리 ID로 해당 카테고리에 속하는 모든 상품을 Offset Paging을 통해 조회한다." + ) + @GetMapping("/{categoryId}/products/offset") + fun getProductLinesByCategory( + @PathVariable categoryId: Long, + @PageableDefault(size = 18) pageable: Pageable, + @RequestParam(required = false) keyword: String? + ): ResponseEntity> { + val productResponses: Page = + productService.getProductLinesByCategoryWithOffsetPaging(categoryId, pageable, keyword) + return ResponseEntity.ok().body>(productResponses) + } + + @Operation( + summary = "카테고리별 상품 조회 (Slice Paging)", + description = "카테고리 ID로 해당 카테고리에 속하는 모든 상품을 Slice Paging을 통해 조회한다." + ) + @GetMapping("/{categoryId}/products/slice") + fun getProductLinesByCategorySlice( + @PathVariable categoryId: Long, + @PageableDefault(size = 18) pageable: Pageable, + @RequestParam(required = false) keyword: String? + ): ResponseEntity> { + val productResponses: Slice = + productService.getProductLinesByCategoryWithSlicePaging(categoryId, pageable, keyword) + return ResponseEntity.ok().body>(productResponses) + } + @Operation(summary = "카테고리 상세 조회", description = "id로 카테고리 한개를 상세 조회한다.") @GetMapping("/{categoryId}") fun getCategory(@PathVariable categoryId: Long): ResponseEntity { @@ -62,34 +93,4 @@ class CategoryController( categoryService.deleteCategory(categoryId) return ResponseEntity.ok(MessageDTO(HttpStatus.OK.value(), "Category deleted successfully")) } - - @Operation( - summary = "카테고리별 상품 조회 (Offset Paging)", - description = "카테고리 ID로 해당 카테고리에 속하는 모든 상품을 Offset Paging을 통해 조회한다." - ) - @GetMapping("/{categoryId}/productLines/offset") - fun getProductLinesByCategory( - @PathVariable categoryId: Long, - @PageableDefault(size = 18) pageable: Pageable, - @RequestParam(required = false) keyword: String? - ): ResponseEntity> { - val productResponses: Page = - productService.getProductLinesByCategoryWithOffsetPaging(categoryId, pageable, keyword) - return ResponseEntity.ok().body>(productResponses) - } - - @Operation( - summary = "카테고리별 상품 조회 (Slice Paging)", - description = "카테고리 ID로 해당 카테고리에 속하는 모든 상품을 Slice Paging을 통해 조회한다." - ) - @GetMapping("/{categoryId}/productLines/slice") - fun getProductLinesByCategorySlice( - @PathVariable categoryId: Long, - @PageableDefault(size = 18) pageable: Pageable, - @RequestParam(required = false) keyword: String? - ): ResponseEntity> { - val productResponses: Slice = - productService.getProductLinesByCategoryWithSlicePaging(categoryId, pageable, keyword) - return ResponseEntity.ok().body>(productResponses) - } } \ No newline at end of file From 1201a866c1d52d611f4e9cf6bb6be7f3123eba31 Mon Sep 17 00:00:00 2001 From: Ogu1208 Date: Sat, 17 Aug 2024 15:38:14 +0900 Subject: [PATCH 07/13] =?UTF-8?q?refactor:=20product=20seller=20API=20?= =?UTF-8?q?=EC=97=94=EB=93=9C=ED=8F=AC=EC=9D=B8=ED=8A=B8=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../store/clothstar/common/config/SecurityConfiguration.kt | 2 +- .../store/clothstar/product/controller/ProductController.kt | 6 ++++-- .../clothstar/product/controller/ProductSellerController.kt | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt b/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt index ccd6561..fa58217 100644 --- a/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt +++ b/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt @@ -61,7 +61,7 @@ class SecurityConfiguration( "/v1/members/login", "/signup", "/v1/members/email/**", "/v1/access", "/v1/categories/**", "/v1/products/**", "/v1/productLines/**", "/v2/productLines/**", "/productLinePagingSlice", "/productLinePagingOffset", - "/v3/products/**", + "/v3/products/**", "v3/sellers/products/**", "/v1/orderdetails", "/v1/orders", "membersPagingOffset", "membersPagingSlice", "/v1/orderdetails", "/v1/orders", "/v2/orders", "/v3/orders", "/v1/orders/list", "/v1/orders/list", "/ordersPagingOffset", "/ordersPagingSlice", "/v2/orders/list", diff --git a/src/main/kotlin/org/store/clothstar/product/controller/ProductController.kt b/src/main/kotlin/org/store/clothstar/product/controller/ProductController.kt index d7d43c5..0f583aa 100644 --- a/src/main/kotlin/org/store/clothstar/product/controller/ProductController.kt +++ b/src/main/kotlin/org/store/clothstar/product/controller/ProductController.kt @@ -8,7 +8,11 @@ import org.springframework.data.domain.Slice import org.springframework.data.web.PageableDefault import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity +import org.springframework.validation.annotation.Validated 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.ProductListResponse import org.store.clothstar.product.dto.response.ProductResponse import org.store.clothstar.product.service.ProductApplicationService @@ -39,8 +43,6 @@ private class ProductController( return ResponseEntity(messageDTO, HttpStatus.CREATED) } - - */ // 상품 전체 Offset 페이징 조회 @GetMapping("/offset") diff --git a/src/main/kotlin/org/store/clothstar/product/controller/ProductSellerController.kt b/src/main/kotlin/org/store/clothstar/product/controller/ProductSellerController.kt index 271f2f0..372c18d 100644 --- a/src/main/kotlin/org/store/clothstar/product/controller/ProductSellerController.kt +++ b/src/main/kotlin/org/store/clothstar/product/controller/ProductSellerController.kt @@ -16,7 +16,7 @@ import org.store.clothstar.product.service.ProductApplicationService @Tag(name = "ProductSellers", description = "ProductSellers(판매자) 관련 API 입니다.") @RestController -@RequestMapping("/v3/products/sellers") +@RequestMapping("/v3/sellers/products") class ProductSellerController( private val productApplicationService: ProductApplicationService ) { From 62183420b38d07b4b51690e373d6f473b9b10dc4 Mon Sep 17 00:00:00 2001 From: Ogu1208 Date: Sat, 17 Aug 2024 15:38:43 +0900 Subject: [PATCH 08/13] =?UTF-8?q?refactor:=20=EC=83=81=ED=92=88=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=95=20=EC=A1=B0=ED=9A=8C=20=EC=A1=B0?= =?UTF-8?q?=EA=B1=B4=EC=97=90=20displaySataus.eq(DisplayStatus.VISIBLE)=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../product/repository/ProductRepositoryCustomImpl.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustomImpl.kt b/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustomImpl.kt index 262f453..e4a17d4 100644 --- a/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustomImpl.kt +++ b/src/main/kotlin/org/store/clothstar/product/repository/ProductRepositoryCustomImpl.kt @@ -9,6 +9,7 @@ import org.springframework.data.support.PageableExecutionUtils import org.springframework.stereotype.Component import org.store.clothstar.product.domain.Product import org.store.clothstar.product.domain.QProduct.product +import org.store.clothstar.product.domain.type.DisplayStatus import java.util.function.LongSupplier @Component @@ -57,6 +58,7 @@ class ProductRepositoryCustomImpl( .from(product) .where( product.deletedAt.isNull() + .and(product.displayStatus.eq(DisplayStatus.VISIBLE)) .and(getSearchCondition(keyword)) ) .orderBy(*orderSpecifiers.toTypedArray()) // * -> 스프레드 연산자 : 배열의 각 요소를 개별 인자로 전달 @@ -84,6 +86,7 @@ class ProductRepositoryCustomImpl( .where( product.categoryId.eq(categoryId) .and(product.deletedAt.isNull()) + .and(product.displayStatus.eq(DisplayStatus.VISIBLE)) .and(getSearchCondition(keyword)) ) @@ -120,6 +123,7 @@ class ProductRepositoryCustomImpl( .where( product.categoryId.eq(categoryId) .and(product.deletedAt.isNull()) + .and(product.displayStatus.eq(DisplayStatus.VISIBLE)) .and(getSearchCondition(keyword)) ) .orderBy(*orderSpecifiers.toTypedArray()) From 94d560a369a2c33345b9cad92cdd7ce392bd72fb Mon Sep 17 00:00:00 2001 From: Ogu1208 Date: Sat, 17 Aug 2024 16:00:18 +0900 Subject: [PATCH 09/13] =?UTF-8?q?refactor:=20=EC=95=84=EC=9D=B4=ED=85=9C?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C=20=EC=98=A4=EB=A5=98=20=EB=A9=94=EC=8B=9C?= =?UTF-8?q?=EC=A7=80=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/org/store/clothstar/product/service/ItemService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/org/store/clothstar/product/service/ItemService.kt b/src/main/kotlin/org/store/clothstar/product/service/ItemService.kt index e63daf3..6fccbb6 100644 --- a/src/main/kotlin/org/store/clothstar/product/service/ItemService.kt +++ b/src/main/kotlin/org/store/clothstar/product/service/ItemService.kt @@ -96,6 +96,6 @@ class ItemService( fun getItemByIdAndProductId(itemId: Long, productId: Long): Item { return itemRepository.findByItemIdAndProduct_ProductId(itemId, productId) - ?: throw EntityNotFoundException("Item not found with id: $itemId and productId: $productId") + ?: throw EntityNotFoundException("productId: $productId, itemId: $itemId 인 아이템을 찾을 수 없습니다.") } } \ No newline at end of file From 6463af5c8e393fd10d71a26d6403f2407fc2ac38 Mon Sep 17 00:00:00 2001 From: Ogu1208 Date: Sat, 17 Aug 2024 16:01:15 +0900 Subject: [PATCH 10/13] =?UTF-8?q?refactor:=20=EC=9D=BC=EB=B0=98=20?= =?UTF-8?q?=EC=9C=A0=EC=A0=80=EB=8A=94=20displaystatus=3DVISIBLE=EC=9D=B8?= =?UTF-8?q?=20=EC=95=84=EC=9D=B4=ED=85=9C=EB=A7=8C=20=EC=A1=B0=ED=9A=8C,?= =?UTF-8?q?=20=ED=8C=90=EB=A7=A4=EC=9E=90=EB=8A=94=20=EC=A0=84=EB=B6=80=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../product/dto/response/ProductListResponse.kt | 4 ---- .../product/dto/response/ProductResponse.kt | 15 +++++++++++++-- .../clothstar/product/service/ProductService.kt | 2 +- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/org/store/clothstar/product/dto/response/ProductListResponse.kt b/src/main/kotlin/org/store/clothstar/product/dto/response/ProductListResponse.kt index c3330e5..4131928 100644 --- a/src/main/kotlin/org/store/clothstar/product/dto/response/ProductListResponse.kt +++ b/src/main/kotlin/org/store/clothstar/product/dto/response/ProductListResponse.kt @@ -25,9 +25,6 @@ class ProductListResponse( @Schema(description = "상품 판매량", example = "10") val saleCount: Long, // ~개 판매중 - @Schema(description = "상품 옵션") - val itemList: List, - @Schema(description = "판매자 정보") val seller: SellerSimpleResponse, ) { @@ -39,7 +36,6 @@ class ProductListResponse( price = product.price, saleCount = product.saleCount, content = product.content, - itemList = product.items.map { ItemResponse.from(it) }, saleStatus = product.saleStatus, seller = seller ) diff --git a/src/main/kotlin/org/store/clothstar/product/dto/response/ProductResponse.kt b/src/main/kotlin/org/store/clothstar/product/dto/response/ProductResponse.kt index 96beae7..88c93bf 100644 --- a/src/main/kotlin/org/store/clothstar/product/dto/response/ProductResponse.kt +++ b/src/main/kotlin/org/store/clothstar/product/dto/response/ProductResponse.kt @@ -39,7 +39,18 @@ class ProductResponse( val seller: SellerSimpleResponse ) { companion object { - fun from(product: Product, seller: SellerSimpleResponse): ProductResponse { + fun from(product: Product, + seller: SellerSimpleResponse, + isSeller: Boolean + ): ProductResponse { + + val items = if (isSeller) { + product.items.map { ItemResponse.from(it) } + } else { + product.items.filter { it.displayStatus == DisplayStatus.VISIBLE } + .map { ItemResponse.from(it) } + } + return ProductResponse( id = product.productId!!, name = product.name, @@ -50,7 +61,7 @@ class ProductResponse( displayStatus = product.displayStatus, saleStatus = product.saleStatus, productOptions = product.productOptions.map { ProductOptionResponse.from(it) }, - items = product.items.map { ItemResponse.from(it) }, + items = items, seller = seller ) } diff --git a/src/main/kotlin/org/store/clothstar/product/service/ProductService.kt b/src/main/kotlin/org/store/clothstar/product/service/ProductService.kt index 2e06537..d12f722 100644 --- a/src/main/kotlin/org/store/clothstar/product/service/ProductService.kt +++ b/src/main/kotlin/org/store/clothstar/product/service/ProductService.kt @@ -33,7 +33,7 @@ class ProductService( val seller = sellerService.getSellerById(product.memberId) val sellerSimpleResponse = SellerSimpleResponse.getSellerSimpleResponseBySeller(seller) - return ProductResponse.from(product, sellerSimpleResponse) + return ProductResponse.from(product, sellerSimpleResponse, false) } @Transactional From 93dc0a6352c0f530c7bccb775e424fdaaf9347ca Mon Sep 17 00:00:00 2001 From: Ogu1208 Date: Sat, 17 Aug 2024 16:08:48 +0900 Subject: [PATCH 11/13] =?UTF-8?q?feat:=20=ED=8C=90=EB=A7=A4=EC=9E=90=20?= =?UTF-8?q?=EC=83=81=ED=92=88=20=EC=83=81=EC=84=B8=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ProductSellerController.kt | 45 +++++++++++++++++++ .../service/ProductApplicationService.kt | 4 +- .../product/service/ProductService.kt | 4 +- 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/org/store/clothstar/product/controller/ProductSellerController.kt b/src/main/kotlin/org/store/clothstar/product/controller/ProductSellerController.kt index 372c18d..44c4bd5 100644 --- a/src/main/kotlin/org/store/clothstar/product/controller/ProductSellerController.kt +++ b/src/main/kotlin/org/store/clothstar/product/controller/ProductSellerController.kt @@ -42,6 +42,14 @@ class ProductSellerController( return ResponseEntity(messageDTO, HttpStatus.CREATED) } + // 상품 상세 조회 + @GetMapping("/{productId}") + @Operation(summary = "상품 상세 조회", description = "상품 ID를 사용하여 해당 상품의 상세 정보를 조회합니다.") + fun getProductDetails(@PathVariable productId: Long): ResponseEntity { + val productResponse = productApplicationService.getProductDetails(productId, true) + return ResponseEntity.ok(productResponse) + } + @PatchMapping("/{productId}/displayStatus") @Operation(summary = "상품 진열 상태 변경", description = "상품 ID를 사용하여 해당 상품의 진열 상태를 변경합니다.") fun updateProductDisplayStatus( @@ -89,4 +97,41 @@ class ProductSellerController( ) return ResponseEntity(messageDTO, HttpStatus.OK) } + + /* + // 상품 기본 정보 수정 + @PutMapping("/{productId}") + @Operation(summary = "상품 기본 정보 수정", description = "상품 ID를 사용하여 해당 상품의 기본 정보를 수정합니다.") + fun updateProduct( + @PathVariable productId: Long, + @RequestPart(value = "mainImage", required = false) mainImage: MultipartFile?, + @RequestPart(value = "subImages", required = false) subImages: List?, + @RequestPart(value = "dto") @Validated productCreateRequest: ProductCreateRequest + ): ResponseEntity { + productApplicationService.updateProduct(productId, mainImage, subImages, productCreateRequest) + + val messageDTO = MessageDTO( + HttpStatus.OK.value(), + "상품 기본 정보가 성공적으로 변경되었습니다." + ) + return ResponseEntity(messageDTO, HttpStatus.OK) + } + + // 상품 옵션&아이템 변경 + @PutMapping("/{productId}/options") + @Operation(summary = "상품 옵션&아이템 변경", description = "상품 ID를 사용하여 해당 상품의 옵션과 아이템을 변경합니다.") + fun updateProductOptions( + @PathVariable productId: Long, + @RequestBody productCreateRequest: ProductCreateRequest + ): ResponseEntity { + productApplicationService.updateProductOptions(productId, productCreateRequest) + + val messageDTO = MessageDTO( + HttpStatus.OK.value(), + "상품 옵션&아이템이 성공적으로 변경되었습니다." + ) + return ResponseEntity(messageDTO, HttpStatus.OK) + } + + */ } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/product/service/ProductApplicationService.kt b/src/main/kotlin/org/store/clothstar/product/service/ProductApplicationService.kt index 31804ba..e0f2597 100644 --- a/src/main/kotlin/org/store/clothstar/product/service/ProductApplicationService.kt +++ b/src/main/kotlin/org/store/clothstar/product/service/ProductApplicationService.kt @@ -73,8 +73,8 @@ class ProductApplicationService( } @Transactional(readOnly = true) - fun getProductDetails(productId: Long): ProductResponse { - return productService.getProductDetails(productId) + fun getProductDetails(productId: Long, isSeller: Boolean): ProductResponse { + return productService.getProductDetails(productId, isSeller) } @Transactional(readOnly = true) diff --git a/src/main/kotlin/org/store/clothstar/product/service/ProductService.kt b/src/main/kotlin/org/store/clothstar/product/service/ProductService.kt index d12f722..addd797 100644 --- a/src/main/kotlin/org/store/clothstar/product/service/ProductService.kt +++ b/src/main/kotlin/org/store/clothstar/product/service/ProductService.kt @@ -27,13 +27,13 @@ class ProductService( } @Transactional(readOnly = true) - fun getProductDetails(productId: Long): ProductResponse { + fun getProductDetails(productId: Long, isSeller: Boolean): ProductResponse { val product = productRepository.findByIdOrNull(productId) ?: throw EntityNotFoundException("상품을 찾을 수 없습니다.") val seller = sellerService.getSellerById(product.memberId) val sellerSimpleResponse = SellerSimpleResponse.getSellerSimpleResponseBySeller(seller) - return ProductResponse.from(product, sellerSimpleResponse, false) + return ProductResponse.from(product, sellerSimpleResponse, isSeller) } @Transactional From 099afcc8b8a0498ed90384b7fef594d7c200ce0f Mon Sep 17 00:00:00 2001 From: Ogu1208 Date: Sat, 17 Aug 2024 16:20:20 +0900 Subject: [PATCH 12/13] =?UTF-8?q?refactor:=20=EA=B8=B0=EC=A1=B4=20product?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20Seller=20=EC=97=94=EB=93=9C=ED=8F=AC?= =?UTF-8?q?=EC=9D=B8=ED=8A=B8=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../product/controller/ProductController.kt | 22 +---- .../controller/ProductSellerController.kt | 13 +-- .../service/ProductApplicationService.kt | 74 --------------- .../ProductSellerApplicationService.kt | 94 +++++++++++++++++++ 4 files changed, 102 insertions(+), 101 deletions(-) create mode 100644 src/main/kotlin/org/store/clothstar/product/service/ProductSellerApplicationService.kt diff --git a/src/main/kotlin/org/store/clothstar/product/controller/ProductController.kt b/src/main/kotlin/org/store/clothstar/product/controller/ProductController.kt index 0f583aa..e654fe4 100644 --- a/src/main/kotlin/org/store/clothstar/product/controller/ProductController.kt +++ b/src/main/kotlin/org/store/clothstar/product/controller/ProductController.kt @@ -23,26 +23,6 @@ import org.store.clothstar.product.service.ProductApplicationService private class ProductController( private val productApplicationService: ProductApplicationService, ) { - @PostMapping - @Operation( - summary = "상품 등록", - description = "카테고리 아이디, 상품 이름, 내용, 가격, 상태를 입력하여 상품을 신규 등록한다." - ) - fun createProduct( - @RequestPart(value = "mainImage", required = false) mainImage: MultipartFile, - @RequestPart(value = "subImages", required = false) subImages: List?, - @RequestPart(value = "dto") @Validated productCreateRequest: ProductCreateRequest - ): ResponseEntity { - // 상품 등록 - productApplicationService.createProduct(mainImage, subImages, productCreateRequest); - - val messageDTO = MessageDTO( - HttpStatus.CREATED.value(), - "상품 생성이 정상적으로 처리됐습니다." - ) - - return ResponseEntity(messageDTO, HttpStatus.CREATED) - } // 상품 전체 Offset 페이징 조회 @GetMapping("/offset") @@ -69,7 +49,7 @@ private class ProductController( @GetMapping("/{productId}") @Operation(summary = "상품 상세 조회", description = "상품 ID를 사용하여 특정 상품의 상세 정보를 조회한다.") fun getProductDetails(@PathVariable productId: Long): ResponseEntity { - val productResponse = productApplicationService.getProductDetails(productId) + val productResponse = productApplicationService.getProductDetails(productId, false) return ResponseEntity(productResponse, HttpStatus.OK) } } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/product/controller/ProductSellerController.kt b/src/main/kotlin/org/store/clothstar/product/controller/ProductSellerController.kt index 44c4bd5..73b51b6 100644 --- a/src/main/kotlin/org/store/clothstar/product/controller/ProductSellerController.kt +++ b/src/main/kotlin/org/store/clothstar/product/controller/ProductSellerController.kt @@ -13,12 +13,13 @@ import org.store.clothstar.product.dto.request.UpdateDisplayStatusRequest import org.store.clothstar.product.dto.request.UpdateStockRequest import org.store.clothstar.product.dto.response.ProductResponse import org.store.clothstar.product.service.ProductApplicationService +import org.store.clothstar.product.service.ProductSellerApplicationService @Tag(name = "ProductSellers", description = "ProductSellers(판매자) 관련 API 입니다.") @RestController @RequestMapping("/v3/sellers/products") class ProductSellerController( - private val productApplicationService: ProductApplicationService + private val productSellerApplicationService: ProductSellerApplicationService ) { // 판매자 상품 등록 @PostMapping @@ -32,7 +33,7 @@ class ProductSellerController( @RequestPart(value = "dto") @Validated productCreateRequest: ProductCreateRequest ): ResponseEntity { // 상품 등록 - productApplicationService.createProduct(mainImage, subImages, productCreateRequest); + productSellerApplicationService.createProduct(mainImage, subImages, productCreateRequest); val messageDTO = MessageDTO( HttpStatus.CREATED.value(), @@ -46,7 +47,7 @@ class ProductSellerController( @GetMapping("/{productId}") @Operation(summary = "상품 상세 조회", description = "상품 ID를 사용하여 해당 상품의 상세 정보를 조회합니다.") fun getProductDetails(@PathVariable productId: Long): ResponseEntity { - val productResponse = productApplicationService.getProductDetails(productId, true) + val productResponse = productSellerApplicationService.getProductDetails(productId, true) return ResponseEntity.ok(productResponse) } @@ -56,7 +57,7 @@ class ProductSellerController( @PathVariable productId: Long, @RequestBody request: UpdateDisplayStatusRequest ): ResponseEntity { - productApplicationService.updateProductDisplayStatus(productId, request.displayStatus) + productSellerApplicationService.updateProductDisplayStatus(productId, request.displayStatus) val messageDTO = MessageDTO( HttpStatus.OK.value(), @@ -72,7 +73,7 @@ class ProductSellerController( @PathVariable itemId: Long, @RequestBody request: UpdateDisplayStatusRequest ): ResponseEntity { - productApplicationService.updateItemDisplayStatus(productId, itemId, request.displayStatus) + productSellerApplicationService.updateItemDisplayStatus(productId, itemId, request.displayStatus) val messageDTO = MessageDTO( HttpStatus.OK.value(), @@ -89,7 +90,7 @@ class ProductSellerController( @PathVariable itemId: Long, @RequestBody request: UpdateStockRequest ): ResponseEntity { - productApplicationService.updateItemStock(productId, itemId, request.stock) + productSellerApplicationService.updateItemStock(productId, itemId, request.stock) val messageDTO = MessageDTO( HttpStatus.OK.value(), diff --git a/src/main/kotlin/org/store/clothstar/product/service/ProductApplicationService.kt b/src/main/kotlin/org/store/clothstar/product/service/ProductApplicationService.kt index e0f2597..f75172a 100644 --- a/src/main/kotlin/org/store/clothstar/product/service/ProductApplicationService.kt +++ b/src/main/kotlin/org/store/clothstar/product/service/ProductApplicationService.kt @@ -3,16 +3,9 @@ package org.store.clothstar.product.service import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.data.domain.Slice -import org.springframework.security.core.context.SecurityContextHolder import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional -import org.springframework.web.multipart.MultipartFile -import org.store.clothstar.member.authentication.domain.CustomUserDetails import org.store.clothstar.member.service.MemberService -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.ProductListResponse import org.store.clothstar.product.dto.response.ProductResponse @@ -24,53 +17,6 @@ class ProductApplicationService( private val itemService: ItemService, private val memberService: MemberService ) { - @Transactional - fun createProduct( - mainImage: MultipartFile, - subImages: List?, - productCreateRequest: ProductCreateRequest - ) { - val member = SecurityContextHolder.getContext().authentication.principal as CustomUserDetails - -// if(memberService.isSeller(member.id)) { -// throw IllegalArgumentException("판매자만 상품을 등록할 수 있습니다.") -// } - - // 1. 상품 생성 - val product = productCreateRequest.toProductEntity() - - // 상품 저장 - val savedProduct = productService.createProduct(product) - - // S3에 이미지 업로드 및 URL 생성 - // 2. 메인 이미지 업로드 및 저장 -// val mainImageUrl = s3Service.uploadFile(mainImage) -// val mainImageUrl = s3Service.uploadFile(mainImage) - val mainImageUrl = - "https://on.com2us.com/wp-content/uploads/2023/12/VxdEKDNZCp9hAW5TU5-3MZTePLGSdlYKzEZUyVLDB-Cyo950Ee19yaOL8ayxgJzEfMYfzfLcRYuwkmKEs2cg0w.webp" - product.imageList.add(ProductImage(mainImageUrl, mainImageUrl, ImageType.MAIN)) - - // 3. 서브 이미지 업로드 및® 저장 - subImages?.forEach { subImage -> -// val subImageUrl = s3Service.uploadFile(subImage) - val subImageUrl = - "https://on.com2us.com/wp-content/uploads/2023/12/%EC%98%A4%EA%B5%AC%EC%99%80-%EB%B9%84%EB%B0%80%EC%9D%98%EC%88%B2-002.jpg" - product.imageList.add(ProductImage(subImageUrl, subImageUrl, ImageType.SUB)) - } - - - // 4. 상품 옵션 생성 - val productOptions = productCreateRequest.productOptions.map { optionRequest -> - val productOption = productOptionService.createProductOption(product, optionRequest) - product.productOptions.add(productOption) - } - - // 5. 상품 아이템 생성 - productCreateRequest.items.forEach { itemRequest -> - val item = itemService.createItem(product, itemRequest) - product.items.add(item) - } - } @Transactional(readOnly = true) fun getProductDetails(productId: Long, isSeller: Boolean): ProductResponse { @@ -86,24 +32,4 @@ class ProductApplicationService( fun getAllProductsSlicePaging(pageable: Pageable, keyword: String?): Slice { return productService.getAllProductsSlicePaging(pageable, keyword) } - - @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) - } - - @Transactional - fun updateItemStock(productId: Long, itemId: Long, stock: Int) { -// val Member = getPrin - - val item = itemService.getItemByIdAndProductId(itemId, productId) - item.updateStock(stock) - } } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/product/service/ProductSellerApplicationService.kt b/src/main/kotlin/org/store/clothstar/product/service/ProductSellerApplicationService.kt new file mode 100644 index 0000000..410f4bf --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/product/service/ProductSellerApplicationService.kt @@ -0,0 +1,94 @@ +package org.store.clothstar.product.service + +import org.springframework.security.core.context.SecurityContextHolder +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import org.springframework.web.multipart.MultipartFile +import org.store.clothstar.member.authentication.domain.CustomUserDetails +import org.store.clothstar.member.service.MemberService +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 + +@Service +class ProductSellerApplicationService ( + private val productService: ProductService, + private val productOptionService: ProductOptionService, + private val itemService: ItemService, + private val memberService: MemberService +) { + @Transactional + fun createProduct( + mainImage: MultipartFile, + subImages: List?, + productCreateRequest: ProductCreateRequest + ) { + val member = SecurityContextHolder.getContext().authentication.principal as CustomUserDetails + +// if(memberService.isSeller(member.id)) { +// throw IllegalArgumentException("판매자만 상품을 등록할 수 있습니다.") +// } + + // 1. 상품 생성 + val product = productCreateRequest.toProductEntity() + + // 상품 저장 + val savedProduct = productService.createProduct(product) + + // S3에 이미지 업로드 및 URL 생성 + // 2. 메인 이미지 업로드 및 저장 +// val mainImageUrl = s3Service.uploadFile(mainImage) +// val mainImageUrl = s3Service.uploadFile(mainImage) + val mainImageUrl = + "https://on.com2us.com/wp-content/uploads/2023/12/VxdEKDNZCp9hAW5TU5-3MZTePLGSdlYKzEZUyVLDB-Cyo950Ee19yaOL8ayxgJzEfMYfzfLcRYuwkmKEs2cg0w.webp" + product.imageList.add(ProductImage(mainImageUrl, mainImageUrl, ImageType.MAIN)) + + // 3. 서브 이미지 업로드 및® 저장 + subImages?.forEach { subImage -> +// val subImageUrl = s3Service.uploadFile(subImage) + val subImageUrl = + "https://on.com2us.com/wp-content/uploads/2023/12/%EC%98%A4%EA%B5%AC%EC%99%80-%EB%B9%84%EB%B0%80%EC%9D%98%EC%88%B2-002.jpg" + product.imageList.add(ProductImage(subImageUrl, subImageUrl, ImageType.SUB)) + } + + + // 4. 상품 옵션 생성 + val productOptions = productCreateRequest.productOptions.map { optionRequest -> + val productOption = productOptionService.createProductOption(product, optionRequest) + product.productOptions.add(productOption) + } + + // 5. 상품 아이템 생성 + productCreateRequest.items.forEach { itemRequest -> + val item = itemService.createItem(product, itemRequest) + product.items.add(item) + } + } + + @Transactional(readOnly = true) + fun getProductDetails(productId: Long, isSeller: Boolean): ProductResponse { + return productService.getProductDetails(productId, isSeller) + } + + @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) + } + + @Transactional + fun updateItemStock(productId: Long, itemId: Long, stock: Int) { +// val Member = getPrin + + val item = itemService.getItemByIdAndProductId(itemId, productId) + item.updateStock(stock) + } +} \ No newline at end of file From 1a3506d6f0dec663b4baa24e08cffb78bd24cb41 Mon Sep 17 00:00:00 2001 From: Ogu1208 Date: Sat, 17 Aug 2024 16:29:41 +0900 Subject: [PATCH 13/13] =?UTF-8?q?test:=20Seller=20Servie=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC=EC=97=90=20=EB=94=B0=EB=A5=B8=20productTest=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../product/service/ProductApplicationServiceTest.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/test/kotlin/org/store/clothstar/product/service/ProductApplicationServiceTest.kt b/src/test/kotlin/org/store/clothstar/product/service/ProductApplicationServiceTest.kt index e2d01fa..b807b5e 100644 --- a/src/test/kotlin/org/store/clothstar/product/service/ProductApplicationServiceTest.kt +++ b/src/test/kotlin/org/store/clothstar/product/service/ProductApplicationServiceTest.kt @@ -33,6 +33,9 @@ class ProductApplicationServiceTest { @InjectMocks private lateinit var productApplicationService: ProductApplicationService + @InjectMocks + private lateinit var productSellerApplicationService: ProductSellerApplicationService + @Disabled @DisplayName("유효한 ProductCreateRequest가 들어오면 상품 생성에 성공한다.") @Test @@ -73,7 +76,7 @@ class ProductApplicationServiceTest { given(productService.getProductById(productId)).willReturn(mockProduct) // when - productApplicationService.updateProductDisplayStatus(productId, displayStatus) + productSellerApplicationService.updateProductDisplayStatus(productId, displayStatus) // then verify(productService).getProductById(productId) @@ -92,7 +95,7 @@ class ProductApplicationServiceTest { given(itemService.getItemByIdAndProductId(itemId, productId)).willReturn(mockItem) // when - productApplicationService.updateItemDisplayStatus(productId, itemId, displayStatus) + productSellerApplicationService.updateItemDisplayStatus(productId, itemId, displayStatus) // then verify(itemService).getItemByIdAndProductId(itemId, productId)