diff --git a/src/main/java/com/example/repick/domain/clothingSales/api/ClothingSalesController.java b/src/main/java/com/example/repick/domain/clothingSales/api/ClothingSalesController.java index 61084c24..af475008 100644 --- a/src/main/java/com/example/repick/domain/clothingSales/api/ClothingSalesController.java +++ b/src/main/java/com/example/repick/domain/clothingSales/api/ClothingSalesController.java @@ -4,6 +4,7 @@ import com.example.repick.domain.clothingSales.service.BagService; import com.example.repick.domain.clothingSales.service.BoxService; import com.example.repick.domain.clothingSales.service.ClothingSalesService; +import com.example.repick.domain.clothingSales.dto.GetSalesProductsByClothingSales; import com.example.repick.global.page.DateCondition; import com.example.repick.global.page.PageCondition; import com.example.repick.global.page.PageResponse; @@ -69,8 +70,23 @@ public SuccessResponse> getPendingClothingSales() 옷장 정리 통합 조회: 판매 중인 옷장, 신청 완료일 내림차순으로 정렬되어 리스트로 반환합니다. """) @GetMapping("/selling") - public SuccessResponse> getSellingClothingSales() { - return SuccessResponse.success(clothingSalesService.getSellingClothingSales()); + public SuccessResponse> getSellingClothingSalesList() { + return SuccessResponse.success(clothingSalesService.getSellingClothingSalesList()); + } + + @Operation(summary = "옷장 개별 보기: 옷장 정보") + @GetMapping("/selling/{clothingSalesId}") + public SuccessResponse getSellingClothingSales(@PathVariable Long clothingSalesId) { + return SuccessResponse.success(clothingSalesService.getSellingClothingSales(clothingSalesId)); + } + + @Operation(summary = "옷장 개별 보기: 옷장 상품 현황", + description = """ + productState: selling, confirm-pending, sold-out, selling-end + """) + @GetMapping("/products/{clothingSalesId}/{productState}") + public SuccessResponse> getProductsByClothingSales(@PathVariable Long clothingSalesId, @PathVariable String productState) { + return SuccessResponse.success(clothingSalesService.getProductsByClothingSales(clothingSalesId, productState)); } @Operation(summary = "수거: 상품 보기", description = """ diff --git a/src/main/java/com/example/repick/domain/clothingSales/dto/GetSalesProductsByClothingSales.java b/src/main/java/com/example/repick/domain/clothingSales/dto/GetSalesProductsByClothingSales.java new file mode 100644 index 00000000..c1a3c5ff --- /dev/null +++ b/src/main/java/com/example/repick/domain/clothingSales/dto/GetSalesProductsByClothingSales.java @@ -0,0 +1,24 @@ +package com.example.repick.domain.clothingSales.dto; + +import com.example.repick.domain.product.entity.Product; +import io.swagger.v3.oas.annotations.media.Schema; + +public record GetSalesProductsByClothingSales( + @Schema(description = "상품 ID") Long productId, + @Schema(description = "상품 브랜드") String productBrand, + @Schema(description = "상품명") String productName, + @Schema(description = "상품 이미지") String productImageUrl, + @Schema(description = "상품 가격") Long productPrice, + @Schema(description = "정산금") Long settlementAmount +) { + public static GetSalesProductsByClothingSales from(Product product) { + return new GetSalesProductsByClothingSales( + product.getId(), + product.getBrandName(), + product.getProductName(), + product.getThumbnailImageUrl(), + product.getPrice(), + product.getSettlement() + ); + } +} diff --git a/src/main/java/com/example/repick/domain/clothingSales/service/ClothingSalesService.java b/src/main/java/com/example/repick/domain/clothingSales/service/ClothingSalesService.java index a821c6dd..f72d1dd3 100644 --- a/src/main/java/com/example/repick/domain/clothingSales/service/ClothingSalesService.java +++ b/src/main/java/com/example/repick/domain/clothingSales/service/ClothingSalesService.java @@ -7,6 +7,7 @@ import com.example.repick.domain.clothingSales.repository.ClothingSalesRepository; import com.example.repick.domain.clothingSales.repository.ClothingSalesStateRepository; import com.example.repick.domain.clothingSales.validator.ClothingSalesValidator; +import com.example.repick.domain.clothingSales.dto.GetSalesProductsByClothingSales; import com.example.repick.domain.product.entity.Product; import com.example.repick.domain.product.entity.ProductOrder; import com.example.repick.domain.product.entity.ProductOrderState; @@ -106,7 +107,7 @@ public Boolean updateProductPrice(List postProductPriceList) { return true; } - public List getSellingClothingSales() { + public List getSellingClothingSalesList() { User user = userRepository.findByProviderId(SecurityContextHolder.getContext().getAuthentication().getName()) .orElseThrow(() -> new CustomException(USER_NOT_FOUND)); @@ -114,33 +115,77 @@ public List getSellingClothingSales() { List clothingSalesList = clothingSalesRepository.findByUserAndClothingSalesState(user, ClothingSalesStateType.SELLING); for (ClothingSales clothingSales : clothingSalesList) { - List productList = clothingSales.getProductList(); - if(productList.isEmpty()) { - continue; - } - int remainingSalesDays = (int) ChronoUnit.DAYS.between(LocalDate.now(), clothingSales.getSalesStartDate().toLocalDate().plusDays(90)); - int sellingQuantity = 0; - int pendingQuantity = 0; - int soldQuantity = 0; - for (Product product : productList) { - if (product.getProductState().equals(ProductStateType.SELLING)) { - sellingQuantity++; - } else if (product.getProductState().equals(ProductStateType.SOLD_OUT)) { - ProductOrder productOrder = productOrderRepository.findFirstByProductIdOrderByCreatedDateDesc(product.getId()) - .orElseThrow(() -> new CustomException(INVALID_PRODUCT_ID)); - if (productOrder.isConfirmed()) { - soldQuantity++; - } else { - pendingQuantity++; - } + sellingClothingSalesList.add(getSellingClothingSalesInfo(clothingSales)); + } + return sellingClothingSalesList; + } + + public GetSellingClothingSales getSellingClothingSales(Long clothingSalesId) { + User user = userRepository.findByProviderId(SecurityContextHolder.getContext().getAuthentication().getName()) + .orElseThrow(() -> new CustomException(USER_NOT_FOUND)); + + ClothingSales clothingSales = clothingSalesRepository.findById(clothingSalesId) + .orElseThrow(() -> new CustomException(INVALID_CLOTHING_SALES_ID)); + + if(!clothingSales.getUser().getId().equals(user.getId())){ + throw new CustomException(INVALID_CLOTHING_SALES_ID); + } + + return getSellingClothingSalesInfo(clothingSales); + } + + + private GetSellingClothingSales getSellingClothingSalesInfo(ClothingSales clothingSales) { + List productList = clothingSales.getProductList(); + if(productList.isEmpty()) { + return GetSellingClothingSales.of(clothingSales, clothingSales.getSalesStartDate().format(DateTimeFormatter.ofPattern("yyyy.MM.dd")), 0, 0, 0, 0); + } + + int remainingSalesDays = (int) ChronoUnit.DAYS.between(LocalDate.now(), clothingSales.getSalesStartDate().toLocalDate().plusDays(90)); + int sellingQuantity = 0; + int pendingQuantity = 0; + int soldQuantity = 0; + + for (Product product : productList) { + if (product.getProductState().equals(ProductStateType.SELLING)) { + sellingQuantity++; + } else if (product.getProductState().equals(ProductStateType.SOLD_OUT)) { + ProductOrder productOrder = productOrderRepository.findFirstByProductIdOrderByCreatedDateDesc(product.getId()) + .orElseThrow(() -> new CustomException(INVALID_PRODUCT_ID)); + if (productOrder.isConfirmed()) { + soldQuantity++; + } else { + pendingQuantity++; } } - sellingClothingSalesList.add(GetSellingClothingSales.of(clothingSales, clothingSales.getSalesStartDate().format(DateTimeFormatter.ofPattern("yyyy.MM.dd")), remainingSalesDays, sellingQuantity, pendingQuantity, soldQuantity)); } - return sellingClothingSalesList; + return GetSellingClothingSales.of(clothingSales, clothingSales.getSalesStartDate().format(DateTimeFormatter.ofPattern("yyyy.MM.dd")), remainingSalesDays, sellingQuantity, pendingQuantity, soldQuantity); + } + + @Transactional(readOnly = true) + public List getProductsByClothingSales(Long clothingSalesId, String productState) { + if (productState.equals("confirm-pending") || productState.equals("sold-out")) { + List products = productRepository.findByClothingSalesIdAndProductState(clothingSalesId, ProductStateType.SOLD_OUT); + boolean isConfirmed = productState.equals("sold-out"); + return products.stream() + .filter(product -> { + ProductOrder productOrder = productOrderRepository.findFirstByProductIdOrderByCreatedDateDesc(product.getId()) + .orElseThrow(() -> new CustomException(PRODUCT_ORDER_NOT_FOUND)); + return productOrder.isConfirmed() == isConfirmed; + }) + .map(GetSalesProductsByClothingSales::from) + .collect(Collectors.toList()); + } else { + ProductStateType productStateType = ProductStateType.fromEngValue(productState); + List products = productRepository.findByClothingSalesIdAndProductState(clothingSalesId, productStateType); + return products.stream() + .map(GetSalesProductsByClothingSales::from) + .collect(Collectors.toList()); + } } + public GetProductListByClothingSales getProductsByClothingSalesId(Long clothingSalesId) { User user = userRepository.findByProviderId(SecurityContextHolder.getContext().getAuthentication().getName()) .orElseThrow(() -> new CustomException(USER_NOT_FOUND)); diff --git a/src/main/java/com/example/repick/domain/product/api/ProductController.java b/src/main/java/com/example/repick/domain/product/api/ProductController.java index 9c338e91..8de8cce6 100644 --- a/src/main/java/com/example/repick/domain/product/api/ProductController.java +++ b/src/main/java/com/example/repick/domain/product/api/ProductController.java @@ -241,7 +241,7 @@ public SuccessResponse>> getProd return SuccessResponse.success(productService.getProductCountByClothingSales(type, userId, pageCondition)); } - @Operation(summary = "유저 상품 현황", + @Operation(summary = "유저 상품 현황 (Admin API)", description = """ productState: selling, sold-out, rejected, selling-end, kg-sell diff --git a/src/main/java/com/example/repick/domain/product/repository/ProductRepository.java b/src/main/java/com/example/repick/domain/product/repository/ProductRepository.java index 5da1001c..ef50d401 100644 --- a/src/main/java/com/example/repick/domain/product/repository/ProductRepository.java +++ b/src/main/java/com/example/repick/domain/product/repository/ProductRepository.java @@ -1,6 +1,7 @@ package com.example.repick.domain.product.repository; import com.example.repick.domain.product.entity.Product; +import com.example.repick.domain.product.entity.ProductStateType; import jakarta.persistence.LockModeType; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Lock; @@ -9,11 +10,9 @@ import java.util.List; public interface ProductRepository extends JpaRepository, ProductRepositoryCustom { - - List findProductByUserIdAndClothingSalesCount(Long userId, Integer clothingSalesCount); - Integer countByUserIdAndClothingSalesCount(Long userId, Integer clothingSalesCount); - @Lock(LockModeType.PESSIMISTIC_WRITE) @Query("SELECT p FROM Product p WHERE p.id IN :ids") List findAllByIdWithLock(List ids); + + List findByClothingSalesIdAndProductState(Long clothingSalesId, ProductStateType productStateType); } diff --git a/src/main/java/com/example/repick/domain/product/service/ProductService.java b/src/main/java/com/example/repick/domain/product/service/ProductService.java index 5b836e17..81748644 100644 --- a/src/main/java/com/example/repick/domain/product/service/ProductService.java +++ b/src/main/java/com/example/repick/domain/product/service/ProductService.java @@ -482,6 +482,7 @@ public PageResponse> getProductCountByClothin return PageResponse.of(pages.getContent(), pages.getTotalPages(), pages.getTotalElements()); } + @Transactional(readOnly = true) public PageResponse> getProductsByUserClothingSales(Long clothingSalesId, String productState, Boolean isExpired, PageCondition pageCondition) { Page pages; if (productState.equals("kg-sell")) { // kg 매입 상품(리젝, 만료되었을 경우 kg 매입 가능)