diff --git a/build.gradle b/build.gradle index 1901760f..d7c96915 100644 --- a/build.gradle +++ b/build.gradle @@ -46,6 +46,12 @@ dependencies { //Junit testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + // querydsl + implementation "com.querydsl:querydsl-jpa:5.0.0:jakarta" + annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" + annotationProcessor "jakarta.annotation:jakarta.annotation-api" + annotationProcessor "jakarta.persistence:jakarta.persistence-api" } tasks.named('test') { diff --git a/src/main/java/com/tasksprints/auction/api/auction/AuctionController.java b/src/main/java/com/tasksprints/auction/api/auction/AuctionController.java index c46fec60..206c94a1 100644 --- a/src/main/java/com/tasksprints/auction/api/auction/AuctionController.java +++ b/src/main/java/com/tasksprints/auction/api/auction/AuctionController.java @@ -70,18 +70,28 @@ public ResponseEntity> getAuctionStatus(@PathVariable Long auc // return ResponseEntity.ok(ApiResult.success(ApiResponseMessages.AUCTION_BY_USER_RETRIEVED, userAuctions)); // } - @GetMapping - @Operation(summary = "Get all auctions", description = "Retrieves all auctions.") - @ApiResponse(responseCode = "200", description = "All auctions retrieved successfully") - public ResponseEntity>> getAllAuctions(@RequestParam(required = false) AuctionRequest.AuctionCategoryParam auctionCategory) { - List allAuctions; - if (auctionCategory != null) { - allAuctions = auctionService.getAuctionsByAuctionCategory(auctionCategory.getAuctionCategory()); - } else { - allAuctions = auctionService.getAllAuctions(); - } - return ResponseEntity.ok(ApiResult.success(ApiResponseMessages.ALL_AUCTIONS_RETRIEVED, allAuctions)); - } +// @GetMapping +// @Operation(summary = "Get all auctions", description = "Retrieves all auctions.") +// @ApiResponse(responseCode = "200", description = "All auctions retrieved successfully") +// public ResponseEntity>> getAllAuctions(@RequestParam(required = false) AuctionRequest.AuctionCategoryParam auctionCategory) { +// List allAuctions; +// if (auctionCategory != null) { +// allAuctions = auctionService.getAuctionsByAuctionCategory(auctionCategory); +// } else { +// allAuctions = auctionService.getAllAuctions(); +// } +// return ResponseEntity.ok(ApiResult.success(ApiResponseMessages.ALL_AUCTIONS_RETRIEVED, allAuctions)); +// } +@GetMapping +@Operation(summary = "Get all auctions", description = "Retrieves all auctions.") +@ApiResponse(responseCode = "200", description = "All auctions retrieved successfully") +public ResponseEntity>> getAllAuctions( + @RequestParam(required = false) AuctionRequest.AuctionCategoryParam auctionCategory, + @RequestParam(required = false) AuctionRequest.ProductCategoryParam productCategory +) { + List auctions = auctionService.getAuctionsByFilter(productCategory,auctionCategory); + return ResponseEntity.ok(ApiResult.success(ApiResponseMessages.ALL_AUCTIONS_RETRIEVED, auctions)); +} @GetMapping("/{auctionId}") @Operation(summary = "Get auction by ID", description = "Retrieves auction details by its ID.") diff --git a/src/main/java/com/tasksprints/auction/common/config/QueryDslConfig.java b/src/main/java/com/tasksprints/auction/common/config/QueryDslConfig.java new file mode 100644 index 00000000..305f4ecc --- /dev/null +++ b/src/main/java/com/tasksprints/auction/common/config/QueryDslConfig.java @@ -0,0 +1,18 @@ +package com.tasksprints.auction.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 +public class QueryDslConfig { + @PersistenceContext + private EntityManager entityManager; + + @Bean + public JPAQueryFactory jpaQueryFactory(){ + return new JPAQueryFactory(entityManager); + } +} diff --git a/src/main/java/com/tasksprints/auction/domain/auction/dto/request/AuctionRequest.java b/src/main/java/com/tasksprints/auction/domain/auction/dto/request/AuctionRequest.java index 07291b75..d4d42578 100644 --- a/src/main/java/com/tasksprints/auction/domain/auction/dto/request/AuctionRequest.java +++ b/src/main/java/com/tasksprints/auction/domain/auction/dto/request/AuctionRequest.java @@ -2,6 +2,7 @@ import com.tasksprints.auction.domain.auction.model.AuctionCategory; import com.tasksprints.auction.domain.auction.model.AuctionStatus; +import com.tasksprints.auction.domain.product.model.ProductCategory; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; @@ -26,7 +27,17 @@ public static class Create{ public static class AuctionCategoryParam { AuctionCategory auctionCategory; public AuctionCategoryParam(String auctionCategory){ - this.auctionCategory = AuctionCategory.fromString(auctionCategory); + this.auctionCategory = AuctionCategory.fromDisplayName(auctionCategory); + } + } + + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class ProductCategoryParam { + ProductCategory productCategory; + public ProductCategoryParam(String productCategory){ + this.productCategory = ProductCategory.fromDisplayName(productCategory); } } } diff --git a/src/main/java/com/tasksprints/auction/domain/auction/model/AuctionCategory.java b/src/main/java/com/tasksprints/auction/domain/auction/model/AuctionCategory.java index 80453302..697f08f9 100644 --- a/src/main/java/com/tasksprints/auction/domain/auction/model/AuctionCategory.java +++ b/src/main/java/com/tasksprints/auction/domain/auction/model/AuctionCategory.java @@ -6,7 +6,7 @@ public enum AuctionCategory { PRIVATE_PAID, PUBLIC_PAID; - public static AuctionCategory fromString(String auctionCategory) { + public static AuctionCategory fromDisplayName(String auctionCategory) { try { return AuctionCategory.valueOf(auctionCategory.toUpperCase()); // 대문자로 변환하여 비교 } catch (IllegalArgumentException e) { diff --git a/src/main/java/com/tasksprints/auction/domain/auction/repository/AuctionCriteriaRepository.java b/src/main/java/com/tasksprints/auction/domain/auction/repository/AuctionCriteriaRepository.java new file mode 100644 index 00000000..62d0174e --- /dev/null +++ b/src/main/java/com/tasksprints/auction/domain/auction/repository/AuctionCriteriaRepository.java @@ -0,0 +1,13 @@ +package com.tasksprints.auction.domain.auction.repository; + +import com.tasksprints.auction.domain.auction.model.Auction; +import com.tasksprints.auction.domain.auction.model.AuctionCategory; +import com.tasksprints.auction.domain.product.model.ProductCategory; + + +import java.util.List; + +public interface AuctionCriteriaRepository { + public List getAuctionsByFilters(ProductCategory productCategory, + AuctionCategory category); +} diff --git a/src/main/java/com/tasksprints/auction/domain/auction/repository/AuctionCriteriaRepositoryImpl.java b/src/main/java/com/tasksprints/auction/domain/auction/repository/AuctionCriteriaRepositoryImpl.java new file mode 100644 index 00000000..91591cc5 --- /dev/null +++ b/src/main/java/com/tasksprints/auction/domain/auction/repository/AuctionCriteriaRepositoryImpl.java @@ -0,0 +1,30 @@ +package com.tasksprints.auction.domain.auction.repository; + +import com.querydsl.jpa.impl.JPAQueryFactory; +import com.tasksprints.auction.domain.auction.model.Auction; +import com.tasksprints.auction.domain.auction.model.AuctionCategory; +import com.tasksprints.auction.domain.auction.model.QAuction; +import com.tasksprints.auction.domain.product.model.ProductCategory; +import com.tasksprints.auction.domain.product.model.QProduct; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@RequiredArgsConstructor +@Repository +public class AuctionCriteriaRepositoryImpl implements AuctionCriteriaRepository { + private final JPAQueryFactory queryFactory; + public List getAuctionsByFilters( ProductCategory productCategory, + AuctionCategory category + ) { + QAuction auction = QAuction.auction; + QProduct product = QProduct.product; + return queryFactory.selectFrom(auction) + .leftJoin(auction.product, product) // Auction과 Product 조인 + .where(category != null ? auction.auctionCategory.eq(category) : null) + .where(productCategory != null ? product.category.eq(productCategory) : null) // ProductCategory 필터 + .fetch(); + } + +} diff --git a/src/main/java/com/tasksprints/auction/domain/auction/repository/AuctionRepository.java b/src/main/java/com/tasksprints/auction/domain/auction/repository/AuctionRepository.java index 93a32947..1b783617 100644 --- a/src/main/java/com/tasksprints/auction/domain/auction/repository/AuctionRepository.java +++ b/src/main/java/com/tasksprints/auction/domain/auction/repository/AuctionRepository.java @@ -3,6 +3,7 @@ import com.tasksprints.auction.domain.auction.model.Auction; import com.tasksprints.auction.domain.auction.model.AuctionCategory; +import com.tasksprints.auction.domain.product.model.ProductCategory; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -10,7 +11,7 @@ import java.util.List; import java.util.Optional; -public interface AuctionRepository extends JpaRepository { +public interface AuctionRepository extends JpaRepository,AuctionCriteriaRepository{ @Query("SELECT a FROM auction a WHERE a.seller.id = :userId") @@ -20,5 +21,6 @@ public interface AuctionRepository extends JpaRepository { Optional findAuctionById(@Param("auctionId") Long auctionId); List findAuctionsByAuctionCategory(AuctionCategory auctionCategory); + List findAuctionByProduct_Category(ProductCategory productCategory); } diff --git a/src/main/java/com/tasksprints/auction/domain/auction/service/AuctionService.java b/src/main/java/com/tasksprints/auction/domain/auction/service/AuctionService.java index 452cfbaa..b3af75e7 100644 --- a/src/main/java/com/tasksprints/auction/domain/auction/service/AuctionService.java +++ b/src/main/java/com/tasksprints/auction/domain/auction/service/AuctionService.java @@ -11,11 +11,12 @@ */ public interface AuctionService { AuctionResponse createAuction(Long userId, AuctionRequest.Create auctionRequest); - void closeAuction(Long auctionId); String getAuctionStatus(Long auctionId); List getAuctionsByUser(Long userId); + List getAuctionsByProductCategory(AuctionRequest.ProductCategoryParam param); List getAllAuctions(); AuctionResponse getAuctionById(Long auctionId); - List getAuctionsByAuctionCategory(AuctionCategory auctionCategory); + List getAuctionsByAuctionCategory(AuctionRequest.AuctionCategoryParam param); + List getAuctionsByFilter(AuctionRequest.ProductCategoryParam productCategoryParam, AuctionRequest.AuctionCategoryParam auctionCategoryParam); } diff --git a/src/main/java/com/tasksprints/auction/domain/auction/service/AuctionServiceImpl.java b/src/main/java/com/tasksprints/auction/domain/auction/service/AuctionServiceImpl.java index 8d974636..dcba6772 100644 --- a/src/main/java/com/tasksprints/auction/domain/auction/service/AuctionServiceImpl.java +++ b/src/main/java/com/tasksprints/auction/domain/auction/service/AuctionServiceImpl.java @@ -86,6 +86,7 @@ public List getAuctionsByUser(Long userId) { .toList(); } + @Override public List getAllAuctions() { List foundAuctions = auctionRepository.findAll(); @@ -101,10 +102,34 @@ public AuctionResponse getAuctionById(Long auctionId) { return AuctionResponse.of(foundAuction); } + @Deprecated + @Override + public List getAuctionsByProductCategory(AuctionRequest.ProductCategoryParam param) { + List foundAuctions = auctionRepository.findAuctionByProduct_Category(param.getProductCategory()); + + return foundAuctions.stream() + .map(AuctionResponse::of) + .toList(); + + } + + @Deprecated + @Override + public List getAuctionsByAuctionCategory(AuctionRequest.AuctionCategoryParam param) { + List foundAuctions = auctionRepository.findAuctionsByAuctionCategory(param.getAuctionCategory()); + return foundAuctions.stream() + .map(AuctionResponse::of) + .toList(); + } + /** + * NULL POINTER EXCEPTION 발생 + * NULL 안정성 보장을 해줬음**/ @Override - public List getAuctionsByAuctionCategory(AuctionCategory auctionCategory) { - List foundAuctions = auctionRepository.findAuctionsByAuctionCategory(auctionCategory); + public List getAuctionsByFilter(AuctionRequest.ProductCategoryParam productCategoryParam, AuctionRequest.AuctionCategoryParam auctionCategoryParam) { + List foundAuctions = auctionRepository.getAuctionsByFilters( + productCategoryParam!=null?productCategoryParam.getProductCategory():null, + auctionCategoryParam!=null?auctionCategoryParam.getAuctionCategory():null); return foundAuctions.stream() .map(AuctionResponse::of) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 0b016c11..2c32aa5f 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -32,7 +32,10 @@ springdoc: display-request-duration: true operations-sorter: alpha packages-to-scan: com.tasksprints.auction.api - +logging: + level: + org.hibernate.SQL: DEBUG + org.hibernate.type.descriptor.sql.BasicBinder: TRACE server: port: 8080 diff --git a/src/test/java/com/tasksprints/auction/api/AuctionControllerTest.java b/src/test/java/com/tasksprints/auction/api/AuctionControllerTest.java index c6448ab2..e99c80e8 100644 --- a/src/test/java/com/tasksprints/auction/api/AuctionControllerTest.java +++ b/src/test/java/com/tasksprints/auction/api/AuctionControllerTest.java @@ -110,7 +110,7 @@ public void testGetAuctionStatus_Success() throws Exception { @DisplayName("경매 유형을 통한 경매목록 조회") public void testFindAuctionByUsingAuctionCategory_Success() throws Exception { List auctionResponseList = new ArrayList<>(); - when(auctionService.getAuctionsByAuctionCategory(any())).thenReturn(auctionResponseList); + when(auctionService.getAuctionsByFilter(any(),any())).thenReturn(auctionResponseList); mockMvc.perform(get("/api/v1/auction") .param("auctionCategory", String.valueOf(AuctionCategory.PRIVATE_FREE))) .andExpect(status().isOk()) @@ -125,7 +125,7 @@ public void testFindAuctionByUsingAuctionCategory_Success() throws Exception { @DisplayName("잘못된 유형을 통한 경매목록 조회(기본값으로 대응)") public void testFindAuctionByUsingWrongAuctionCategory_Success() throws Exception { List auctionResponseList = new ArrayList<>(); - when(auctionService.getAuctionsByAuctionCategory(any())).thenReturn(auctionResponseList); + when(auctionService.getAuctionsByFilter(any(),any())).thenReturn(auctionResponseList); mockMvc.perform(get("/api/v1/auction") .param("auctionCategory", "NON")) diff --git a/src/test/java/com/tasksprints/auction/domain/auction/repository/AuctionRepositoryTest.java b/src/test/java/com/tasksprints/auction/domain/auction/repository/AuctionRepositoryTest.java index 32b7c778..546cc4ee 100644 --- a/src/test/java/com/tasksprints/auction/domain/auction/repository/AuctionRepositoryTest.java +++ b/src/test/java/com/tasksprints/auction/domain/auction/repository/AuctionRepositoryTest.java @@ -1,4 +1,5 @@ package com.tasksprints.auction.domain.auction.repository; +import com.tasksprints.auction.common.config.QueryDslConfig; import com.tasksprints.auction.domain.auction.model.Auction; import com.tasksprints.auction.domain.auction.model.AuctionCategory; import com.tasksprints.auction.domain.auction.model.AuctionStatus; @@ -11,6 +12,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; import java.math.BigDecimal; import java.time.LocalDateTime; @@ -21,6 +23,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; @DataJpaTest +@Import(QueryDslConfig.class) @Slf4j public class AuctionRepositoryTest { @@ -101,7 +104,24 @@ public void testFindAuctionsByAuctionCategory() { assertThat(auctions).hasSize(2); assertThat(auctions).allMatch(auction -> auction.getAuctionCategory() == AuctionCategory.PUBLIC_PAID); } + @Test + @DisplayName("QueryDSL 필터를 통해서 경매 목록 조회") + public void testFindAllUsingFilter() { + Auction auction1 = createAuction(seller, AuctionCategory.PUBLIC_FREE, AuctionStatus.ACTIVE); + Auction auction2 = createAuction(seller, AuctionCategory.PUBLIC_PAID, AuctionStatus.PENDING); + + auctionRepository.save(auction1); + auctionRepository.save(auction2); + + List auctions = auctionRepository.getAuctionsByFilters(null, AuctionCategory.PUBLIC_FREE); + log.info(auctions.toString()); + + assertThat(auctions).hasSize(1); + assertThat(auctions.get(0).getAuctionCategory()).isEqualTo(AuctionCategory.PUBLIC_FREE); + assertThat(auctions.get(0).getAuctionCategory()).isNotEqualTo(AuctionCategory.PUBLIC_PAID); + + } private Auction createAuction(User seller, AuctionCategory category, AuctionStatus status) { return Auction.create( LocalDateTime.now(), diff --git a/src/test/java/com/tasksprints/auction/domain/auction/service/AuctionServiceImplTest.java b/src/test/java/com/tasksprints/auction/domain/auction/service/AuctionServiceImplTest.java index c2f48a5b..8c5c204a 100644 --- a/src/test/java/com/tasksprints/auction/domain/auction/service/AuctionServiceImplTest.java +++ b/src/test/java/com/tasksprints/auction/domain/auction/service/AuctionServiceImplTest.java @@ -297,7 +297,7 @@ class GetAuctionsByAuctionCategoryTests { public void testGetAuctionsByAuctionCategory_Success() { Auction auction1 = createAuction(1L, seller, AuctionStatus.PENDING); Auction auction2 = createAuction(2L, seller, AuctionStatus.PENDING); - + AuctionRequest.AuctionCategoryParam param = new AuctionRequest.AuctionCategoryParam(AuctionCategory.PUBLIC_PAID); List expectedAuctions = List.of(auction1, auction2); when(auctionRepository.findAuctionsByAuctionCategory(AuctionCategory.PUBLIC_PAID)).thenReturn(expectedAuctions); @@ -305,28 +305,50 @@ public void testGetAuctionsByAuctionCategory_Success() { .map(AuctionResponse::of) .collect(Collectors.toList()); - List actualAuctions = auctionService.getAuctionsByAuctionCategory(AuctionCategory.PUBLIC_PAID); + List actualAuctions = auctionService.getAuctionsByAuctionCategory(param); + assertThat(actualAuctions).isEqualTo(expectedResponses); + } + @Test + @DisplayName("경매 유형 조회 : [성공] -Criteria 사용") + public void testGetAuctionsByAuctionCategory_Success_Criteria() { + Auction auction1 = createAuction(1L, seller, AuctionStatus.PENDING); + Auction auction2 = createAuction(2L, seller, AuctionStatus.PENDING); + AuctionRequest.AuctionCategoryParam param = new AuctionRequest.AuctionCategoryParam(AuctionCategory.PUBLIC_PAID); + List expectedAuctions = List.of(auction1, auction2); + + when(auctionRepository.getAuctionsByFilters(null,AuctionCategory.PUBLIC_PAID)).thenReturn(expectedAuctions); + List expectedResponses = expectedAuctions.stream() + .map(AuctionResponse::of) + .toList(); + + List actualAuctions = auctionService.getAuctionsByFilter(null,param); assertThat(actualAuctions).isEqualTo(expectedResponses); } @Test @DisplayName("경매 유형 조회 : [결과 없음]") public void testGetAuctionsByAuctionCategory_AuctionNotFound() { List emptyAuctionList = List.of(); + AuctionRequest.AuctionCategoryParam param = new AuctionRequest.AuctionCategoryParam(AuctionCategory.PUBLIC_FREE); when(auctionRepository.findAuctionsByAuctionCategory(AuctionCategory.PUBLIC_FREE)) .thenReturn(emptyAuctionList); - List actualAuctions = auctionService.getAuctionsByAuctionCategory(AuctionCategory.PUBLIC_FREE); + List actualAuctions = auctionService.getAuctionsByAuctionCategory(param); assertThat(actualAuctions).isEmpty(); + } + @Test + @DisplayName("경매 유형 조회 : [결과 없음] - Criteria 사용") + public void testGetAuctionsByAuctionCategory_AuctionNotFound_Criteria() { + List emptyAuctionList = List.of(); + AuctionRequest.AuctionCategoryParam param = new AuctionRequest.AuctionCategoryParam(AuctionCategory.PUBLIC_FREE); - //service 메서드 안에서 예외 처리를 하면 아래 코드로 테스트? -// assertThrows(AuctionNotFoundException.class, () -> { -// auctionService.getAuctionsByAuctionCategory(AuctionCategory.PUBLIC_FREE); -// }); - + when(auctionRepository.getAuctionsByFilters(null,AuctionCategory.PUBLIC_FREE)) + .thenReturn(emptyAuctionList); + List actualAuctions = auctionService.getAuctionsByFilter(null,param); + assertThat(actualAuctions).isEmpty(); } } diff --git a/src/test/java/com/tasksprints/auction/domain/product/ProductRepositoryTest.java b/src/test/java/com/tasksprints/auction/domain/product/ProductRepositoryTest.java index 2ecf8a61..5249c9c3 100644 --- a/src/test/java/com/tasksprints/auction/domain/product/ProductRepositoryTest.java +++ b/src/test/java/com/tasksprints/auction/domain/product/ProductRepositoryTest.java @@ -1,5 +1,6 @@ package com.tasksprints.auction.domain.product; +import com.tasksprints.auction.common.config.QueryDslConfig; import com.tasksprints.auction.domain.auction.model.Auction; import com.tasksprints.auction.domain.auction.model.AuctionCategory; import com.tasksprints.auction.domain.auction.model.AuctionStatus; @@ -13,6 +14,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; import java.math.BigDecimal; import java.time.LocalDateTime; @@ -23,6 +25,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; @DataJpaTest +@Import(QueryDslConfig.class) public class ProductRepositoryTest { @Autowired diff --git a/src/test/java/com/tasksprints/auction/domain/user/UserRepositoryTest.java b/src/test/java/com/tasksprints/auction/domain/user/UserRepositoryTest.java index 58d456ab..fdbf0aa4 100644 --- a/src/test/java/com/tasksprints/auction/domain/user/UserRepositoryTest.java +++ b/src/test/java/com/tasksprints/auction/domain/user/UserRepositoryTest.java @@ -1,5 +1,6 @@ package com.tasksprints.auction.domain.user; +import com.tasksprints.auction.common.config.QueryDslConfig; import com.tasksprints.auction.domain.user.model.User; import com.tasksprints.auction.domain.user.repository.UserRepository; import jakarta.transaction.Transactional; @@ -7,6 +8,7 @@ import org.junit.jupiter.api.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; import java.util.Optional; @@ -14,6 +16,7 @@ * [CRUD TEST] UserRepositoryTest */ @DataJpaTest +@Import(QueryDslConfig.class) @Slf4j public class UserRepositoryTest {