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 17ed6f33..be5694c5 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 @@ -70,14 +70,16 @@ public SuccessResponse> getMainPageRecommendation( return SuccessResponse.success(productService.getMainPageRecommendation(gender, cursorId, pageSize)); } - @Operation(summary = "최신순 조회", - description = """ - 최신순으로 상품 리스트 페이지를 조회합니다. 무한스크롤 방식을 사용합니다. + @Operation(summary = "상품 조회", + description = """ + 최신순, 가격 낮은순, 가격 높은순, 할인율 높은순으로 상품 리스트 페이지를 조회합니다. + 무한스크롤 방식을 사용합니다. 각각의 파라미터는 옵셔널입니다. """) - @GetMapping("/latest") - public SuccessResponse> getLatestProduct( + @GetMapping("/{type}") + public SuccessResponse> getProducts( + @Parameter(description = "조회 타입 (latest, lowest-price, highest-price, highest-discount)") @PathVariable String type, @Parameter(description = "조회 의류 성별") @RequestParam(required = false) String gender, @Parameter(description = "카테고리") @RequestParam(required = false) String category, @Parameter(description = "스타일") @RequestParam(required = false) List styles, @@ -88,74 +90,8 @@ public SuccessResponse> getLatestProduct( @Parameter(description = "사이즈") @RequestParam(required = false) List sizes, @Parameter(description = "1번째 페이지 조회시 null, " + "2번째 이상 페이지 조회시 직전 페이지의 마지막 episode id") @RequestParam(required = false) Long cursorId, - @Parameter(description = "한 페이지에 가져올 에피소드 개수, 기본값 4") @RequestParam(required = false) Integer pageSize) { - return SuccessResponse.success(productService.getLatest(gender, category, styles, minPrice, maxPrice, brandNames, qualityRates, sizes, cursorId, pageSize)); - } - - @Operation(summary = "가격 낮은순 조회", - description = """ - 가격 낮은순으로 상품 리스트 페이지를 조회합니다. 무한스크롤 방식을 사용합니다. - - 각각의 파라미터는 옵셔널입니다. - """) - @GetMapping("/lowest-price") - public SuccessResponse> getLowestProduct( - @Parameter(description = "조회 의류 성별") @RequestParam(required = false) String gender, - @Parameter(description = "카테고리") @RequestParam(required = false) String category, - @Parameter(description = "스타일") @RequestParam(required = false) List styles, - @Parameter(description = "최소 가격") @RequestParam(required = false) Long minPrice, - @Parameter(description = "최대 가격") @RequestParam(required = false) Long maxPrice, - @Parameter(description = "브랜드") @RequestParam(required = false) List brandNames, - @Parameter(description = "상품등급") @RequestParam(required = false) List qualityRates, - @Parameter(description = "사이즈") @RequestParam(required = false) List sizes, - @Parameter(description = "1번째 페이지 조회시 null, " + - "2번째 이상 페이지 조회시 직전 페이지의 마지막 episode id") @RequestParam(required = false) Long cursorId, - @Parameter(description = "한 페이지에 가져올 에피소드 개수, 기본값 4") @RequestParam(required = false) Integer pageSize) { - return SuccessResponse.success(productService.getLowest(gender, category, styles, minPrice, maxPrice, brandNames, qualityRates, sizes, cursorId, pageSize)); - } - - @Operation(summary = "가격 높은순 조회", - description = """ - 가격 높은순으로 상품 리스트 페이지를 조회합니다. 무한스크롤 방식을 사용합니다. - - 각각의 파라미터는 옵셔널입니다. - """) - @GetMapping("/highest-price") - public SuccessResponse> getHighestProduct( - @Parameter(description = "조회 의류 성별") @RequestParam(required = false) String gender, - @Parameter(description = "카테고리") @RequestParam(required = false) String category, - @Parameter(description = "스타일") @RequestParam(required = false) List styles, - @Parameter(description = "최소 가격") @RequestParam(required = false) Long minPrice, - @Parameter(description = "최대 가격") @RequestParam(required = false) Long maxPrice, - @Parameter(description = "브랜드") @RequestParam(required = false) List brandNames, - @Parameter(description = "상품등급") @RequestParam(required = false) List qualityRates, - @Parameter(description = "사이즈") @RequestParam(required = false) List sizes, - @Parameter(description = "1번째 페이지 조회시 null, " + - "2번째 이상 페이지 조회시 직전 페이지의 마지막 episode id") @RequestParam(required = false) Long cursorId, - @Parameter(description = "한 페이지에 가져올 에피소드 개수, 기본값 4") @RequestParam(required = false) Integer pageSize) { - return SuccessResponse.success(productService.getHighest(gender, category, styles, minPrice, maxPrice, brandNames, qualityRates, sizes, cursorId, pageSize)); - } - - @Operation(summary = "할인율 높은순 조회", - description = """ - 할인율 높은순으로 상품 리스트 페이지를 조회합니다. 무한스크롤 방식을 사용합니다. - - 각각의 파라미터는 옵셔널입니다. - """) - @GetMapping("/highest-discount") - public SuccessResponse> getHighestDiscountProduct( - @Parameter(description = "조회 의류 성별") @RequestParam(required = false) String gender, - @Parameter(description = "카테고리") @RequestParam(required = false) String category, - @Parameter(description = "스타일") @RequestParam(required = false) List styles, - @Parameter(description = "최소 가격") @RequestParam(required = false) Long minPrice, - @Parameter(description = "최대 가격") @RequestParam(required = false) Long maxPrice, - @Parameter(description = "브랜드") @RequestParam(required = false) List brandNames, - @Parameter(description = "상품등급") @RequestParam(required = false) List qualityRates, - @Parameter(description = "사이즈") @RequestParam(required = false) List sizes, - @Parameter(description = "1번째 페이지 조회시 null, " + - "2번째 이상 페이지 조회시 직전 페이지의 마지막 episode id") @RequestParam(required = false) Long cursorId, - @Parameter(description = "한 페이지에 가져올 에피소드 개수, 기본값 4") @RequestParam(required = false) Integer pageSize) { - return SuccessResponse.success(productService.getHighestDiscount(gender, category, styles, minPrice, maxPrice, brandNames, qualityRates, sizes, cursorId, pageSize)); + @Parameter(description = "한 페이지에 가져올 에피소드 개수, 기본값 4") @RequestParam(required = false) Integer pageSize){ + return SuccessResponse.success(productService.getProducts(type, gender, category, styles, minPrice, maxPrice, brandNames, qualityRates, sizes, cursorId, pageSize)); } // TODO: !!ADMIN ACCESS REQUIRED!! diff --git a/src/main/java/com/example/repick/domain/product/dto/PostPayment.java b/src/main/java/com/example/repick/domain/product/dto/PostPayment.java index 8e1c2ead..c47deadb 100644 --- a/src/main/java/com/example/repick/domain/product/dto/PostPayment.java +++ b/src/main/java/com/example/repick/domain/product/dto/PostPayment.java @@ -8,6 +8,5 @@ public record PostPayment( @Schema(description = "결제 고유번호") String iamportUid, @Schema(description = "주문 아이디") String merchantUid, - @Schema(description = "결제 금액") Long paymentAmount, Address address) { } diff --git a/src/main/java/com/example/repick/domain/product/repository/ProductRepositoryCustom.java b/src/main/java/com/example/repick/domain/product/repository/ProductRepositoryCustom.java index 6d68dea1..49ad80ba 100644 --- a/src/main/java/com/example/repick/domain/product/repository/ProductRepositoryCustom.java +++ b/src/main/java/com/example/repick/domain/product/repository/ProductRepositoryCustom.java @@ -20,8 +20,7 @@ List findLatestProducts( List sizes, Long cursorId, Integer pageSize, - Long userId, - ProductSellingStateType productSellingStateType); + Long userId); List findLowestProducts( String gender, @@ -34,8 +33,7 @@ List findLowestProducts( List sizes, Long cursorId, Integer pageSize, - Long userId, - ProductSellingStateType productSellingStateType); + Long userId); List findHighestProducts( String gender, @@ -48,8 +46,7 @@ List findHighestProducts( List sizes, Long cursorId, Integer pageSize, - Long userId, - ProductSellingStateType productSellingStateType); + Long userId); List findHighestDiscountProducts( String gender, @@ -62,8 +59,7 @@ List findHighestDiscountProducts( List sizes, Long cursorId, Integer pageSize, - Long userId, - ProductSellingStateType productSellingStateType); + Long userId); List findMainPageRecommendation(Long cursorId, Integer pageSize, Long userId, String gender, ProductSellingStateType productSellingStateType); diff --git a/src/main/java/com/example/repick/domain/product/repository/ProductRepositoryImpl.java b/src/main/java/com/example/repick/domain/product/repository/ProductRepositoryImpl.java index 58830927..575f3ecd 100644 --- a/src/main/java/com/example/repick/domain/product/repository/ProductRepositoryImpl.java +++ b/src/main/java/com/example/repick/domain/product/repository/ProductRepositoryImpl.java @@ -7,6 +7,7 @@ import com.example.repick.domain.product.entity.Gender; import com.example.repick.domain.product.entity.ProductSellingStateType; import com.example.repick.domain.product.entity.Style; +import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.Projections; import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.core.types.dsl.Expressions; @@ -29,8 +30,7 @@ public class ProductRepositoryImpl implements ProductRepositoryCustom { private final JPAQueryFactory jpaQueryFactory; - @Override - public List findLatestProducts( + private List findProducts( String gender, String category, List styles, @@ -42,7 +42,7 @@ public List findLatestProducts( Long cursorId, Integer pageSize, Long userId, - ProductSellingStateType productSellingStateType) { + OrderSpecifier orderBy) { return jpaQueryFactory .select(Projections.constructor(GetProductThumbnail.class, @@ -74,8 +74,8 @@ public List findLatestProducts( sizesFilter(sizes), ltProductId(cursorId), deletedFilter(), - sellingStateFilter(productSellingStateType)) - .orderBy(product.id.desc()) + sellingStateFilter(ProductSellingStateType.SELLING)) + .orderBy(orderBy) .limit(pageSize) .fetch() .stream() @@ -84,165 +84,31 @@ public List findLatestProducts( } @Override - public List findLowestProducts( - String gender, - String category, - List styles, - Long minPrice, - Long maxPrice, - List brandNames, - List qualityRates, - List sizes, - Long cursorId, - Integer pageSize, - Long userId, - ProductSellingStateType productSellingStateType) { + public List findLatestProducts( + String gender, String category, List styles, Long minPrice, Long maxPrice, List brandNames, + List qualityRates, List sizes, Long cursorId, Integer pageSize, Long userId) { + return findProducts(gender, category, styles, minPrice, maxPrice, brandNames, qualityRates, sizes, cursorId, pageSize, userId, product.id.desc()); + } - return jpaQueryFactory - .select(Projections.constructor(GetProductThumbnail.class, - product.id, - product.thumbnailImageUrl, - product.productName, - product.price, - product.discountRate, - product.brandName, - product.qualityRate.stringValue(), - productLike.id.isNotNull())) - .from(product) - .leftJoin(productLike) - .on(product.id.eq(productLike.productId) - .and(productLike.userId.eq(userId))) - .leftJoin(productStyle) - .on(product.id.eq(productStyle.product.id)) - .leftJoin(productCategory) - .on(product.id.eq(productCategory.product.id)) - .leftJoin(productSellingState) - .on(product.id.eq(productSellingState.productId)) - .where( - genderFilter(gender), - categoryFilter(category), - stylesFilter(styles), - priceFilter(minPrice, maxPrice), - brandFilter(brandNames), - qualityFilter(qualityRates), - sizesFilter(sizes), - ltProductId(cursorId), - deletedFilter(), - sellingStateFilter(productSellingStateType)) - .orderBy(product.price.asc()) - .limit(pageSize) - .fetch() - .stream() - .distinct() - .collect(Collectors.toList()); + @Override + public List findLowestProducts( + String gender, String category, List styles, Long minPrice, Long maxPrice, List brandNames, + List qualityRates, List sizes, Long cursorId, Integer pageSize, Long userId) { + return findProducts(gender, category, styles, minPrice, maxPrice, brandNames, qualityRates, sizes, cursorId, pageSize, userId, product.price.asc()); } @Override public List findHighestProducts( - String gender, - String category, - List styles, - Long minPrice, - Long maxPrice, - List brandNames, - List qualityRates, - List sizes, - Long cursorId, - Integer pageSize, - Long userId, - ProductSellingStateType productSellingStateType) { - - return jpaQueryFactory - .select(Projections.constructor(GetProductThumbnail.class, - product.id, - product.thumbnailImageUrl, - product.productName, - product.price, - product.discountRate, - product.brandName, - product.qualityRate.stringValue(), - productLike.id.isNotNull())) - .from(product) - .leftJoin(productLike) - .on(product.id.eq(productLike.productId) - .and(productLike.userId.eq(userId))) - .leftJoin(productStyle) - .on(product.id.eq(productStyle.product.id)) - .leftJoin(productCategory) - .on(product.id.eq(productCategory.product.id)) - .leftJoin(productSellingState) - .on(product.id.eq(productSellingState.productId)) - .where( - genderFilter(gender), - categoryFilter(category), - stylesFilter(styles), - priceFilter(minPrice, maxPrice), - brandFilter(brandNames), - qualityFilter(qualityRates), - sizesFilter(sizes), - ltProductId(cursorId), - deletedFilter(), - sellingStateFilter(productSellingStateType)) - .orderBy(product.price.desc()) - .limit(pageSize) - .fetch() - .stream() - .distinct() - .collect(Collectors.toList()); + String gender, String category, List styles, Long minPrice, Long maxPrice, List brandNames, + List qualityRates, List sizes, Long cursorId, Integer pageSize, Long userId) { + return findProducts(gender, category, styles, minPrice, maxPrice, brandNames, qualityRates, sizes, cursorId, pageSize, userId, product.price.desc()); } @Override public List findHighestDiscountProducts( - String gender, - String category, - List styles, - Long minPrice, - Long maxPrice, - List brandNames, - List qualityRates, - List sizes, - Long cursorId, - Integer pageSize, - Long userId, - ProductSellingStateType productSellingStateType) { - - return jpaQueryFactory - .select(Projections.constructor(GetProductThumbnail.class, - product.id, - product.thumbnailImageUrl, - product.productName, - product.price, - product.discountRate, - product.brandName, - product.qualityRate.stringValue(), - productLike.id.isNotNull())) - .from(product) - .leftJoin(productLike) - .on(product.id.eq(productLike.productId) - .and(productLike.userId.eq(userId))) - .leftJoin(productStyle) - .on(product.id.eq(productStyle.product.id)) - .leftJoin(productCategory) - .on(product.id.eq(productCategory.product.id)) - .leftJoin(productSellingState) - .on(product.id.eq(productSellingState.productId)) - .where( - genderFilter(gender), - categoryFilter(category), - stylesFilter(styles), - priceFilter(minPrice, maxPrice), - brandFilter(brandNames), - qualityFilter(qualityRates), - sizesFilter(sizes), - ltProductId(cursorId), - deletedFilter(), - sellingStateFilter(productSellingStateType)) - .orderBy(product.discountRate.desc()) - .limit(pageSize) - .fetch() - .stream() - .distinct() - .collect(Collectors.toList()); + String gender, String category, List styles, Long minPrice, Long maxPrice, List brandNames, + List qualityRates, List sizes, Long cursorId, Integer pageSize, Long userId) { + return findProducts(gender, category, styles, minPrice, maxPrice, brandNames, qualityRates, sizes, cursorId, pageSize, userId, product.discountRate.desc()); } @Override 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 f260086b..37d230c3 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 @@ -174,51 +174,29 @@ public List getMainPageRecommendation(String gender, Long c return productRepository.findMainPageRecommendation(cursorId, pageSize, user.getId(), gender, ProductSellingStateType.SELLING); } - public List getLatest(String gender, String category, List styles, Long minPrice, Long maxPrice, List brandNames, List qualityRates, List sizes, Long cursorId, Integer pageSize) { + public List getProducts(String type, String gender, String category, List styles, Long minPrice, Long maxPrice, List brandNames, List qualityRates, List sizes, Long cursorId, Integer pageSize){ User user = userRepository.findByProviderId(SecurityContextHolder.getContext().getAuthentication().getName()) .orElse(null); + Long userId = user == null ? 0L : user.getId(); // non-login user 고려 if (pageSize == null) pageSize = 4; - // non-login user - if (user == null) return productRepository.findLatestProducts(gender, category, styles, minPrice, maxPrice, brandNames, qualityRates, sizes, cursorId, pageSize, 0L, ProductSellingStateType.SELLING); - - return productRepository.findLatestProducts(gender, category, styles, minPrice, maxPrice, brandNames, qualityRates, sizes, cursorId, pageSize, user.getId(), ProductSellingStateType.SELLING); - } - - public List getLowest(String gender, String category, List styles, Long minPrice, Long maxPrice, List brandNames, List qualityRates, List sizes, Long cursorId, Integer pageSize) { - User user = userRepository.findByProviderId(SecurityContextHolder.getContext().getAuthentication().getName()) - .orElse(null); - - if (pageSize == null) pageSize = 4; - - // non-login user - if (user == null) return productRepository.findLowestProducts(gender, category, styles, minPrice, maxPrice, brandNames, qualityRates, sizes, cursorId, pageSize, 0L, ProductSellingStateType.SELLING); - return productRepository.findLowestProducts(gender, category, styles, minPrice, maxPrice, brandNames, qualityRates, sizes, cursorId, pageSize, user.getId(), ProductSellingStateType.SELLING); - } - - public List getHighest(String gender, String category, List styles, Long minPrice, Long maxPrice, List brandNames, List qualityRates, List sizes, Long cursorId, Integer pageSize) { - User user = userRepository.findByProviderId(SecurityContextHolder.getContext().getAuthentication().getName()) - .orElse(null); - - if (pageSize == null) pageSize = 4; - - // non-login user - if (user == null) return productRepository.findHighestProducts(gender, category, styles, minPrice, maxPrice, brandNames, qualityRates, sizes, cursorId, pageSize, 0L, ProductSellingStateType.SELLING); - - return productRepository.findHighestProducts(gender, category, styles, minPrice, maxPrice, brandNames, qualityRates, sizes, cursorId, pageSize, user.getId(), ProductSellingStateType.SELLING); - } - - public List getHighestDiscount(String gender, String category, List styles, Long minPrice, Long maxPrice, List brandNames, List qualityRates, List sizes, Long cursorId, Integer pageSize) { - User user = userRepository.findByProviderId(SecurityContextHolder.getContext().getAuthentication().getName()) - .orElse(null); - - if (pageSize == null) pageSize = 4; - - // non-login user - if (user == null) return productRepository.findHighestDiscountProducts(gender, category, styles, minPrice, maxPrice, brandNames, qualityRates, sizes, cursorId, pageSize, 0L, ProductSellingStateType.SELLING); + switch (type) { + case "latest" -> { + return productRepository.findLatestProducts(gender, category, styles, minPrice, maxPrice, brandNames, qualityRates, sizes, cursorId, pageSize, userId); + } + case "lowest-price" -> { + return productRepository.findLowestProducts(gender, category, styles, minPrice, maxPrice, brandNames, qualityRates, sizes, cursorId, pageSize, userId); + } + case "highest-price" -> { + return productRepository.findHighestProducts(gender, category, styles, minPrice, maxPrice, brandNames, qualityRates, sizes, cursorId, pageSize, userId); + } + case "highest-discount" -> { + return productRepository.findHighestDiscountProducts(gender, category, styles, minPrice, maxPrice, brandNames, qualityRates, sizes, cursorId, pageSize, userId); + } + default -> throw new CustomException(INVALID_SORT_TYPE); + } - return productRepository.findHighestDiscountProducts(gender, category, styles, minPrice, maxPrice, brandNames, qualityRates, sizes, cursorId, pageSize, user.getId(), ProductSellingStateType.SELLING); } public Boolean toggleLike(Long productId) { diff --git a/src/main/java/com/example/repick/global/error/exception/ErrorCode.java b/src/main/java/com/example/repick/global/error/exception/ErrorCode.java index c3f50518..86d0b5ca 100644 --- a/src/main/java/com/example/repick/global/error/exception/ErrorCode.java +++ b/src/main/java/com/example/repick/global/error/exception/ErrorCode.java @@ -28,6 +28,7 @@ public enum ErrorCode { INVALID_QUALITY_RATE_NAME(400, "P024", "존재하지 않는 품질 이름입니다."), INVALID_GENDER_ID(400, "P025", "존재하지 않는 성별 ID입니다."), INVALID_GENDER_NAME(400, "P026", "존재하지 않는 성별 이름입니다."), + INVALID_SORT_TYPE(400, "P027", "존재하지 않는 정렬 타입입니다."), PRICE_ALREADY_EXISTS(400, "P027", "이미 가격이 설정된 상품입니다."), PRICE_NOT_EXISTS(400, "P028", "가격이 설정되지 않은 상품이 존재합니다."), PRODUCT_NOT_DESIRED_STATE(400, "P029", "정상적인 접근이 아닙니다."),