diff --git a/src/main/java/com/dailyon/productservice/category/repository/CategoryRepository.java b/src/main/java/com/dailyon/productservice/category/repository/CategoryRepository.java index c11d32bb..991f75ff 100644 --- a/src/main/java/com/dailyon/productservice/category/repository/CategoryRepository.java +++ b/src/main/java/com/dailyon/productservice/category/repository/CategoryRepository.java @@ -1,6 +1,8 @@ package com.dailyon.productservice.category.repository; import com.dailyon.productservice.category.entity.Category; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -16,6 +18,8 @@ public interface CategoryRepository extends JpaRepository { List findAllByOrderByNameAsc(); + Page findAllByOrderByNameAsc(Pageable pageable); + List findCategoriesByNameContainsOrderByNameAsc(String name); @Query(nativeQuery = true, value = diff --git a/src/main/java/com/dailyon/productservice/category/service/CategoryService.java b/src/main/java/com/dailyon/productservice/category/service/CategoryService.java index 2c55e194..caace6f2 100644 --- a/src/main/java/com/dailyon/productservice/category/service/CategoryService.java +++ b/src/main/java/com/dailyon/productservice/category/service/CategoryService.java @@ -83,7 +83,7 @@ public ReadChildrenCategoryListResponse readLeafCategories() { } public ReadCategoryPageResponse readCategoryPages(Pageable pageable) { - return ReadCategoryPageResponse.fromEntity(categoryRepository.findAll(pageable)); + return ReadCategoryPageResponse.fromEntity(categoryRepository.findAllByOrderByNameAsc(pageable)); } @Transactional diff --git a/src/main/java/com/dailyon/productservice/common/config/CacheConfig.java b/src/main/java/com/dailyon/productservice/common/config/CacheConfig.java index ac0fa373..dcab60e6 100644 --- a/src/main/java/com/dailyon/productservice/common/config/CacheConfig.java +++ b/src/main/java/com/dailyon/productservice/common/config/CacheConfig.java @@ -4,6 +4,7 @@ import com.dailyon.productservice.category.entity.Category; import com.dailyon.productservice.product.dto.response.ReadBestProductListResponse; import com.dailyon.productservice.product.dto.response.ReadNewProductListResponse; +import com.dailyon.productservice.product.dto.response.ReadProductDetailResponse; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; @@ -54,8 +55,12 @@ public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer( Jackson2JsonRedisSerializer bestProductVOJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(ReadBestProductListResponse.class); + Jackson2JsonRedisSerializer auctionProductVOJackson2JsonRedisSerializer = + new Jackson2JsonRedisSerializer<>(ReadProductDetailResponse.class); + newProductVOJackson2JsonRedisSerializer.setObjectMapper(objectMapper); bestProductVOJackson2JsonRedisSerializer.setObjectMapper(objectMapper); + auctionProductVOJackson2JsonRedisSerializer.setObjectMapper(objectMapper); return (builder -> builder .withCacheConfiguration( @@ -82,6 +87,18 @@ public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer( .fromSerializer(bestProductVOJackson2JsonRedisSerializer) ) ) + .withCacheConfiguration( + "auctionProducts", + RedisCacheConfiguration.defaultCacheConfig() + .entryTtl(Duration.ofDays(1)) + .disableCachingNullValues() + .serializeKeysWith(RedisSerializationContext.SerializationPair + .fromSerializer(new StringRedisSerializer()) + ) + .serializeValuesWith(RedisSerializationContext.SerializationPair + .fromSerializer(auctionProductVOJackson2JsonRedisSerializer) + ) + ) ); } } diff --git a/src/main/java/com/dailyon/productservice/common/exception/advice/GlobalControllerAdvice.java b/src/main/java/com/dailyon/productservice/common/exception/advice/GlobalControllerAdvice.java index add3b9c6..86da1c6d 100644 --- a/src/main/java/com/dailyon/productservice/common/exception/advice/GlobalControllerAdvice.java +++ b/src/main/java/com/dailyon/productservice/common/exception/advice/GlobalControllerAdvice.java @@ -23,7 +23,7 @@ public ErrorResponse validException(BindException e) { } @ExceptionHandler(NotExistsException.class) - @ResponseStatus(HttpStatus.BAD_REQUEST) + @ResponseStatus(HttpStatus.NOT_FOUND) public ErrorResponse notExistsException(NotExistsException e) { return ErrorResponse.builder() .message(e.getMessage()) diff --git a/src/main/java/com/dailyon/productservice/product/controller/ProductAdminController.java b/src/main/java/com/dailyon/productservice/product/controller/ProductAdminController.java index 5dff4914..370c78ab 100644 --- a/src/main/java/com/dailyon/productservice/product/controller/ProductAdminController.java +++ b/src/main/java/com/dailyon/productservice/product/controller/ProductAdminController.java @@ -44,14 +44,16 @@ ResponseEntity readProductPage( @RequestParam(required = false) Long categoryId, @RequestParam ProductType type, @RequestParam(required = false) String query, - @PageableDefault( - size = 5, - sort = {"updatedAt"}, - direction = Sort.Direction.DESC - ) Pageable pageable + @RequestParam int page, + @RequestParam int size, + @RequestParam String sort, + @RequestParam String direction ) { return ResponseEntity.status(HttpStatus.OK).body( - productFacade.readProductPage(brandId, categoryId, type, query, pageable) + productFacade.readProductPage( + brandId, categoryId, type, query, + page, size, sort, direction + ) ); } diff --git a/src/main/java/com/dailyon/productservice/product/controller/ProductController.java b/src/main/java/com/dailyon/productservice/product/controller/ProductController.java index c4b45864..91683102 100644 --- a/src/main/java/com/dailyon/productservice/product/controller/ProductController.java +++ b/src/main/java/com/dailyon/productservice/product/controller/ProductController.java @@ -5,6 +5,7 @@ import com.dailyon.productservice.product.dto.response.*; import com.dailyon.productservice.product.facade.ProductFacade; import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.Cacheable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -20,6 +21,10 @@ ResponseEntity readProductDetail(@PathVariable Long p return ResponseEntity.status(HttpStatus.OK).body(productFacade.readProductDetail(productId)); } + @GetMapping("/auctions/id/{productId}") + ResponseEntity readAuctionProductDetail(@PathVariable Long productId) { + return ResponseEntity.status(HttpStatus.OK).body(productFacade.readAuctionProductDetail(productId)); + } /** * 쇼핑몰 화면에서 무한 스크롤 위한 조회 api * @param lastVal 최초 호출 시 direction이 asc면 0, desc면 큰 값 diff --git a/src/main/java/com/dailyon/productservice/product/dto/response/ReadProductAdminResponse.java b/src/main/java/com/dailyon/productservice/product/dto/response/ReadProductAdminResponse.java index 66cc404e..0975a02f 100644 --- a/src/main/java/com/dailyon/productservice/product/dto/response/ReadProductAdminResponse.java +++ b/src/main/java/com/dailyon/productservice/product/dto/response/ReadProductAdminResponse.java @@ -18,7 +18,9 @@ public class ReadProductAdminResponse { private Long id; private Long brandId; + private String brandName; private Long categoryId; + private String categoryName; private String name; private Gender gender; private Integer price; @@ -33,7 +35,9 @@ public static ReadProductAdminResponse fromEntity(Product product) { return ReadProductAdminResponse.builder() .id(product.getId()) .brandId(product.getBrand().getId()) + .brandName(product.getBrand().getName()) .categoryId(product.getCategory().getId()) + .categoryName(product.getCategory().getName()) .name(product.getName()) .gender(product.getGender()) .price(product.getPrice()) diff --git a/src/main/java/com/dailyon/productservice/product/facade/ProductFacade.java b/src/main/java/com/dailyon/productservice/product/facade/ProductFacade.java index da677cbb..909c7df8 100644 --- a/src/main/java/com/dailyon/productservice/product/facade/ProductFacade.java +++ b/src/main/java/com/dailyon/productservice/product/facade/ProductFacade.java @@ -72,6 +72,11 @@ public ReadProductDetailResponse readProductDetail(Long productId) { return productService.readProductDetail(productId); } + @Cacheable(value = "auctionProducts", key = "#productId", unless = "#result == null") + public ReadProductDetailResponse readAuctionProductDetail(Long productId) { + return productService.readProductDetail(productId); + } + public ReadProductSliceResponse readProductSlice( String lastVal, Long brandId, Long categoryId, Gender gender, ProductType productType, Integer lowPrice, Integer highPrice, String query, String sort, String direction @@ -91,8 +96,14 @@ public ReadOOTDSearchSliceResponse searchFromOOTD(Long lastId, String query) { return productService.searchFromOOTD(lastId, query); } - public ReadProductPageResponse readProductPage(Long brandId, Long categoryId, ProductType type, String query, Pageable pageable) { - return productService.readProductPage(brandId, categoryId, type, query, pageable); + public ReadProductPageResponse readProductPage( + Long brandId, Long categoryId, ProductType type, String query, + int page, int size, String sort, String direction + ) { + return productService.readProductPage( + brandId, categoryId, type, query, + page, size, sort, direction + ); } @Cacheable(value = "newProducts", unless = "#result == null") diff --git a/src/main/java/com/dailyon/productservice/product/repository/ProductCustomRepository.java b/src/main/java/com/dailyon/productservice/product/repository/ProductCustomRepository.java index 6c9ce0b3..043522cc 100644 --- a/src/main/java/com/dailyon/productservice/product/repository/ProductCustomRepository.java +++ b/src/main/java/com/dailyon/productservice/product/repository/ProductCustomRepository.java @@ -17,8 +17,8 @@ Slice findProductSlice( ); Page findProductPage( - Long brandId, List childCategories, - ProductType type, String query, Pageable pageable + Long brandId, List childCategories, ProductType type, String query, + int page, int size, String sort, String direction ); Slice searchProductsFromOOTD(Long lastId, String query); diff --git a/src/main/java/com/dailyon/productservice/product/repository/ProductCustomRepositoryImpl.java b/src/main/java/com/dailyon/productservice/product/repository/ProductCustomRepositoryImpl.java index bdca108e..c4e0b1a6 100644 --- a/src/main/java/com/dailyon/productservice/product/repository/ProductCustomRepositoryImpl.java +++ b/src/main/java/com/dailyon/productservice/product/repository/ProductCustomRepositoryImpl.java @@ -5,10 +5,8 @@ import com.dailyon.productservice.common.enums.ProductType; import com.dailyon.productservice.product.entity.Product; import com.querydsl.core.BooleanBuilder; -import com.querydsl.core.types.Order; import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.dsl.BooleanExpression; -import com.querydsl.core.types.dsl.PathBuilder; import com.querydsl.jpa.impl.JPAQuery; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; @@ -73,10 +71,11 @@ public Slice findProductSlice( @Override public Page findProductPage( - Long brandId, List childCategories, - ProductType type, String query, Pageable pageable + Long brandId, List childCategories, ProductType type, String query, + int page, int size, String sort, String direction ) { - PathBuilder entityPath = new PathBuilder<>(Product.class, "product"); + Pageable pageable = PageRequest.of(page, size); + BooleanBuilder booleanBuilder = new BooleanBuilder(); if(query != null) { booleanBuilder.and(nameContains(query).or(codeContains(query))); @@ -90,14 +89,10 @@ public Page findProductPage( .and(booleanBuilder) .and(productTypeEq(type)) ) + .orderBy(orderSpecifier(sort, direction)) .offset(pageable.getOffset()) .limit(pageable.getPageSize()); - for(Sort.Order order: pageable.getSort()) { - indexQuery.orderBy(new OrderSpecifier( - order.isAscending() ? Order.ASC : Order.DESC, - entityPath.get(order.getProperty()) - )); - } + List indexes = indexQuery.fetch(); if(indexes.isEmpty()) { return new PageImpl<>(new ArrayList<>(), pageable, 0); @@ -111,14 +106,9 @@ public Page findProductPage( .leftJoin(product.describeImages, describeImage).fetchJoin() .leftJoin(product.productStocks, productStock).fetchJoin() .leftJoin(product.reviewAggregate, reviewAggregate).fetchJoin() + .orderBy(orderSpecifier(sort, direction)) .where(product.id.in(indexes)); - for(Sort.Order order: pageable.getSort()) { - resultQuery.orderBy(new OrderSpecifier( - order.isAscending() ? Order.ASC : Order.DESC, - entityPath.get(order.getProperty()) - )); - } List result = resultQuery.fetch(); JPAQuery countQuery = jpaQueryFactory @@ -166,11 +156,11 @@ public Slice searchProductsFromOOTD(Long lastId, String query) { } private BooleanExpression brandIdEq(Long brandId) { - return brandId == null ? null : brand.id.eq(brandId); + return brandId == null ? null : product.brand.id.eq(brandId); } private BooleanExpression categoryIn(List childCategories) { - return childCategories == null ? null : category.in(childCategories); + return childCategories == null ? null : product.category.in(childCategories); } private BooleanExpression genderEq(Gender gender) { diff --git a/src/main/java/com/dailyon/productservice/product/service/ProductService.java b/src/main/java/com/dailyon/productservice/product/service/ProductService.java index 7e7e2149..40ae74d6 100644 --- a/src/main/java/com/dailyon/productservice/product/service/ProductService.java +++ b/src/main/java/com/dailyon/productservice/product/service/ProductService.java @@ -242,15 +242,19 @@ public Slice readProductSlice( } public ReadProductPageResponse readProductPage( - Long brandId, Long categoryId, ProductType type, - String query, Pageable pageable + Long brandId, Long categoryId, ProductType type, String query, + int page, int size, String sort, String direction ) { List childCategories = null; if(categoryId != null) { childCategories = categoryRepository.findAllChildCategories(categoryId); } return ReadProductPageResponse.fromEntity( - productRepository.findProductPage(brandId, childCategories, type, query, pageable)); + productRepository.findProductPage( + brandId, childCategories, type, query, + page, size, sort, direction + ) + ); } public ReadOOTDSearchSliceResponse searchFromOOTD(Long lastId, String query) { diff --git a/src/main/java/com/dailyon/productservice/product/vo/BestProductVO.java b/src/main/java/com/dailyon/productservice/product/vo/BestProductVO.java index afedec9d..2b474a35 100644 --- a/src/main/java/com/dailyon/productservice/product/vo/BestProductVO.java +++ b/src/main/java/com/dailyon/productservice/product/vo/BestProductVO.java @@ -14,6 +14,7 @@ public class BestProductVO implements Serializable, Comparable { private Long id; private String brandName; + private Long categoryId; private String categoryName; private Integer price; private String name; @@ -25,6 +26,7 @@ public static BestProductVO create(Product product, ProductRankResponse rank) { return BestProductVO.builder() .id(product.getId()) .brandName(product.getBrand().getName()) + .categoryId(product.getCategory().getId()) .categoryName(product.getCategory().getName()) .price(product.getPrice()) .name(product.getName()) diff --git a/src/main/java/com/dailyon/productservice/product/vo/NewProductVO.java b/src/main/java/com/dailyon/productservice/product/vo/NewProductVO.java index 86799fdd..62b5e087 100644 --- a/src/main/java/com/dailyon/productservice/product/vo/NewProductVO.java +++ b/src/main/java/com/dailyon/productservice/product/vo/NewProductVO.java @@ -13,6 +13,7 @@ public class NewProductVO implements Serializable { private Long id; private String brandName; + private Long categoryId; private String categoryName; private Integer price; private String name; @@ -23,6 +24,7 @@ public static NewProductVO fromEntity(Product product) { return NewProductVO.builder() .id(product.getId()) .brandName(product.getBrand().getName()) + .categoryId(product.getCategory().getId()) .categoryName(product.getCategory().getName()) .price(product.getPrice()) .name(product.getName()) diff --git a/src/test/java/com/dailyon/productservice/controller/brand/BrandAdminControllerTests.java b/src/test/java/com/dailyon/productservice/controller/brand/BrandAdminControllerTests.java index e58dcd79..136c113d 100644 --- a/src/test/java/com/dailyon/productservice/controller/brand/BrandAdminControllerTests.java +++ b/src/test/java/com/dailyon/productservice/controller/brand/BrandAdminControllerTests.java @@ -117,7 +117,7 @@ void updateBrandControllerFail1() throws Exception { ); // then - resultActions.andExpect(MockMvcResultMatchers.status().isBadRequest()); + resultActions.andExpect(MockMvcResultMatchers.status().isNotFound()); } @Test @@ -171,7 +171,7 @@ void deleteBrandFail1() throws Exception { ); resultActions - .andExpect(MockMvcResultMatchers.status().isBadRequest()) + .andExpect(MockMvcResultMatchers.status().isNotFound()) .andExpect(MockMvcResultMatchers.jsonPath("$.message").value(NotExistsException.BRAND_NOT_FOUND)); } } diff --git a/src/test/java/com/dailyon/productservice/controller/category/CategoryAdminControllerTests.java b/src/test/java/com/dailyon/productservice/controller/category/CategoryAdminControllerTests.java index 8202dd29..1248fff4 100644 --- a/src/test/java/com/dailyon/productservice/controller/category/CategoryAdminControllerTests.java +++ b/src/test/java/com/dailyon/productservice/controller/category/CategoryAdminControllerTests.java @@ -75,7 +75,7 @@ void createCategoryFail2() throws Exception { // then resultActions - .andExpect(MockMvcResultMatchers.status().isBadRequest()); + .andExpect(MockMvcResultMatchers.status().isNotFound()); } @Test @@ -225,7 +225,7 @@ void deleteCategoryFail() throws Exception { // then resultActions - .andExpect(MockMvcResultMatchers.status().isBadRequest()) + .andExpect(MockMvcResultMatchers.status().isNotFound()) .andExpect(MockMvcResultMatchers.jsonPath("$.message") .value(NotExistsException.CATEGORY_NOT_FOUND)); } diff --git a/src/test/java/com/dailyon/productservice/controller/productsize/ProductSizeAdminControllerTests.java b/src/test/java/com/dailyon/productservice/controller/productsize/ProductSizeAdminControllerTests.java index ee692b52..3b059363 100644 --- a/src/test/java/com/dailyon/productservice/controller/productsize/ProductSizeAdminControllerTests.java +++ b/src/test/java/com/dailyon/productservice/controller/productsize/ProductSizeAdminControllerTests.java @@ -82,7 +82,7 @@ void createProductSizeFail1() throws Exception { ); // then - resultActions.andExpect(MockMvcResultMatchers.status().isBadRequest()); + resultActions.andExpect(MockMvcResultMatchers.status().isNotFound()); } @Test diff --git a/src/test/java/com/dailyon/productservice/service/product/ProductServiceTests.java b/src/test/java/com/dailyon/productservice/service/product/ProductServiceTests.java index 7ef49a8b..6b33163f 100644 --- a/src/test/java/com/dailyon/productservice/service/product/ProductServiceTests.java +++ b/src/test/java/com/dailyon/productservice/service/product/ProductServiceTests.java @@ -281,7 +281,8 @@ void searchProductsFromOOTDTest() { void readProductPage() { // given, when ReadProductPageResponse response = productService.readProductPage( - null, null, ProductType.valueOf("NORMAL"), null, PageRequest.of(0, 8) + null, null, ProductType.valueOf("NORMAL"), null, + 0, 5, "updatedAt", "desc" ); // then