From 0c047874fda47b7eeb43f7cebec0a34043cc15e2 Mon Sep 17 00:00:00 2001 From: na0th <105775683+na0th@users.noreply.github.com> Date: Fri, 13 Sep 2024 16:51:28 +0900 Subject: [PATCH 1/9] =?UTF-8?q?feat=20:=20=EC=B5=9C=EC=8B=A0=EC=88=9C=20?= =?UTF-8?q?=EC=A0=95=EB=A0=AC=20=EA=B5=AC=ED=98=84(=EC=BF=BC=EB=A6=AC=20DT?= =?UTF-8?q?O=20=EB=B3=80=ED=99=98=20,=20=ED=8E=98=EC=9D=B4=EC=A7=95=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 최신순 정렬 구현 2: 쿼리에서 DTO로 변환 3. 페이징 처리 할 일 : 안 쓰는 메서드 삭제해야 함 조회수 순 구현 --- .../api/auction/AuctionController.java | 6 +- .../support/AuctionCriteriaRepository.java | 8 +- .../AuctionCriteriaRepositoryImpl.java | 146 ++++++++++++++++-- .../auction/service/AuctionService.java | 5 +- .../auction/service/AuctionServiceImpl.java | 17 +- 5 files changed, 157 insertions(+), 25 deletions(-) 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 2136a4f8..b2a5daaa 100644 --- a/src/main/java/com/tasksprints/auction/api/auction/AuctionController.java +++ b/src/main/java/com/tasksprints/auction/api/auction/AuctionController.java @@ -16,6 +16,8 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -80,8 +82,8 @@ public ResponseEntity> getAuctionStatus(@PathVariable Long auc @GetMapping @Operation(summary = "Get all auctions", description = "Retrieves all auctions.") @ApiResponse(responseCode = "200", description = "All auctions retrieved successfully") - public ResponseEntity>> getAllAuctions(AuctionRequest.SearchCondition searchCondition) { - List auctions = auctionService.getAuctionsByFilter(searchCondition); + public ResponseEntity>> getAllAuctions(Pageable pageable, AuctionRequest.SearchCondition searchCondition) { + Page auctions = auctionService.getAuctionsByFilter(pageable, searchCondition); return ResponseEntity.ok(ApiResult.success(ApiResponseMessages.ALL_AUCTIONS_RETRIEVED, auctions)); } diff --git a/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepository.java b/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepository.java index 3999b874..15610d19 100644 --- a/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepository.java +++ b/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepository.java @@ -1,10 +1,16 @@ package com.tasksprints.auction.domain.auction.repository.support; import com.tasksprints.auction.domain.auction.dto.request.AuctionRequest; +import com.tasksprints.auction.domain.auction.dto.response.AuctionResponse; import com.tasksprints.auction.domain.auction.model.Auction; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + import java.util.List; public interface AuctionCriteriaRepository { - List getAuctionsByFilters(AuctionRequest.SearchCondition searchCondition); + Page getAuctionsByFilters(Pageable pageable, AuctionRequest.SearchCondition searchCondition); +// Page findAllSortedByNewest(Pageable pageable); + } diff --git a/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java b/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java index 9db471af..0c953d78 100644 --- a/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java +++ b/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java @@ -2,40 +2,73 @@ import com.querydsl.core.BooleanBuilder; import com.querydsl.core.types.OrderSpecifier; +import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.jpa.impl.JPAQueryFactory; import com.tasksprints.auction.domain.auction.dto.request.AuctionRequest; +import com.tasksprints.auction.domain.auction.dto.response.AuctionResponse; import com.tasksprints.auction.domain.auction.model.Auction; +import com.tasksprints.auction.domain.auction.model.AuctionCategory; +import com.tasksprints.auction.domain.auction.model.AuctionStatus; import com.tasksprints.auction.domain.auction.model.QAuction; import com.tasksprints.auction.domain.product.model.QProduct; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Repository; import java.util.List; +import static com.tasksprints.auction.domain.auction.model.QAuction.auction; +import static com.tasksprints.auction.domain.product.model.QProduct.product; +import static com.tasksprints.auction.domain.user.model.QUser.user; + @RequiredArgsConstructor @Repository public class AuctionCriteriaRepositoryImpl implements AuctionCriteriaRepository { private final JPAQueryFactory queryFactory; - public List getAuctionsByFilters(AuctionRequest.SearchCondition condition) { - QAuction auction = QAuction.auction; - QProduct product = QProduct.product; - - BooleanBuilder builder = buildSearchCondition(condition, auction, product); + public Page getAuctionsByFilters(Pageable pageable, AuctionRequest.SearchCondition condition) { + BooleanBuilder builder = buildSearchCondition(condition); - var query = queryFactory.selectFrom(auction) - .leftJoin(auction.product, product) - .where(builder); + var query = queryFactory + .select(Projections.constructor(AuctionResponse.class, + auction.id, + auction.startTime, + auction.endTime, + auction.auctionCategory.stringValue(), + auction.auctionStatus.stringValue(), + auction.startingBid, + auction.seller.id.as("sellerId"), + auction.seller.nickName.as("sellerNickName") + )) + .from(auction) + .leftJoin(auction.product, product) + .leftJoin(auction.seller, user) + .where(builder); - OrderSpecifier sortOrder = getSortOrder(condition, auction); + OrderSpecifier sortOrder = getSortOrder(condition); if (sortOrder != null) { query = query.orderBy(sortOrder); } + // 페이징 + query = query.offset(pageable.getOffset()) + .limit(pageable.getPageSize()); + + List result = query.fetch(); - return query.fetch(); + //int 오버플로 주의 + int total = queryFactory + .selectFrom(auction) + .where(builder) + .fetch().size(); + + return new PageImpl<>(result, pageable, total); } - private BooleanBuilder buildSearchCondition(AuctionRequest.SearchCondition condition, QAuction auction, QProduct product) { + + private BooleanBuilder buildSearchCondition(AuctionRequest.SearchCondition condition) { BooleanBuilder builder = new BooleanBuilder(); if (condition.getAuctionCategory() != null) { @@ -57,15 +90,102 @@ private BooleanBuilder buildSearchCondition(AuctionRequest.SearchCondition condi return builder; } - private OrderSpecifier getSortOrder(AuctionRequest.SearchCondition condition, QAuction auction) { + private OrderSpecifier getSortOrder(AuctionRequest.SearchCondition condition) { if (condition.getSortBy() != null) { return switch (condition.getSortBy()) { case "bidsAsc" -> auction.bids.size().asc(); case "bidsDesc" -> auction.bids.size().desc(); - case "endingSoon" -> auction.endTime.asc(); + case "endTimeASC" -> auction.endTime.asc(); + case "startTimeASC" -> auction.startTime.asc(); +// case "viewCountDESC" -> auction.viewCount.desc(); + default -> null; }; } return null; } + +// @Override +// public Page findAllSortedByNewest(Pageable pageable) { +// +// List result = queryFactory +// .select( +// Projections.constructor(AuctionResponse.class, +// auction.id, +// auction.startTime, +// auction.endTime, +// auction.auctionCategory.stringValue(), +// auction.auctionStatus.stringValue(), +// auction.startingBid, +// auction.seller.id.as("sellerId"), +// auction.seller.nickName.as("sellerNickName") +// ) +// ) +// .from(auction) +// .join(auction.seller, user) +// //공개 경매 +// .where(auction.auctionCategory.eq(AuctionCategory.PUBLIC_PAID)) +// //경매 시작 전 +// .where(auction.auctionStatus.eq(AuctionStatus.PENDING)) +// //orderBy? +// .orderBy(auction.startTime.asc()) +// .offset(pageable.getOffset()) +// .limit(pageable.getPageSize()) +// .fetch(); +// +// //int 오버플로 주의 +// int total = queryFactory +// .select(auction) +// .from(auction) +// .where(auction.auctionCategory.eq(AuctionCategory.PUBLIC_PAID)) +// .where(auction.auctionStatus.eq(AuctionStatus.PENDING)) +// .fetch().size(); +// +// +// return new PageImpl<>(result, pageable, total); +// } +// +// public enum RatingSortType { +// +// VIEW_COUNT_DESC, +// START_TIME_ASC, +// END_TIME_DESC, +// ; +// +// } +// private BooleanBuilder createWhereClause(RatingSortType ratingSortType) { +// QAuction auction = QAuction.auction; +// BooleanBuilder builder = new BooleanBuilder(); +// +// switch (ratingSortType) { +//// case VIEW_COUNT_DESC: +//// null +// case START_TIME_ASC: +// // START_TIME_ASC에 대한 조건 추가 +// builder.and(auction.auctionStatus.eq(AuctionStatus.PENDING)); +// break; +// case END_TIME_DESC: +// // END_TIME_DESC에 대한 조건 추가 +// builder.and(auction.auctionStatus.eq(AuctionStatus.ACTIVE)); +// break; +// default: +// //default 정렬은 END_TIME_DESC +// builder.and(auction.auctionStatus.eq(AuctionStatus.ACTIVE)); +// break; +// } +// +// return builder; +// } +// private OrderSpecifier createSpecifier(RatingSortType ratingSortType) { +//// if (ratingSortType.equals(VIEW_COUNT_DESC)) { +//// return auction.viewCount.desc(); +//// } +// if (ratingSortType.equals(START_TIME_ASC)) { +// return auction.startTime.asc(); +// } +// +// return auction.endTime.desc(); +// } + + } 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 788d1ddb..791697e7 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 @@ -2,6 +2,8 @@ import com.tasksprints.auction.domain.auction.dto.request.AuctionRequest; import com.tasksprints.auction.domain.auction.dto.response.AuctionResponse; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import java.util.List; @@ -21,6 +23,7 @@ public interface AuctionService { AuctionResponse getAuctionById(Long auctionId); - List getAuctionsByFilter(AuctionRequest.SearchCondition searchCondition); + Page getAuctionsByFilter(Pageable pageable, AuctionRequest.SearchCondition searchCondition); +// Page getAuctionsSortedByNewest(Pageable pageable); } 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 8cfef088..6c8e9806 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 @@ -13,6 +13,8 @@ import com.tasksprints.auction.domain.user.repository.UserRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import java.util.List; @@ -106,13 +108,12 @@ public AuctionResponse getAuctionById(Long auctionId) { * NULL 안정성 보장을 해줬음 **/ @Override - public List getAuctionsByFilter(AuctionRequest.SearchCondition searchCondition) { - List foundAuctions = auctionRepository.getAuctionsByFilters( - searchCondition - ); - - return foundAuctions.stream() - .map(AuctionResponse::of) - .toList(); + public Page getAuctionsByFilter(Pageable pageable, AuctionRequest.SearchCondition searchCondition) { + return auctionRepository.getAuctionsByFilters(pageable, searchCondition); } + +// @Override +// public Page getAuctionsSortedByNewest(Pageable pageable) { +// return auctionRepository.findAllSortedByNewest(pageable); +// } } From 76d6758756f14a42d71345deae45b3815ae43069 Mon Sep 17 00:00:00 2001 From: na0th <105775683+na0th@users.noreply.github.com> Date: Fri, 13 Sep 2024 16:56:53 +0900 Subject: [PATCH 2/9] =?UTF-8?q?test=20:=20=ED=8E=98=EC=9D=B4=EC=A7=95,=20?= =?UTF-8?q?=EC=A0=95=EB=A0=AC=20=EC=BF=BC=EB=A6=AC=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=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 1 : 페이징, 정렬 처리에 맞게 테스트 수정 --- .../auction/api/AuctionControllerTest.java | 8 +- .../repository/AuctionRepositoryTest.java | 67 +++++----- .../service/AuctionServiceImplTest.java | 118 ++++++++++-------- 3 files changed, 112 insertions(+), 81 deletions(-) diff --git a/src/test/java/com/tasksprints/auction/api/AuctionControllerTest.java b/src/test/java/com/tasksprints/auction/api/AuctionControllerTest.java index e6456531..cac0b8c4 100644 --- a/src/test/java/com/tasksprints/auction/api/AuctionControllerTest.java +++ b/src/test/java/com/tasksprints/auction/api/AuctionControllerTest.java @@ -19,6 +19,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; @@ -115,7 +119,9 @@ public void testGetAuctionStatus_Success() throws Exception { public void testFindAuctionByUsingQueryString_Success() throws Exception { // Given List auctionResponseList = new ArrayList<>(); - when(auctionService.getAuctionsByFilter(any())).thenReturn(auctionResponseList); + Pageable pageable = PageRequest.of(0, 10); + Page pageAuctionResponse = new PageImpl<>(auctionResponseList, pageable, 0); + when(auctionService.getAuctionsByFilter(any(),any())).thenReturn(pageAuctionResponse); // When & Then mockMvc.perform(get("/api/v1/auction") 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 1455d8b5..fef6b25b 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,6 +1,7 @@ package com.tasksprints.auction.domain.auction.repository; import com.tasksprints.auction.common.config.QueryDslConfig; +import com.tasksprints.auction.domain.auction.dto.response.AuctionResponse; import com.tasksprints.auction.domain.auction.dto.request.AuctionRequest; import com.tasksprints.auction.domain.auction.model.Auction; import com.tasksprints.auction.domain.auction.model.AuctionCategory; @@ -14,6 +15,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.Import; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import java.math.BigDecimal; import java.time.LocalDateTime; @@ -58,6 +62,17 @@ private Auction createAuction(User seller, AuctionCategory category, AuctionStat ); } + private Auction createAuction(User seller, LocalDateTime startTime) { + return Auction.create( + startTime, + LocalDateTime.of(2024, 9, 1, 10, 0), + BigDecimal.valueOf(100.00), + AuctionCategory.PUBLIC_PAID, + AuctionStatus.PENDING, + seller + ); + } + @Test @DisplayName("사용자 ID로 경매 목록 조회") public void testFindAuctionsByUserId() { @@ -110,12 +125,14 @@ public void testFindAuctionsByAuctionCategory() { auctionRepository.save(auction2); auctionRepository.save(auction3); + Pageable pageable = PageRequest.of(0, 10); + //when - List auctions = auctionRepository.getAuctionsByFilters(condition); + Page auctions = auctionRepository.getAuctionsByFilters(pageable, condition); //then assertThat(auctions).hasSize(2); - assertThat(auctions).allMatch(auction -> auction.getAuctionCategory() == AuctionCategory.PUBLIC_PAID); + assertThat(auctions.getContent()).allMatch(auction -> auction.getCategory().equals(AuctionCategory.PUBLIC_PAID.name())); } @Test @@ -127,50 +144,38 @@ public void testFindAllUsingFilter() { auctionRepository.save(auction1); auctionRepository.save(auction2); - List auctions = auctionRepository.getAuctionsByFilters(condition); + Pageable pageable = PageRequest.of(0, 10); + Page auctions = auctionRepository.getAuctionsByFilters(pageable, condition); 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); + assertThat(auctions.getContent().get(0).getCategory()).isEqualTo(AuctionCategory.PUBLIC_FREE.name()); + assertThat(auctions.getContent().get(0).getCategory()).isNotEqualTo(AuctionCategory.PUBLIC_PAID.name()); } // @Test -// @DisplayName("경매 마감 시간까지 24시간 이하로 남은 경매 목록 조회") -// public void testFindAuctionsByEndTimeBetweenOrderByEndTimeAsc() { +// @DisplayName("경매 목록 최신 순 조회") +// public void testFindAllSortedByNewest() { // //given -// LocalDateTime fixedNow = LocalDateTime.of(2024, 9, 1, 10, 0); -// LocalDateTime next24Hours = fixedNow.plusHours(24); +// Auction auction1 = createAuction(seller, LocalDateTime.of(2024, 8, 2, 10, 0)); +// Auction auction2 = createAuction(seller, LocalDateTime.of(2024, 8, 1, 10, 0)); // -// List auctions = List.of( -// createAuction(fixedNow.plusHours(23), AuctionStatus.ACTIVE), -// createAuction(fixedNow.plusHours(22), AuctionStatus.ACTIVE), -// createAuction(fixedNow.plusHours(21), AuctionStatus.ACTIVE), -// createAuction(fixedNow.plusHours(48), AuctionStatus.ACTIVE), -// createAuction(fixedNow.plusHours(20), AuctionStatus.PENDING) -// ); +// auctionRepository.saveAll(List.of(auction1, auction2)); // -// auctionRepository.saveAll(auctions); +// Pageable pageable = PageRequest.of(0, 10); // -// // when -// List result = auctionRepository.getAuctionsEndWith24Hours(fixedNow, next24Hours, AuctionStatus.ACTIVE); +// //when +// Page auctions = auctionRepository.findAllSortedByNewest(pageable); // // //then -// assertThat(result).hasSize(3); +// assertThat(auctions).hasSize(2); +// +// assertThat(auctions.getContent().get(0).getStartTime()).isEqualTo(auction2.getStartTime()); +// assertThat(auctions.getContent().get(1).getStartTime()).isEqualTo(auction1.getStartTime()); +// // -// assertAll("endTime을 기준으로 오름차순 정렬이 되었는지 확인", -// () -> { -// LocalDateTime previousEndTime = null; -// for (Auction auction : result) { -// if (previousEndTime != null) { -// assertThat(auction.getEndTime()).isAfterOrEqualTo(previousEndTime); -// } -// previousEndTime = auction.getEndTime(); -// } -// } -// ); // } } 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 224ac20c..759537a5 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 @@ -20,6 +20,10 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import java.math.BigDecimal; import java.time.LocalDateTime; @@ -27,6 +31,7 @@ import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @@ -68,13 +73,13 @@ private Auction createAuction(Long auctionId, User seller, AuctionStatus status) .build(); } - private Auction createAuction(Long auctionId, LocalDateTime endTime, AuctionStatus status) { + private Auction createAuction(Long auctionId, LocalDateTime startTime) { return Auction.builder() .id(auctionId) .auctionCategory(AuctionCategory.PUBLIC_PAID) - .auctionStatus(status) - .startTime(LocalDateTime.of(2024, 8, 1, 10, 0)) - .endTime(endTime) + .auctionStatus(AuctionStatus.ACTIVE) + .startTime(startTime) + .endTime(LocalDateTime.of(2024, 9, 1, 10, 0)) .startingBid(BigDecimal.valueOf(100.00)) .seller(seller) .build(); @@ -318,94 +323,109 @@ class GetAuctionsByAuctionCategoryTests { @Test @DisplayName("경매 유형 조회 : [성공]") public void testGetAuctionsByAuctionCategory_Success() { - Auction auction1 = createAuction(1L, seller, AuctionStatus.PENDING); - Auction auction2 = createAuction(2L, seller, AuctionStatus.PENDING); AuctionRequest.SearchCondition condition = new AuctionRequest.SearchCondition(AuctionCategory.PUBLIC_PAID, null, null, null, null, null, null, null); - List expectedAuctions = List.of(auction1, auction2); - when(auctionRepository.getAuctionsByFilters(condition)).thenReturn(expectedAuctions); - List expectedResponses = expectedAuctions.stream() + List auctions = List.of( + createAuction(1L, seller, AuctionStatus.PENDING), + createAuction(2L, seller, AuctionStatus.PENDING) + ); + List auctionResponses = auctions + .stream() .map(AuctionResponse::of) .toList(); - List actualAuctions = auctionService.getAuctionsByFilter(condition); - assertThat(actualAuctions).isEqualTo(expectedResponses); + Pageable pageable = PageRequest.of(0, 10); + Page expectedPage = new PageImpl<>(auctionResponses, pageable, auctionResponses.size()); + + when(auctionRepository.getAuctionsByFilters(pageable, condition)).thenReturn(expectedPage); + + Page actualAuctions = auctionService.getAuctionsByFilter(pageable, condition); + assertThat(actualAuctions).isEqualTo(expectedPage); } @Test @DisplayName("경매 유형 조회 : [성공] -Criteria 사용") public void testGetAuctionsByAuctionCategory_Success_Criteria() { - Auction auction1 = createAuction(1L, seller, AuctionStatus.PENDING); - Auction auction2 = createAuction(2L, seller, AuctionStatus.PENDING); AuctionRequest.SearchCondition condition = new AuctionRequest.SearchCondition(AuctionCategory.PUBLIC_PAID, null, null, null, null, null, null, null); - List expectedAuctions = List.of(auction1, auction2); - when(auctionRepository.getAuctionsByFilters(condition)).thenReturn(expectedAuctions); - List expectedResponses = expectedAuctions.stream() + List auctions = List.of( + createAuction(1L, seller, AuctionStatus.PENDING), + createAuction(2L, seller, AuctionStatus.PENDING) + ); + + List auctionResponses = auctions + .stream() .map(AuctionResponse::of) .toList(); - List actualAuctions = auctionService.getAuctionsByFilter(condition); - assertThat(actualAuctions).isEqualTo(expectedResponses); + Pageable pageable = PageRequest.of(0, 10); + Page expectedPage = new PageImpl<>(auctionResponses, pageable, auctionResponses.size()); + + when(auctionRepository.getAuctionsByFilters(pageable, condition)).thenReturn(expectedPage); + + Page actualAuctions = auctionService.getAuctionsByFilter(pageable, condition); + assertThat(actualAuctions).isEqualTo(expectedPage); } @Test @DisplayName("경매 유형 조회 : [결과 없음]") public void testGetAuctionsByAuctionCategory_AuctionNotFound() { - List emptyAuctionList = List.of(); + List emptyAuctionList = List.of(); AuctionRequest.SearchCondition condition = new AuctionRequest.SearchCondition(AuctionCategory.PUBLIC_FREE, null, null, null, null, null, null, null); - when(auctionRepository.getAuctionsByFilters(condition)) - .thenReturn(emptyAuctionList); + Pageable pageable = PageRequest.of(0, 10); + Page expectedPage = new PageImpl<>(emptyAuctionList, pageable, 0); + when(auctionRepository.getAuctionsByFilters(pageable, condition)) + .thenReturn(expectedPage); - List actualAuctions = auctionService.getAuctionsByFilter(condition); + Page actualAuctions = auctionService.getAuctionsByFilter(pageable, condition); - assertThat(actualAuctions).isEmpty(); + assertThat(actualAuctions.isEmpty()).isTrue(); } @Test @DisplayName("경매 유형 조회 : [결과 없음] - Criteria 사용") public void testGetAuctionsByAuctionCategory_AuctionNotFound_Criteria() { - List emptyAuctionList = List.of(); - AuctionRequest.SearchCondition condition = new AuctionRequest.SearchCondition(AuctionCategory.PUBLIC_PAID, null, null, null, null, null, null, null); + List emptyAuctionList = List.of(); + AuctionRequest.SearchCondition condition = new AuctionRequest.SearchCondition(AuctionCategory.PUBLIC_FREE, null, null, null, null, null, null, null); - when(auctionRepository.getAuctionsByFilters(condition)) - .thenReturn(emptyAuctionList); + Pageable pageable = PageRequest.of(0, 10); + Page expectedPage = new PageImpl<>(emptyAuctionList, pageable, 0); + when(auctionRepository.getAuctionsByFilters(pageable, condition)) + .thenReturn(expectedPage); - List actualAuctions = auctionService.getAuctionsByFilter(condition); + Page actualAuctions = auctionService.getAuctionsByFilter(pageable, condition); - assertThat(actualAuctions).isEmpty(); + assertThat(actualAuctions.isEmpty()).isTrue(); } } - // @Nested -// @DisplayName("경매 마감 시간이 24시간 이하인 경매 목록 조회") -// class GetAuctionsByEndTimeBetweenAndAuctionStatusOrderByEndTimeAscTests { +// @DisplayName("경매 목록 최신 순 조회") +// class GetAuctionsSortedByNewestTests { // @Test -// @DisplayName("경매 마감 임박 목록 조회 : 성공") -// public void testGetAuctionsByEndTimeBetweenAndAuctionStatusOrderByEndTimeAsc_Success() { +// @DisplayName("경매 목록 최신 순 조회 성공") +// void testGetAuctionsSortedByNewest_Success() { // //given -// LocalDateTime fixedNow = LocalDateTime.of(2024, 9, 1, 10, 0); -// LocalDateTime next24Hours = fixedNow.plusHours(24); -// -// List expectedAuctions = List.of(createAuction(1L, fixedNow.plusHours(21), AuctionStatus.ACTIVE), -// createAuction(2L, fixedNow.plusHours(22), AuctionStatus.ACTIVE), -// createAuction(3L, fixedNow.plusHours(23), AuctionStatus.ACTIVE) +// List auctions = List.of( +// createAuction(1L, LocalDateTime.of(2024, 8, 1, 9, 0)), +// createAuction(2L, LocalDateTime.of(2024, 8, 1, 10, 0)) // ); // -// List expectedResponses = expectedAuctions.stream() -// .map(AuctionResponse::of) -// .toList(); +// List auctionResponses = auctions.stream() +// .map(AuctionResponse::of) +// .toList(); // -// when(auctionRepository.getAuctionsEndWith24Hours(fixedNow, next24Hours, AuctionStatus.ACTIVE)).thenReturn(expectedAuctions); -// List actualResponses = auctionService.getAuctionsEndWith24Hours(fixedNow, next24Hours); +// Pageable pageable = PageRequest.of(0, 10); +// Page expectedPage = new PageImpl<>(auctionResponses, pageable, auctionResponses.size()); // +// //when +// when(auctionRepository.findAllSortedByNewest(pageable)).thenReturn(expectedPage); +// Page actualPage = auctionService.getAuctionsSortedByNewest(pageable); // //then -// assertThat(actualResponses).isEqualTo(expectedResponses); -// +// assertThat(actualPage).isEqualTo(expectedPage); // // } +// +// // } - - } From de8d180d1183ad0c3680f23efed65f012912d08f Mon Sep 17 00:00:00 2001 From: na0th <105775683+na0th@users.noreply.github.com> Date: Fri, 13 Sep 2024 17:01:16 +0900 Subject: [PATCH 3/9] =?UTF-8?q?delete=20:=20=EC=95=88=20=EC=93=B0=EB=8A=94?= =?UTF-8?q?=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 구현 전에 만들었던 메서드들 삭제 --- .../support/AuctionCriteriaRepository.java | 3 - .../AuctionCriteriaRepositoryImpl.java | 88 ------------------- .../auction/service/AuctionService.java | 1 - .../auction/service/AuctionServiceImpl.java | 4 - .../repository/AuctionRepositoryTest.java | 23 ----- .../service/AuctionServiceImplTest.java | 31 +------ 6 files changed, 1 insertion(+), 149 deletions(-) diff --git a/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepository.java b/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepository.java index 15610d19..ff46d5ee 100644 --- a/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepository.java +++ b/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepository.java @@ -2,15 +2,12 @@ import com.tasksprints.auction.domain.auction.dto.request.AuctionRequest; import com.tasksprints.auction.domain.auction.dto.response.AuctionResponse; -import com.tasksprints.auction.domain.auction.model.Auction; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import java.util.List; public interface AuctionCriteriaRepository { Page getAuctionsByFilters(Pageable pageable, AuctionRequest.SearchCondition searchCondition); -// Page findAllSortedByNewest(Pageable pageable); } diff --git a/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java b/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java index 0c953d78..4dcfe270 100644 --- a/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java +++ b/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java @@ -3,15 +3,9 @@ import com.querydsl.core.BooleanBuilder; import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.Projections; -import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.jpa.impl.JPAQueryFactory; import com.tasksprints.auction.domain.auction.dto.request.AuctionRequest; import com.tasksprints.auction.domain.auction.dto.response.AuctionResponse; -import com.tasksprints.auction.domain.auction.model.Auction; -import com.tasksprints.auction.domain.auction.model.AuctionCategory; -import com.tasksprints.auction.domain.auction.model.AuctionStatus; -import com.tasksprints.auction.domain.auction.model.QAuction; -import com.tasksprints.auction.domain.product.model.QProduct; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; @@ -105,87 +99,5 @@ private OrderSpecifier getSortOrder(AuctionRequest.SearchCondition condition) return null; } -// @Override -// public Page findAllSortedByNewest(Pageable pageable) { -// -// List result = queryFactory -// .select( -// Projections.constructor(AuctionResponse.class, -// auction.id, -// auction.startTime, -// auction.endTime, -// auction.auctionCategory.stringValue(), -// auction.auctionStatus.stringValue(), -// auction.startingBid, -// auction.seller.id.as("sellerId"), -// auction.seller.nickName.as("sellerNickName") -// ) -// ) -// .from(auction) -// .join(auction.seller, user) -// //공개 경매 -// .where(auction.auctionCategory.eq(AuctionCategory.PUBLIC_PAID)) -// //경매 시작 전 -// .where(auction.auctionStatus.eq(AuctionStatus.PENDING)) -// //orderBy? -// .orderBy(auction.startTime.asc()) -// .offset(pageable.getOffset()) -// .limit(pageable.getPageSize()) -// .fetch(); -// -// //int 오버플로 주의 -// int total = queryFactory -// .select(auction) -// .from(auction) -// .where(auction.auctionCategory.eq(AuctionCategory.PUBLIC_PAID)) -// .where(auction.auctionStatus.eq(AuctionStatus.PENDING)) -// .fetch().size(); -// -// -// return new PageImpl<>(result, pageable, total); -// } -// -// public enum RatingSortType { -// -// VIEW_COUNT_DESC, -// START_TIME_ASC, -// END_TIME_DESC, -// ; -// -// } -// private BooleanBuilder createWhereClause(RatingSortType ratingSortType) { -// QAuction auction = QAuction.auction; -// BooleanBuilder builder = new BooleanBuilder(); -// -// switch (ratingSortType) { -//// case VIEW_COUNT_DESC: -//// null -// case START_TIME_ASC: -// // START_TIME_ASC에 대한 조건 추가 -// builder.and(auction.auctionStatus.eq(AuctionStatus.PENDING)); -// break; -// case END_TIME_DESC: -// // END_TIME_DESC에 대한 조건 추가 -// builder.and(auction.auctionStatus.eq(AuctionStatus.ACTIVE)); -// break; -// default: -// //default 정렬은 END_TIME_DESC -// builder.and(auction.auctionStatus.eq(AuctionStatus.ACTIVE)); -// break; -// } -// -// return builder; -// } -// private OrderSpecifier createSpecifier(RatingSortType ratingSortType) { -//// if (ratingSortType.equals(VIEW_COUNT_DESC)) { -//// return auction.viewCount.desc(); -//// } -// if (ratingSortType.equals(START_TIME_ASC)) { -// return auction.startTime.asc(); -// } -// -// return auction.endTime.desc(); -// } - } 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 791697e7..7239d723 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 @@ -25,5 +25,4 @@ public interface AuctionService { Page getAuctionsByFilter(Pageable pageable, AuctionRequest.SearchCondition searchCondition); -// Page getAuctionsSortedByNewest(Pageable pageable); } 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 6c8e9806..e01256f5 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 @@ -112,8 +112,4 @@ public Page getAuctionsByFilter(Pageable pageable, AuctionReque return auctionRepository.getAuctionsByFilters(pageable, searchCondition); } -// @Override -// public Page getAuctionsSortedByNewest(Pageable pageable) { -// return auctionRepository.findAllSortedByNewest(pageable); -// } } 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 fef6b25b..a076b032 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 @@ -155,27 +155,4 @@ public void testFindAllUsingFilter() { } -// @Test -// @DisplayName("경매 목록 최신 순 조회") -// public void testFindAllSortedByNewest() { -// //given -// Auction auction1 = createAuction(seller, LocalDateTime.of(2024, 8, 2, 10, 0)); -// Auction auction2 = createAuction(seller, LocalDateTime.of(2024, 8, 1, 10, 0)); -// -// auctionRepository.saveAll(List.of(auction1, auction2)); -// -// Pageable pageable = PageRequest.of(0, 10); -// -// //when -// Page auctions = auctionRepository.findAllSortedByNewest(pageable); -// -// //then -// assertThat(auctions).hasSize(2); -// -// assertThat(auctions.getContent().get(0).getStartTime()).isEqualTo(auction2.getStartTime()); -// assertThat(auctions.getContent().get(1).getStartTime()).isEqualTo(auction1.getStartTime()); -// -// -// } - } 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 759537a5..9d14857c 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 @@ -31,7 +31,6 @@ import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @@ -399,33 +398,5 @@ public void testGetAuctionsByAuctionCategory_AuctionNotFound_Criteria() { assertThat(actualAuctions.isEmpty()).isTrue(); } } -// @Nested -// @DisplayName("경매 목록 최신 순 조회") -// class GetAuctionsSortedByNewestTests { -// @Test -// @DisplayName("경매 목록 최신 순 조회 성공") -// void testGetAuctionsSortedByNewest_Success() { -// //given -// List auctions = List.of( -// createAuction(1L, LocalDateTime.of(2024, 8, 1, 9, 0)), -// createAuction(2L, LocalDateTime.of(2024, 8, 1, 10, 0)) -// ); -// -// List auctionResponses = auctions.stream() -// .map(AuctionResponse::of) -// .toList(); -// -// Pageable pageable = PageRequest.of(0, 10); -// Page expectedPage = new PageImpl<>(auctionResponses, pageable, auctionResponses.size()); -// -// //when -// when(auctionRepository.findAllSortedByNewest(pageable)).thenReturn(expectedPage); -// Page actualPage = auctionService.getAuctionsSortedByNewest(pageable); -// //then -// assertThat(actualPage).isEqualTo(expectedPage); -// -// } -// -// -// } + } From 4de3fd9a771613ef647d4bfacf9f05fbdb0ec524 Mon Sep 17 00:00:00 2001 From: na0th <105775683+na0th@users.noreply.github.com> Date: Sat, 14 Sep 2024 01:21:35 +0900 Subject: [PATCH 4/9] =?UTF-8?q?feat=20:=20=EC=A1=B0=ED=9A=8C=EC=88=98=20?= =?UTF-8?q?=EC=A0=95=EB=A0=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 조회수 정렬 추가 --- .../auction/dto/response/AuctionResponse.java | 3 ++- .../auction/domain/auction/model/Auction.java | 17 ++++++++++++++++- .../support/AuctionCriteriaRepositoryImpl.java | 3 ++- .../auction/service/AuctionServiceImpl.java | 3 ++- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/tasksprints/auction/domain/auction/dto/response/AuctionResponse.java b/src/main/java/com/tasksprints/auction/domain/auction/dto/response/AuctionResponse.java index ff4356db..b8115c28 100644 --- a/src/main/java/com/tasksprints/auction/domain/auction/dto/response/AuctionResponse.java +++ b/src/main/java/com/tasksprints/auction/domain/auction/dto/response/AuctionResponse.java @@ -20,7 +20,7 @@ public class AuctionResponse { private String category; private String status; private BigDecimal startingBid; - + private Long viewCount; private Long sellerId; private String sellerNickName; @@ -33,6 +33,7 @@ public static AuctionResponse of(Auction auction) { .category(auction.getAuctionCategory().name()) .status(auction.getAuctionStatus().name()) .startingBid(auction.getStartingBid()) + .viewCount(auction.getViewCount()) .sellerId(auction.getSeller().getId()) .sellerNickName(auction.getSeller().getNickName()) .build(); diff --git a/src/main/java/com/tasksprints/auction/domain/auction/model/Auction.java b/src/main/java/com/tasksprints/auction/domain/auction/model/Auction.java index 320b5078..572e4ba9 100644 --- a/src/main/java/com/tasksprints/auction/domain/auction/model/Auction.java +++ b/src/main/java/com/tasksprints/auction/domain/auction/model/Auction.java @@ -6,6 +6,7 @@ import com.tasksprints.auction.domain.user.model.User; import jakarta.persistence.*; import lombok.*; +import org.hibernate.annotations.ColumnDefault; import java.math.BigDecimal; import java.time.LocalDateTime; @@ -50,11 +51,20 @@ public class Auction extends BaseEntity { @Builder.Default private Product product = null; - @OneToMany @Builder.Default private List bids = new ArrayList<>(); + @Column(nullable = false) + private Long viewCount; + + @PrePersist + protected void onCreate() { + if (viewCount == null) { + viewCount = 0L; // 기본값 설정 + } + } + public static Auction create(LocalDateTime startTime, LocalDateTime endTime, BigDecimal startingBid, AuctionCategory auctionCategory, AuctionStatus auctionStatus, User seller) { Auction newAuction = Auction.builder() .startTime(startTime) @@ -77,4 +87,9 @@ public void addUser(User seller) { this.seller = seller; } + public void incrementViewCount() { + this.viewCount += 1; + } + + } diff --git a/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java b/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java index 4dcfe270..68e75fc1 100644 --- a/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java +++ b/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java @@ -34,6 +34,7 @@ public Page getAuctionsByFilters(Pageable pageable, AuctionRequ auction.auctionCategory.stringValue(), auction.auctionStatus.stringValue(), auction.startingBid, + auction.viewCount, auction.seller.id.as("sellerId"), auction.seller.nickName.as("sellerNickName") )) @@ -91,7 +92,7 @@ private OrderSpecifier getSortOrder(AuctionRequest.SearchCondition condition) case "bidsDesc" -> auction.bids.size().desc(); case "endTimeASC" -> auction.endTime.asc(); case "startTimeASC" -> auction.startTime.asc(); -// case "viewCountDESC" -> auction.viewCount.desc(); + case "viewCountDESC" -> auction.viewCount.desc(); default -> null; }; 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 e01256f5..756d5df9 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 @@ -99,7 +99,8 @@ public List getAllAuctions() { public AuctionResponse getAuctionById(Long auctionId) { Auction foundAuction = auctionRepository.findAuctionById(auctionId) .orElseThrow(() -> new AuctionNotFoundException("Auction not found")); - + foundAuction.incrementViewCount(); + auctionRepository.save(foundAuction); return AuctionResponse.of(foundAuction); } From c25864f4c5cf1376075c5074c98978d878d04131 Mon Sep 17 00:00:00 2001 From: na0th <105775683+na0th@users.noreply.github.com> Date: Sat, 14 Sep 2024 10:34:44 +0900 Subject: [PATCH 5/9] =?UTF-8?q?fix=20:=20viewCount=EA=B0=80=20null=20?= =?UTF-8?q?=EC=9D=BC=20=EB=95=8C=20=EB=82=98=EB=8A=94=20=EC=97=90=EB=9F=AC?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 경매 ID로 조회 테스트 성공: 에서 나던 nullPointerException 해결 --- .../com/tasksprints/auction/domain/auction/model/Auction.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/tasksprints/auction/domain/auction/model/Auction.java b/src/main/java/com/tasksprints/auction/domain/auction/model/Auction.java index 572e4ba9..486699f8 100644 --- a/src/main/java/com/tasksprints/auction/domain/auction/model/Auction.java +++ b/src/main/java/com/tasksprints/auction/domain/auction/model/Auction.java @@ -88,6 +88,9 @@ public void addUser(User seller) { } public void incrementViewCount() { + if (viewCount == null) { + viewCount = 0L; + } this.viewCount += 1; } From 27679317249b7d5308032177501d39b65c67e0d6 Mon Sep 17 00:00:00 2001 From: na0th <105775683+na0th@users.noreply.github.com> Date: Tue, 24 Sep 2024 20:23:11 +0900 Subject: [PATCH 6/9] =?UTF-8?q?refactor=20:=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=ED=95=84=ED=84=B0=20=EC=BF=BC=EB=A6=AC=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 검색 필터 쿼리 리팩토링 2. 상품 카테고리 엔드포인트 분리(Deprecated 처리) 3. Product를 create Auction쪽에 Product 매핑(양방향 매핑) 4. 테스트 코드 추가 미흡 : - 쿼리스트링 부분 없는 값 예외처리 추가 필요 - AuctionResponse에 productCategory null값으로 넘어오는 거 처리 필요 --- .../api/auction/AuctionController.java | 10 ++ .../auction/dto/response/AuctionResponse.java | 1 + .../auction/domain/auction/model/Auction.java | 2 +- .../support/AuctionCriteriaRepository.java | 6 +- .../AuctionCriteriaRepositoryImpl.java | 116 ++++++++++++++---- .../auction/service/AuctionService.java | 3 + .../auction/service/AuctionServiceImpl.java | 7 ++ .../auction/domain/product/model/Product.java | 3 + .../repository/AuctionRepositoryTest.java | 47 +++++-- .../domain/product/ProductRepositoryTest.java | 88 ++++++++++++- 10 files changed, 249 insertions(+), 34 deletions(-) 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 b2a5daaa..d7cf9dde 100644 --- a/src/main/java/com/tasksprints/auction/api/auction/AuctionController.java +++ b/src/main/java/com/tasksprints/auction/api/auction/AuctionController.java @@ -7,6 +7,7 @@ import com.tasksprints.auction.domain.auction.service.AuctionService; import com.tasksprints.auction.domain.bid.dto.BidResponse; import com.tasksprints.auction.domain.bid.service.BidService; +import com.tasksprints.auction.domain.product.model.ProductCategory; import com.tasksprints.auction.domain.review.dto.request.ReviewRequest; import com.tasksprints.auction.domain.review.dto.response.ReviewResponse; import com.tasksprints.auction.domain.review.service.ReviewService; @@ -95,6 +96,15 @@ public ResponseEntity> getAuctionById(@PathVariable L return ResponseEntity.ok(ApiResult.success(ApiResponseMessages.AUCTION_RETRIEVED, auction)); } + @Deprecated + @GetMapping("/category/{category}") + @Operation(summary = "Get auctions by ProductCategory", description = "Retrieve all auction by its ProductCategory.") + @ApiResponse(responseCode = "200", description = "All auctions retrieved successfully") + public ResponseEntity>> getAuctionByProductCategory(Pageable pageable, @PathVariable String category, AuctionRequest.SearchCondition searchCondition) { + Page auctions = auctionService.getAuctionsByProductCategory(pageable, searchCondition, ProductCategory.fromDisplayName(category)); + return ResponseEntity.ok(ApiResult.success(ApiResponseMessages.AUCTION_RETRIEVED, auctions)); + } + // Bid Endpoints @PostMapping("/{auctionId}/bid") @Operation(summary = "Submit a bid", description = "Submits a bid for the specified auction.") diff --git a/src/main/java/com/tasksprints/auction/domain/auction/dto/response/AuctionResponse.java b/src/main/java/com/tasksprints/auction/domain/auction/dto/response/AuctionResponse.java index b8115c28..6dde13c6 100644 --- a/src/main/java/com/tasksprints/auction/domain/auction/dto/response/AuctionResponse.java +++ b/src/main/java/com/tasksprints/auction/domain/auction/dto/response/AuctionResponse.java @@ -23,6 +23,7 @@ public class AuctionResponse { private Long viewCount; private Long sellerId; private String sellerNickName; + private String productCategory; public static AuctionResponse of(Auction auction) { diff --git a/src/main/java/com/tasksprints/auction/domain/auction/model/Auction.java b/src/main/java/com/tasksprints/auction/domain/auction/model/Auction.java index 486699f8..c141b035 100644 --- a/src/main/java/com/tasksprints/auction/domain/auction/model/Auction.java +++ b/src/main/java/com/tasksprints/auction/domain/auction/model/Auction.java @@ -78,7 +78,7 @@ public static Auction create(LocalDateTime startTime, LocalDateTime endTime, Big } public void addProduct(Product product) { - product.addAuction(this); + //product.addAuction(this); product에서 auction을 추가하고 있어서 중복 this.product = product; } diff --git a/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepository.java b/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepository.java index ff46d5ee..37bba68b 100644 --- a/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepository.java +++ b/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepository.java @@ -3,11 +3,15 @@ import com.tasksprints.auction.domain.auction.dto.request.AuctionRequest; import com.tasksprints.auction.domain.auction.dto.response.AuctionResponse; +import com.tasksprints.auction.domain.product.model.ProductCategory; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; public interface AuctionCriteriaRepository { Page getAuctionsByFilters(Pageable pageable, AuctionRequest.SearchCondition searchCondition); - + @Deprecated + Page getAuctionsByCategory(Pageable pageable, + AuctionRequest.SearchCondition searchCondition, + ProductCategory category); } diff --git a/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java b/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java index 68e75fc1..e4f2bc6d 100644 --- a/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java +++ b/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java @@ -3,9 +3,11 @@ import com.querydsl.core.BooleanBuilder; import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.Projections; +import com.querydsl.jpa.impl.JPAQuery; import com.querydsl.jpa.impl.JPAQueryFactory; import com.tasksprints.auction.domain.auction.dto.request.AuctionRequest; import com.tasksprints.auction.domain.auction.dto.response.AuctionResponse; +import com.tasksprints.auction.domain.product.model.ProductCategory; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; @@ -23,43 +25,115 @@ public class AuctionCriteriaRepositoryImpl implements AuctionCriteriaRepository { private final JPAQueryFactory queryFactory; +// public Page getAuctionsByFilters(Pageable pageable, AuctionRequest.SearchCondition condition) { +// BooleanBuilder builder = buildSearchCondition(condition); +// +// OrderSpecifier sortOrder = getSortOrder(condition); +// var query = queryFactory +// .select(Projections.constructor(AuctionResponse.class, +// auction.id, +// auction.startTime, +// auction.endTime, +// auction.auctionCategory.stringValue(), +// auction.auctionStatus.stringValue(), +// auction.startingBid, +// auction.viewCount, +// auction.seller.id.as("sellerId"), +// auction.seller.nickName.as("sellerNickName"), +// product.category.stringValue().as("productCategory") +// )) +// .from(auction) +// .leftJoin(auction.product, product) +// .leftJoin(auction.seller, user) +// .where(builder); +// +// +// // 페이징 +// query = query.offset(pageable.getOffset()) +// .limit(pageable.getPageSize()); +// +// List result = query.fetch(); +// +// //int 오버플로 주의 +// int total = queryFactory +// .selectFrom(product) +// .where(builder) +// .fetch().size(); +// +// return new PageImpl<>(result, pageable, total); +// } public Page getAuctionsByFilters(Pageable pageable, AuctionRequest.SearchCondition condition) { BooleanBuilder builder = buildSearchCondition(condition); + OrderSpecifier sortOrder = getSortOrder(condition); + JPAQuery query = buildQueryWithPaginationAndSorting(builder, pageable, sortOrder); + List result = query.fetch(); + + //int 오버플로 주의 + int total = queryFactory + .selectFrom(product) + .where(builder) + .fetch().size(); + return new PageImpl<>(result, pageable, total); + } + private JPAQuery buildQueryWithPaginationAndSorting(BooleanBuilder builder, Pageable pageable, OrderSpecifier sortOrder) { var query = queryFactory - .select(Projections.constructor(AuctionResponse.class, - auction.id, - auction.startTime, - auction.endTime, - auction.auctionCategory.stringValue(), - auction.auctionStatus.stringValue(), - auction.startingBid, - auction.viewCount, - auction.seller.id.as("sellerId"), - auction.seller.nickName.as("sellerNickName") - )) - .from(auction) - .leftJoin(auction.product, product) - .leftJoin(auction.seller, user) - .where(builder); + .select(Projections.constructor(AuctionResponse.class, + auction.id, + auction.startTime, + auction.endTime, + auction.auctionCategory.stringValue(), + auction.auctionStatus.stringValue(), + auction.startingBid, + auction.viewCount, + auction.seller.id.as("sellerId"), + auction.seller.nickName.as("sellerNickName"), +// product.owner.id.as("sellerId"), +// product.owner.nickName.as("sellerNickName"), + auction.product.category.stringValue().as("productCategory") +// product.category.stringValue().as("productCategory") + )) + .from(auction) + .leftJoin(auction.product, product) + .leftJoin(auction.seller, user) +// .from(product) +// .leftJoin(product.auction, auction) +// .leftJoin(product.owner, user) + .where(builder) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()); - OrderSpecifier sortOrder = getSortOrder(condition); if (sortOrder != null) { - query = query.orderBy(sortOrder); + query.orderBy(sortOrder); } - // 페이징 - query = query.offset(pageable.getOffset()) - .limit(pageable.getPageSize()); + return query; + } + + @Deprecated + public Page getAuctionsByCategory(Pageable pageable, + AuctionRequest.SearchCondition condition, + ProductCategory category) { + + BooleanBuilder builder = buildSearchCondition(condition); + filterByCategory(category, builder); + OrderSpecifier sortOrder = getSortOrder(condition); + JPAQuery query = buildQueryWithPaginationAndSorting(builder, pageable, sortOrder); List result = query.fetch(); - //int 오버플로 주의 int total = queryFactory .selectFrom(auction) .where(builder) .fetch().size(); return new PageImpl<>(result, pageable, total); + + } + @Deprecated + private void filterByCategory(ProductCategory category, BooleanBuilder builder) { + if (category != null) { + builder.and(product.category.eq(category)); + } } 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 7239d723..228e0c44 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 @@ -2,6 +2,7 @@ import com.tasksprints.auction.domain.auction.dto.request.AuctionRequest; import com.tasksprints.auction.domain.auction.dto.response.AuctionResponse; +import com.tasksprints.auction.domain.product.model.ProductCategory; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -25,4 +26,6 @@ public interface AuctionService { Page getAuctionsByFilter(Pageable pageable, AuctionRequest.SearchCondition searchCondition); + @Deprecated + Page getAuctionsByProductCategory(Pageable pageable, AuctionRequest.SearchCondition searchCondition, ProductCategory category); } 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 756d5df9..9fe517f4 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 @@ -8,6 +8,7 @@ import com.tasksprints.auction.domain.auction.model.Auction; import com.tasksprints.auction.domain.auction.model.AuctionStatus; import com.tasksprints.auction.domain.auction.repository.AuctionRepository; +import com.tasksprints.auction.domain.product.model.ProductCategory; import com.tasksprints.auction.domain.user.exception.UserNotFoundException; import com.tasksprints.auction.domain.user.model.User; import com.tasksprints.auction.domain.user.repository.UserRepository; @@ -113,4 +114,10 @@ public Page getAuctionsByFilter(Pageable pageable, AuctionReque return auctionRepository.getAuctionsByFilters(pageable, searchCondition); } + @Deprecated + @Override + public Page getAuctionsByProductCategory(Pageable pageable, AuctionRequest.SearchCondition searchCondition, ProductCategory category) { + return auctionRepository.getAuctionsByCategory(pageable, searchCondition, category); + } + } diff --git a/src/main/java/com/tasksprints/auction/domain/product/model/Product.java b/src/main/java/com/tasksprints/auction/domain/product/model/Product.java index 2d650910..ff72c7c7 100644 --- a/src/main/java/com/tasksprints/auction/domain/product/model/Product.java +++ b/src/main/java/com/tasksprints/auction/domain/product/model/Product.java @@ -37,6 +37,7 @@ public class Product extends BaseEntity { @OneToOne //mappedby + @JoinColumn(name = "auction_id") private Auction auction; @OneToMany @@ -55,7 +56,9 @@ public void addOwner(User owner) { } public void addAuction(Auction auction) { + //양방향 매핑 this.auction = auction; + auction.addProduct(this); } public void initProductImageList(List productImageList) { 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 a076b032..020e34cf 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 @@ -6,6 +6,9 @@ import com.tasksprints.auction.domain.auction.model.Auction; import com.tasksprints.auction.domain.auction.model.AuctionCategory; import com.tasksprints.auction.domain.auction.model.AuctionStatus; +import com.tasksprints.auction.domain.product.model.Product; +import com.tasksprints.auction.domain.product.model.ProductCategory; +import com.tasksprints.auction.domain.product.repository.ProductRepository; import com.tasksprints.auction.domain.user.model.User; import com.tasksprints.auction.domain.user.repository.UserRepository; import lombok.extern.slf4j.Slf4j; @@ -38,6 +41,9 @@ public class AuctionRepositoryTest { @Autowired private UserRepository userRepository; + @Autowired + private ProductRepository productRepository; + private User seller; @BeforeEach @@ -73,6 +79,17 @@ private Auction createAuction(User seller, LocalDateTime startTime) { ); } + private Product createProduct(User user, Auction auction, String productCategory) { + return Product.create( + "testName", + "testDescription", + user, + auction, + productCategory, + null + ); + } + @Test @DisplayName("사용자 ID로 경매 목록 조회") public void testFindAuctionsByUserId() { @@ -112,7 +129,7 @@ public void testFindAll() { } - + @Deprecated @Test @DisplayName("경매 유형이 []인 경매 목록 조회") public void testFindAuctionsByAuctionCategory() { @@ -120,10 +137,14 @@ public void testFindAuctionsByAuctionCategory() { Auction auction1 = createAuction(seller, AuctionCategory.PUBLIC_FREE, AuctionStatus.ACTIVE); Auction auction2 = createAuction(seller, AuctionCategory.PUBLIC_PAID, AuctionStatus.PENDING); Auction auction3 = createAuction(seller, AuctionCategory.PUBLIC_PAID, AuctionStatus.PENDING); - AuctionRequest.SearchCondition condition = new AuctionRequest.SearchCondition(AuctionCategory.PUBLIC_PAID, null, null, null, null, null, null, null); - auctionRepository.save(auction1); - auctionRepository.save(auction2); - auctionRepository.save(auction3); + auctionRepository.saveAll(List.of(auction1, auction2, auction3)); + + Product product1 = createProduct(seller, auction1, "TV"); + Product product2 = createProduct(seller, auction2, "TV"); + Product product3 = createProduct(seller, auction3, "TV"); + productRepository.saveAll(List.of(product1, product2, product3)); + + AuctionRequest.SearchCondition condition = new AuctionRequest.SearchCondition(AuctionCategory.PUBLIC_PAID, ProductCategory.TV, null, null, null, null, null, null); Pageable pageable = PageRequest.of(0, 10); @@ -133,16 +154,22 @@ public void testFindAuctionsByAuctionCategory() { //then assertThat(auctions).hasSize(2); assertThat(auctions.getContent()).allMatch(auction -> auction.getCategory().equals(AuctionCategory.PUBLIC_PAID.name())); + assertThat(auctions.getContent()).allMatch(auction -> auction.getProductCategory().equals(ProductCategory.TV.name())); } - @Test @DisplayName("QueryDSL 필터를 통해서 경매 목록 조회") public void testFindAllUsingFilter() { Auction auction1 = createAuction(seller, AuctionCategory.PUBLIC_FREE, AuctionStatus.ACTIVE); Auction auction2 = createAuction(seller, AuctionCategory.PUBLIC_PAID, AuctionStatus.PENDING); - AuctionRequest.SearchCondition condition = new AuctionRequest.SearchCondition(AuctionCategory.PUBLIC_FREE, null, null, null, null, null, null, null); - auctionRepository.save(auction1); - auctionRepository.save(auction2); + + auctionRepository.saveAll(List.of(auction1, auction2)); + + Product product1 = createProduct(seller, auction1, "TV"); + Product product2 = createProduct(seller, auction2, "TV"); + + productRepository.saveAll(List.of(product1, product2)); + + AuctionRequest.SearchCondition condition = new AuctionRequest.SearchCondition(AuctionCategory.PUBLIC_FREE, ProductCategory.TV, null, null, null, null, null, null); Pageable pageable = PageRequest.of(0, 10); Page auctions = auctionRepository.getAuctionsByFilters(pageable, condition); @@ -150,7 +177,7 @@ public void testFindAllUsingFilter() { assertThat(auctions).hasSize(1); assertThat(auctions.getContent().get(0).getCategory()).isEqualTo(AuctionCategory.PUBLIC_FREE.name()); - assertThat(auctions.getContent().get(0).getCategory()).isNotEqualTo(AuctionCategory.PUBLIC_PAID.name()); + assertThat(auctions.getContent().get(0).getProductCategory()).isEqualTo(ProductCategory.TV.name()); } 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 11bb7d92..1d8c0248 100644 --- a/src/test/java/com/tasksprints/auction/domain/product/ProductRepositoryTest.java +++ b/src/test/java/com/tasksprints/auction/domain/product/ProductRepositoryTest.java @@ -1,20 +1,27 @@ package com.tasksprints.auction.domain.product; import com.tasksprints.auction.common.config.QueryDslConfig; +import com.tasksprints.auction.domain.auction.dto.request.AuctionRequest; +import com.tasksprints.auction.domain.auction.dto.response.AuctionResponse; import com.tasksprints.auction.domain.auction.model.Auction; import com.tasksprints.auction.domain.auction.model.AuctionCategory; import com.tasksprints.auction.domain.auction.model.AuctionStatus; import com.tasksprints.auction.domain.auction.repository.AuctionRepository; import com.tasksprints.auction.domain.product.model.Product; +import com.tasksprints.auction.domain.product.model.ProductCategory; import com.tasksprints.auction.domain.product.repository.ProductRepository; import com.tasksprints.auction.domain.user.model.User; import com.tasksprints.auction.domain.user.repository.UserRepository; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; 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 org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import java.math.BigDecimal; import java.time.LocalDateTime; @@ -98,6 +105,63 @@ public void testDeleteById() { Optional deletedProduct = productRepository.findById(product.getId()); assertTrue(deletedProduct.isEmpty()); } + @Deprecated + @Test + @DisplayName("queryDSL 검색 필터로 조회") + public void testFindAllUsingProductCategory() { + //given + Auction auction1 = createAuction(owner, AuctionStatus.PENDING); + Auction auction2 = createAuction(owner, AuctionStatus.ACTIVE); + + Product product1 = createProduct("product1", "description1", ProductCategory.TV, owner, auction1); + Product product2 = createProduct("product2", "description2", ProductCategory.DSLR, owner, auction2); + + product1.addAuction(auction1); + product2.addAuction(auction2); + + AuctionRequest.SearchCondition condition = new AuctionRequest.SearchCondition( + null, ProductCategory.TV, null, null, null, null, AuctionStatus.PENDING, null + ); + Pageable pageable = PageRequest.of(0, 10); + + //when + Page auctionsByFilters = auctionRepository.getAuctionsByFilters(pageable, condition); + + //then + assertThat(auctionsByFilters).hasSize(1); + assertThat(auctionsByFilters.getContent().get(0).getProductCategory()).isEqualTo("TV"); + assertThat(auctionsByFilters.getContent().get(0).getStatus()).isEqualTo("PENDING"); + + + } + @Deprecated + @Test + @DisplayName("상품 카테고리로 경매를 조회 : Condition 쿼리스트링이 넘어온 경우") + public void testFindAllUsingConditionFilter() { + //given + Auction auction1 = createAuction(owner, AuctionStatus.ACTIVE); + Auction auction2 = createAuction(owner, AuctionStatus.PENDING); + + Product product1 = createProduct("product1", "description1", ProductCategory.TV, owner, auction1); + Product product2 = createProduct("product2", "description2", ProductCategory.DSLR, owner, auction2); + + product1.addAuction(auction1); + product2.addAuction(auction2); + + Pageable pageable = PageRequest.of(0, 10); + + AuctionRequest.SearchCondition searchCondition = new AuctionRequest.SearchCondition(null, null, null, + null, null, null, + AuctionStatus.PENDING, null) + ; + //when + Page auctionsByCategory = auctionRepository.getAuctionsByCategory(pageable, searchCondition, ProductCategory.DSLR); + + //then + assertThat(auctionsByCategory).hasSize(1); + assertThat(auctionsByCategory.getContent().get(0).getProductCategory()).isEqualTo("DSLR"); + } + // Helper methods to minimize code duplication private User createUser(String name, String nickName, String email) { @@ -109,7 +173,6 @@ private User createUser(String name, String nickName, String email) { .build(); return userRepository.save(user); } - private Auction createAuction(User owner, BigDecimal startingBid, LocalDateTime startTime, LocalDateTime endTime) { Auction auction = Auction.builder() .startingBid(startingBid) @@ -122,6 +185,18 @@ private Auction createAuction(User owner, BigDecimal startingBid, LocalDateTime return auctionRepository.save(auction); } + private Auction createAuction(User owner, AuctionStatus auctionStatus) { + Auction auction = Auction.builder() + .startingBid(BigDecimal.valueOf(100.00)) + .startTime(LocalDateTime.now()) + .endTime(LocalDateTime.now().plusHours(1)) + .auctionStatus(auctionStatus) + .auctionCategory(AuctionCategory.PRIVATE_FREE) + .seller(owner) + .build(); + return auctionRepository.save(auction); + } + private Product createProduct(String name, String description, User owner, Auction auction) { Product product = Product.builder() .name(name) @@ -131,4 +206,15 @@ private Product createProduct(String name, String description, User owner, Aucti .build(); return productRepository.save(product); } + + private Product createProduct(String name, String description, ProductCategory category, User owner, Auction auction) { + Product product = Product.builder() + .name(name) + .description(description) + .owner(owner) + .auction(auction) + .category(category) + .build(); + return productRepository.save(product); + } } From 3500230de528aa6b6561e5ef1316f6cb8fe349ff Mon Sep 17 00:00:00 2001 From: na0th <105775683+na0th@users.noreply.github.com> Date: Wed, 25 Sep 2024 01:49:31 +0900 Subject: [PATCH 7/9] =?UTF-8?q?fix=20:=20=EC=BF=BC=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EB=A7=81=EC=A4=91=20ENUM=EC=97=90=20=EC=97=86?= =?UTF-8?q?=EB=8A=94=20string=EC=97=90=20=EB=8C=80=ED=95=9C=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 쿼리스트링중 ENUM에 없는 string에 대한 예외 처리 2. count쿼리 from절 product -> auciton 수정 --- .../auction/domain/auction/model/AuctionCategory.java | 10 ++++++---- .../auction/domain/auction/model/AuctionStatus.java | 9 +++++---- .../support/AuctionCriteriaRepositoryImpl.java | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) 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 697f08f9..7c362815 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,11 +6,13 @@ public enum AuctionCategory { PRIVATE_PAID, PUBLIC_PAID; + public static AuctionCategory fromDisplayName(String auctionCategory) { - try { - return AuctionCategory.valueOf(auctionCategory.toUpperCase()); // 대문자로 변환하여 비교 - } catch (IllegalArgumentException e) { - return PUBLIC_FREE; // 유효하지 않은 값일 경우 기본값으로 PUBLIC_FREE 반환 + for (AuctionCategory category : values()) { + if (category.name().equalsIgnoreCase(auctionCategory)) { + return category; + } } + throw new IllegalArgumentException("Unknown auctionCategory: " + auctionCategory); } } diff --git a/src/main/java/com/tasksprints/auction/domain/auction/model/AuctionStatus.java b/src/main/java/com/tasksprints/auction/domain/auction/model/AuctionStatus.java index 2f69867d..71355903 100644 --- a/src/main/java/com/tasksprints/auction/domain/auction/model/AuctionStatus.java +++ b/src/main/java/com/tasksprints/auction/domain/auction/model/AuctionStatus.java @@ -7,10 +7,11 @@ public enum AuctionStatus { CANCELED; public static AuctionStatus fromDisplayName(String auctionStatus) { - try { - return AuctionStatus.valueOf(auctionStatus.toUpperCase()); // 대문자로 변환하여 비교 - } catch (IllegalArgumentException e) { - return AuctionStatus.ACTIVE; // 유효하지 않은 값일 경우 기본값으로 ACTIVE 반환 + for (AuctionStatus status : values() ) { + if (status.name().equalsIgnoreCase(auctionStatus)) { + return status; + } } + throw new IllegalArgumentException("Unknown auctionStatus: " + auctionStatus); } } diff --git a/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java b/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java index e4f2bc6d..fd26cd97 100644 --- a/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java +++ b/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java @@ -70,7 +70,7 @@ public Page getAuctionsByFilters(Pageable pageable, AuctionRequ //int 오버플로 주의 int total = queryFactory - .selectFrom(product) + .selectFrom(auction) .where(builder) .fetch().size(); From b9397f7f945a1044d1229a90bf4f3ccfbf883925 Mon Sep 17 00:00:00 2001 From: na0th <105775683+na0th@users.noreply.github.com> Date: Fri, 27 Sep 2024 19:37:26 +0900 Subject: [PATCH 8/9] =?UTF-8?q?refactor=20:=20=EC=A1=B0=ED=9A=8C=EC=9A=A9?= =?UTF-8?q?=20Response=20DTO=20=EB=B6=84=EB=A6=AC,=20=EB=A9=94=EC=9D=B8=20?= =?UTF-8?q?=EC=BF=BC=EB=A6=AC=EC=99=80=20=EC=BB=AC=EB=A0=89=EC=85=98=20?= =?UTF-8?q?=EC=BF=BC=EB=A6=AC=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1 : Response DTO를 Create, Update용 Response와 쿼리 Response용 DTO(Details)로 분리 2. 메인 쿼리와 컬렉션 쿼리 분리 => 컬렉션과 함께 join 연산을 하면, 데이터 row가 증가하여 페이징 처리가 이상해짐 3. of 메서드에서 null 체크 -> product의 images는 null이 가능 + auction.product나 product.auction이 일반적으로는 어렵지만 혹시 모를 경우 null이 들어올 수 있다. 4. queryDSL에서 JPAQuery부분과 query.fetch부분 코드 합침 + query 부분 주석 제거 주의할점 : Details클래스 productImageUrls에 @Setter를 추가함 메인 쿼리와 컬렉션 쿼리를 분리했기 때문에 컬렉션을 다시 메인 쿼리에 담아주는 과정에서 setter가 필요해서 추가.. builder는 또다시 객체를 만들어서 옮기는 과정이 불필요하다 생각했음 이 부분 논의가 필요해보입니다. 미흡 : user 생성 -> auction 생성 -> product 추가순으로 진행하면 검색 필터 사용해서 join 쿼리 조회가 잘 되지만, user 생성 -> auction 생성 후 검색 필터를 입력했을 시 예외처리가 미흡한 것 같다. --- .../api/auction/AuctionController.java | 8 +- .../auction/dto/response/AuctionResponse.java | 50 +++++++++-- .../support/AuctionCriteriaRepository.java | 4 +- .../AuctionCriteriaRepositoryImpl.java | 88 +++++++------------ .../auction/service/AuctionService.java | 4 +- .../auction/service/AuctionServiceImpl.java | 4 +- .../auction/api/AuctionControllerTest.java | 4 +- .../repository/AuctionRepositoryTest.java | 4 +- .../service/AuctionServiceImplTest.java | 28 +++--- .../domain/product/ProductRepositoryTest.java | 4 +- 10 files changed, 106 insertions(+), 92 deletions(-) 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 d7cf9dde..49881281 100644 --- a/src/main/java/com/tasksprints/auction/api/auction/AuctionController.java +++ b/src/main/java/com/tasksprints/auction/api/auction/AuctionController.java @@ -83,8 +83,8 @@ public ResponseEntity> getAuctionStatus(@PathVariable Long auc @GetMapping @Operation(summary = "Get all auctions", description = "Retrieves all auctions.") @ApiResponse(responseCode = "200", description = "All auctions retrieved successfully") - public ResponseEntity>> getAllAuctions(Pageable pageable, AuctionRequest.SearchCondition searchCondition) { - Page auctions = auctionService.getAuctionsByFilter(pageable, searchCondition); + public ResponseEntity>> getAllAuctions(Pageable pageable, AuctionRequest.SearchCondition searchCondition) { + Page auctions = auctionService.getAuctionsByFilter(pageable, searchCondition); return ResponseEntity.ok(ApiResult.success(ApiResponseMessages.ALL_AUCTIONS_RETRIEVED, auctions)); } @@ -100,8 +100,8 @@ public ResponseEntity> getAuctionById(@PathVariable L @GetMapping("/category/{category}") @Operation(summary = "Get auctions by ProductCategory", description = "Retrieve all auction by its ProductCategory.") @ApiResponse(responseCode = "200", description = "All auctions retrieved successfully") - public ResponseEntity>> getAuctionByProductCategory(Pageable pageable, @PathVariable String category, AuctionRequest.SearchCondition searchCondition) { - Page auctions = auctionService.getAuctionsByProductCategory(pageable, searchCondition, ProductCategory.fromDisplayName(category)); + public ResponseEntity>> getAuctionByProductCategory(Pageable pageable, @PathVariable String category, AuctionRequest.SearchCondition searchCondition) { + Page auctions = auctionService.getAuctionsByProductCategory(pageable, searchCondition, ProductCategory.fromDisplayName(category)); return ResponseEntity.ok(ApiResult.success(ApiResponseMessages.AUCTION_RETRIEVED, auctions)); } diff --git a/src/main/java/com/tasksprints/auction/domain/auction/dto/response/AuctionResponse.java b/src/main/java/com/tasksprints/auction/domain/auction/dto/response/AuctionResponse.java index 6dde13c6..c0ff0dd4 100644 --- a/src/main/java/com/tasksprints/auction/domain/auction/dto/response/AuctionResponse.java +++ b/src/main/java/com/tasksprints/auction/domain/auction/dto/response/AuctionResponse.java @@ -1,13 +1,15 @@ package com.tasksprints.auction.domain.auction.dto.response; import com.tasksprints.auction.domain.auction.model.Auction; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; +import com.tasksprints.auction.domain.product.model.ProductImage; +import lombok.*; import java.math.BigDecimal; import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; + +import static java.util.stream.Collectors.toList; @Data @Builder @@ -23,9 +25,47 @@ public class AuctionResponse { private Long viewCount; private Long sellerId; private String sellerNickName; - private String productCategory; + @Getter + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class Details { + private Long id; + private LocalDateTime startTime; + private LocalDateTime endTime; + private String category; + private String status; + private BigDecimal startingBid; + private Long viewCount; + private Long sellerId; + private String sellerNickName; + private Long productId; + private String productCategory; + @Setter + private List productImageUrls; + public static AuctionResponse.Details of(Auction auction) { + return AuctionResponse.Details.builder() + .id(auction.getId()) + .startTime(auction.getStartTime()) + .endTime(auction.getEndTime()) + .category(auction.getAuctionCategory().name()) + .status(auction.getAuctionStatus().name()) + .startingBid(auction.getStartingBid()) + .viewCount(auction.getViewCount()) + .sellerId(auction.getSeller() != null ? auction.getSeller().getId() : null) + .sellerNickName(auction.getSeller() != null ? auction.getSeller().getNickName() : null) + .productId(auction.getProduct() != null ? auction.getProduct().getId() : null) + .productCategory(auction.getProduct() != null && auction.getProduct().getCategory() != null ? auction.getProduct().getCategory().name() : null) + .productImageUrls(auction.getProduct() != null && auction.getProduct().getProductImageList() != null + ? auction.getProduct().getProductImageList().stream() + .map(ProductImage::getImageUrl) + .collect(toList()) + : Collections.emptyList()) + .build(); + } + } public static AuctionResponse of(Auction auction) { return AuctionResponse.builder() .id(auction.getId()) diff --git a/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepository.java b/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepository.java index 37bba68b..70afa343 100644 --- a/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepository.java +++ b/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepository.java @@ -9,9 +9,9 @@ public interface AuctionCriteriaRepository { - Page getAuctionsByFilters(Pageable pageable, AuctionRequest.SearchCondition searchCondition); + Page getAuctionsByFilters(Pageable pageable, AuctionRequest.SearchCondition searchCondition); @Deprecated - Page getAuctionsByCategory(Pageable pageable, + Page getAuctionsByCategory(Pageable pageable, AuctionRequest.SearchCondition searchCondition, ProductCategory category); } diff --git a/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java b/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java index fd26cd97..dd6f1b1c 100644 --- a/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java +++ b/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java @@ -18,6 +18,7 @@ import static com.tasksprints.auction.domain.auction.model.QAuction.auction; import static com.tasksprints.auction.domain.product.model.QProduct.product; +import static com.tasksprints.auction.domain.product.model.QProductImage.productImage; import static com.tasksprints.auction.domain.user.model.QUser.user; @RequiredArgsConstructor @@ -25,48 +26,10 @@ public class AuctionCriteriaRepositoryImpl implements AuctionCriteriaRepository { private final JPAQueryFactory queryFactory; -// public Page getAuctionsByFilters(Pageable pageable, AuctionRequest.SearchCondition condition) { -// BooleanBuilder builder = buildSearchCondition(condition); -// -// OrderSpecifier sortOrder = getSortOrder(condition); -// var query = queryFactory -// .select(Projections.constructor(AuctionResponse.class, -// auction.id, -// auction.startTime, -// auction.endTime, -// auction.auctionCategory.stringValue(), -// auction.auctionStatus.stringValue(), -// auction.startingBid, -// auction.viewCount, -// auction.seller.id.as("sellerId"), -// auction.seller.nickName.as("sellerNickName"), -// product.category.stringValue().as("productCategory") -// )) -// .from(auction) -// .leftJoin(auction.product, product) -// .leftJoin(auction.seller, user) -// .where(builder); -// -// -// // 페이징 -// query = query.offset(pageable.getOffset()) -// .limit(pageable.getPageSize()); -// -// List result = query.fetch(); -// -// //int 오버플로 주의 -// int total = queryFactory -// .selectFrom(product) -// .where(builder) -// .fetch().size(); -// -// return new PageImpl<>(result, pageable, total); -// } - public Page getAuctionsByFilters(Pageable pageable, AuctionRequest.SearchCondition condition) { + public Page getAuctionsByFilters(Pageable pageable, AuctionRequest.SearchCondition condition) { BooleanBuilder builder = buildSearchCondition(condition); OrderSpecifier sortOrder = getSortOrder(condition); - JPAQuery query = buildQueryWithPaginationAndSorting(builder, pageable, sortOrder); - List result = query.fetch(); + List result = buildQueryWithPaginationAndSorting(builder, pageable, sortOrder); //int 오버플로 주의 int total = queryFactory @@ -76,50 +39,61 @@ public Page getAuctionsByFilters(Pageable pageable, AuctionRequ return new PageImpl<>(result, pageable, total); } - private JPAQuery buildQueryWithPaginationAndSorting(BooleanBuilder builder, Pageable pageable, OrderSpecifier sortOrder) { - var query = queryFactory - .select(Projections.constructor(AuctionResponse.class, + private List buildQueryWithPaginationAndSorting(BooleanBuilder builder, Pageable pageable, OrderSpecifier sortOrder) { + var mainQuery = queryFactory + .select(Projections.fields(AuctionResponse.Details.class, auction.id, auction.startTime, auction.endTime, - auction.auctionCategory.stringValue(), - auction.auctionStatus.stringValue(), + auction.auctionCategory.stringValue().as("category"), + auction.auctionStatus.stringValue().as("status"), auction.startingBid, auction.viewCount, auction.seller.id.as("sellerId"), auction.seller.nickName.as("sellerNickName"), -// product.owner.id.as("sellerId"), -// product.owner.nickName.as("sellerNickName"), + auction.product.id.as("productId"), auction.product.category.stringValue().as("productCategory") -// product.category.stringValue().as("productCategory") - )) + ) + ) .from(auction) .leftJoin(auction.product, product) .leftJoin(auction.seller, user) -// .from(product) -// .leftJoin(product.auction, auction) -// .leftJoin(product.owner, user) .where(builder) .offset(pageable.getOffset()) .limit(pageable.getPageSize()); if (sortOrder != null) { - query.orderBy(sortOrder); + mainQuery.orderBy(sortOrder); } + List detailsResult = mainQuery.fetch(); + //컬렉션은 메인 쿼리와 분리 + attachImageUrlsToMainQuery(detailsResult); - return query; + return detailsResult; + } + + private void attachImageUrlsToMainQuery(List detailsResult) { + detailsResult.forEach(detail -> { + List imageUrls = queryFactory + .select(productImage.imageUrl) + .from(product) + .leftJoin(product.productImageList, productImage) + .where(product.id.eq(detail.getProductId())) + .fetch(); + + detail.setProductImageUrls(imageUrls); + }); } @Deprecated - public Page getAuctionsByCategory(Pageable pageable, + public Page getAuctionsByCategory(Pageable pageable, AuctionRequest.SearchCondition condition, ProductCategory category) { BooleanBuilder builder = buildSearchCondition(condition); filterByCategory(category, builder); OrderSpecifier sortOrder = getSortOrder(condition); - JPAQuery query = buildQueryWithPaginationAndSorting(builder, pageable, sortOrder); - List result = query.fetch(); + List result = buildQueryWithPaginationAndSorting(builder, pageable, sortOrder); int total = queryFactory .selectFrom(auction) 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 228e0c44..69606124 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 @@ -24,8 +24,8 @@ public interface AuctionService { AuctionResponse getAuctionById(Long auctionId); - Page getAuctionsByFilter(Pageable pageable, AuctionRequest.SearchCondition searchCondition); + Page getAuctionsByFilter(Pageable pageable, AuctionRequest.SearchCondition searchCondition); @Deprecated - Page getAuctionsByProductCategory(Pageable pageable, AuctionRequest.SearchCondition searchCondition, ProductCategory category); + Page getAuctionsByProductCategory(Pageable pageable, AuctionRequest.SearchCondition searchCondition, ProductCategory category); } 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 9fe517f4..efdc25c7 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 @@ -110,13 +110,13 @@ public AuctionResponse getAuctionById(Long auctionId) { * NULL 안정성 보장을 해줬음 **/ @Override - public Page getAuctionsByFilter(Pageable pageable, AuctionRequest.SearchCondition searchCondition) { + public Page getAuctionsByFilter(Pageable pageable, AuctionRequest.SearchCondition searchCondition) { return auctionRepository.getAuctionsByFilters(pageable, searchCondition); } @Deprecated @Override - public Page getAuctionsByProductCategory(Pageable pageable, AuctionRequest.SearchCondition searchCondition, ProductCategory category) { + public Page getAuctionsByProductCategory(Pageable pageable, AuctionRequest.SearchCondition searchCondition, ProductCategory category) { return auctionRepository.getAuctionsByCategory(pageable, searchCondition, category); } diff --git a/src/test/java/com/tasksprints/auction/api/AuctionControllerTest.java b/src/test/java/com/tasksprints/auction/api/AuctionControllerTest.java index cac0b8c4..e21acad2 100644 --- a/src/test/java/com/tasksprints/auction/api/AuctionControllerTest.java +++ b/src/test/java/com/tasksprints/auction/api/AuctionControllerTest.java @@ -118,9 +118,9 @@ public void testGetAuctionStatus_Success() throws Exception { @DisplayName("QueryString을 통한 경매 목록 조회") public void testFindAuctionByUsingQueryString_Success() throws Exception { // Given - List auctionResponseList = new ArrayList<>(); + List auctionResponseList = new ArrayList<>(); Pageable pageable = PageRequest.of(0, 10); - Page pageAuctionResponse = new PageImpl<>(auctionResponseList, pageable, 0); + Page pageAuctionResponse = new PageImpl<>(auctionResponseList, pageable, 0); when(auctionService.getAuctionsByFilter(any(),any())).thenReturn(pageAuctionResponse); // When & Then 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 020e34cf..364f1982 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 @@ -149,7 +149,7 @@ public void testFindAuctionsByAuctionCategory() { Pageable pageable = PageRequest.of(0, 10); //when - Page auctions = auctionRepository.getAuctionsByFilters(pageable, condition); + Page auctions = auctionRepository.getAuctionsByFilters(pageable, condition); //then assertThat(auctions).hasSize(2); @@ -172,7 +172,7 @@ public void testFindAllUsingFilter() { AuctionRequest.SearchCondition condition = new AuctionRequest.SearchCondition(AuctionCategory.PUBLIC_FREE, ProductCategory.TV, null, null, null, null, null, null); Pageable pageable = PageRequest.of(0, 10); - Page auctions = auctionRepository.getAuctionsByFilters(pageable, condition); + Page auctions = auctionRepository.getAuctionsByFilters(pageable, condition); log.info(auctions.toString()); assertThat(auctions).hasSize(1); 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 9d14857c..38a5de91 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 @@ -328,17 +328,17 @@ public void testGetAuctionsByAuctionCategory_Success() { createAuction(1L, seller, AuctionStatus.PENDING), createAuction(2L, seller, AuctionStatus.PENDING) ); - List auctionResponses = auctions + List auctionResponses = auctions .stream() - .map(AuctionResponse::of) + .map(AuctionResponse.Details::of) .toList(); Pageable pageable = PageRequest.of(0, 10); - Page expectedPage = new PageImpl<>(auctionResponses, pageable, auctionResponses.size()); + Page expectedPage = new PageImpl<>(auctionResponses, pageable, auctionResponses.size()); when(auctionRepository.getAuctionsByFilters(pageable, condition)).thenReturn(expectedPage); - Page actualAuctions = auctionService.getAuctionsByFilter(pageable, condition); + Page actualAuctions = auctionService.getAuctionsByFilter(pageable, condition); assertThat(actualAuctions).isEqualTo(expectedPage); } @@ -352,32 +352,32 @@ public void testGetAuctionsByAuctionCategory_Success_Criteria() { createAuction(2L, seller, AuctionStatus.PENDING) ); - List auctionResponses = auctions + List auctionResponses = auctions .stream() - .map(AuctionResponse::of) + .map(AuctionResponse.Details::of) .toList(); Pageable pageable = PageRequest.of(0, 10); - Page expectedPage = new PageImpl<>(auctionResponses, pageable, auctionResponses.size()); + Page expectedPage = new PageImpl<>(auctionResponses, pageable, auctionResponses.size()); when(auctionRepository.getAuctionsByFilters(pageable, condition)).thenReturn(expectedPage); - Page actualAuctions = auctionService.getAuctionsByFilter(pageable, condition); + Page actualAuctions = auctionService.getAuctionsByFilter(pageable, condition); assertThat(actualAuctions).isEqualTo(expectedPage); } @Test @DisplayName("경매 유형 조회 : [결과 없음]") public void testGetAuctionsByAuctionCategory_AuctionNotFound() { - List emptyAuctionList = List.of(); + List emptyAuctionList = List.of(); AuctionRequest.SearchCondition condition = new AuctionRequest.SearchCondition(AuctionCategory.PUBLIC_FREE, null, null, null, null, null, null, null); Pageable pageable = PageRequest.of(0, 10); - Page expectedPage = new PageImpl<>(emptyAuctionList, pageable, 0); + Page expectedPage = new PageImpl<>(emptyAuctionList, pageable, 0); when(auctionRepository.getAuctionsByFilters(pageable, condition)) .thenReturn(expectedPage); - Page actualAuctions = auctionService.getAuctionsByFilter(pageable, condition); + Page actualAuctions = auctionService.getAuctionsByFilter(pageable, condition); assertThat(actualAuctions.isEmpty()).isTrue(); } @@ -385,15 +385,15 @@ public void testGetAuctionsByAuctionCategory_AuctionNotFound() { @Test @DisplayName("경매 유형 조회 : [결과 없음] - Criteria 사용") public void testGetAuctionsByAuctionCategory_AuctionNotFound_Criteria() { - List emptyAuctionList = List.of(); + List emptyAuctionList = List.of(); AuctionRequest.SearchCondition condition = new AuctionRequest.SearchCondition(AuctionCategory.PUBLIC_FREE, null, null, null, null, null, null, null); Pageable pageable = PageRequest.of(0, 10); - Page expectedPage = new PageImpl<>(emptyAuctionList, pageable, 0); + Page expectedPage = new PageImpl<>(emptyAuctionList, pageable, 0); when(auctionRepository.getAuctionsByFilters(pageable, condition)) .thenReturn(expectedPage); - Page actualAuctions = auctionService.getAuctionsByFilter(pageable, condition); + Page actualAuctions = auctionService.getAuctionsByFilter(pageable, condition); assertThat(actualAuctions.isEmpty()).isTrue(); } 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 1d8c0248..89226ece 100644 --- a/src/test/java/com/tasksprints/auction/domain/product/ProductRepositoryTest.java +++ b/src/test/java/com/tasksprints/auction/domain/product/ProductRepositoryTest.java @@ -125,7 +125,7 @@ public void testFindAllUsingProductCategory() { Pageable pageable = PageRequest.of(0, 10); //when - Page auctionsByFilters = auctionRepository.getAuctionsByFilters(pageable, condition); + Page auctionsByFilters = auctionRepository.getAuctionsByFilters(pageable, condition); //then assertThat(auctionsByFilters).hasSize(1); @@ -155,7 +155,7 @@ public void testFindAllUsingConditionFilter() { AuctionStatus.PENDING, null) ; //when - Page auctionsByCategory = auctionRepository.getAuctionsByCategory(pageable, searchCondition, ProductCategory.DSLR); + Page auctionsByCategory = auctionRepository.getAuctionsByCategory(pageable, searchCondition, ProductCategory.DSLR); //then assertThat(auctionsByCategory).hasSize(1); From 63d3ab9968778cf82c5d2774aa23fbc29e13982b Mon Sep 17 00:00:00 2001 From: na0th <105775683+na0th@users.noreply.github.com> Date: Mon, 30 Sep 2024 15:16:24 +0900 Subject: [PATCH 9/9] =?UTF-8?q?feat=20=EC=BF=BC=EB=A6=AC=20=EC=84=B1?= =?UTF-8?q?=EB=8A=A5=20=EC=B5=9C=EC=A0=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 기존 N+1 문제를 해결 - toOne관계는 fetch join, toMany(컬렉션)은 batch_size 설정으로 해결 2. 카운트 쿼리는 삭제하고, 어플리케이션에서 쿼리 결과 리스트의 size()를 이용하는 것으로 수정 3. 컬렉션 쿼리는 삭제 -컬렉션을 처리하지 않아도 Details의 of 메서드에서 컬렉션을 불러올 때 지연로딩으로 하이버네이트에서 자동으로 쿼리가 나가는 것을 직접 쿼리를 작성하는 것으로 변경하는 것이 좋아보임 4. 쿼리 결과의 반환 타입이 Page으로 변경되어 테스트 코드를 수정 --- .../support/AuctionCriteriaRepository.java | 5 +- .../AuctionCriteriaRepositoryImpl.java | 64 +++++------------ .../auction/service/AuctionServiceImpl.java | 6 +- .../repository/AuctionRepositoryTest.java | 37 +++++----- .../service/AuctionServiceImplTest.java | 69 ++++++++++--------- .../domain/product/ProductRepositoryTest.java | 11 +-- 6 files changed, 86 insertions(+), 106 deletions(-) diff --git a/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepository.java b/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepository.java index 70afa343..47fc40ee 100644 --- a/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepository.java +++ b/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepository.java @@ -3,15 +3,16 @@ import com.tasksprints.auction.domain.auction.dto.request.AuctionRequest; import com.tasksprints.auction.domain.auction.dto.response.AuctionResponse; +import com.tasksprints.auction.domain.auction.model.Auction; import com.tasksprints.auction.domain.product.model.ProductCategory; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; public interface AuctionCriteriaRepository { - Page getAuctionsByFilters(Pageable pageable, AuctionRequest.SearchCondition searchCondition); + Page getAuctionsByFilters(Pageable pageable, AuctionRequest.SearchCondition searchCondition); @Deprecated - Page getAuctionsByCategory(Pageable pageable, + Page getAuctionsByCategory(Pageable pageable, AuctionRequest.SearchCondition searchCondition, ProductCategory category); } diff --git a/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java b/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java index dd6f1b1c..18e085be 100644 --- a/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java +++ b/src/main/java/com/tasksprints/auction/domain/auction/repository/support/AuctionCriteriaRepositoryImpl.java @@ -2,11 +2,9 @@ import com.querydsl.core.BooleanBuilder; import com.querydsl.core.types.OrderSpecifier; -import com.querydsl.core.types.Projections; -import com.querydsl.jpa.impl.JPAQuery; import com.querydsl.jpa.impl.JPAQueryFactory; import com.tasksprints.auction.domain.auction.dto.request.AuctionRequest; -import com.tasksprints.auction.domain.auction.dto.response.AuctionResponse; +import com.tasksprints.auction.domain.auction.model.Auction; import com.tasksprints.auction.domain.product.model.ProductCategory; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; @@ -18,7 +16,6 @@ import static com.tasksprints.auction.domain.auction.model.QAuction.auction; import static com.tasksprints.auction.domain.product.model.QProduct.product; -import static com.tasksprints.auction.domain.product.model.QProductImage.productImage; import static com.tasksprints.auction.domain.user.model.QUser.user; @RequiredArgsConstructor @@ -26,38 +23,29 @@ public class AuctionCriteriaRepositoryImpl implements AuctionCriteriaRepository { private final JPAQueryFactory queryFactory; - public Page getAuctionsByFilters(Pageable pageable, AuctionRequest.SearchCondition condition) { + public Page getAuctionsByFilters(Pageable pageable, AuctionRequest.SearchCondition condition) { BooleanBuilder builder = buildSearchCondition(condition); OrderSpecifier sortOrder = getSortOrder(condition); - List result = buildQueryWithPaginationAndSorting(builder, pageable, sortOrder); + List result = buildQueryWithPaginationAndSorting(builder, pageable, sortOrder); - //int 오버플로 주의 - int total = queryFactory - .selectFrom(auction) - .where(builder) - .fetch().size(); + // int 오버플로 주의 +// int total = queryFactory +// .selectFrom(auction) +// .where(builder) +// .fetch().size(); + + long total = result.size(); return new PageImpl<>(result, pageable, total); } - private List buildQueryWithPaginationAndSorting(BooleanBuilder builder, Pageable pageable, OrderSpecifier sortOrder) { + + private List buildQueryWithPaginationAndSorting(BooleanBuilder builder, Pageable pageable, OrderSpecifier sortOrder) { var mainQuery = queryFactory - .select(Projections.fields(AuctionResponse.Details.class, - auction.id, - auction.startTime, - auction.endTime, - auction.auctionCategory.stringValue().as("category"), - auction.auctionStatus.stringValue().as("status"), - auction.startingBid, - auction.viewCount, - auction.seller.id.as("sellerId"), - auction.seller.nickName.as("sellerNickName"), - auction.product.id.as("productId"), - auction.product.category.stringValue().as("productCategory") - ) - ) - .from(auction) + .selectFrom(auction) .leftJoin(auction.product, product) + .fetchJoin() .leftJoin(auction.seller, user) + .fetchJoin() .where(builder) .offset(pageable.getOffset()) .limit(pageable.getPageSize()); @@ -65,35 +53,19 @@ private List buildQueryWithPaginationAndSorting(Boolean if (sortOrder != null) { mainQuery.orderBy(sortOrder); } - List detailsResult = mainQuery.fetch(); - //컬렉션은 메인 쿼리와 분리 - attachImageUrlsToMainQuery(detailsResult); - - return detailsResult; - } - private void attachImageUrlsToMainQuery(List detailsResult) { - detailsResult.forEach(detail -> { - List imageUrls = queryFactory - .select(productImage.imageUrl) - .from(product) - .leftJoin(product.productImageList, productImage) - .where(product.id.eq(detail.getProductId())) - .fetch(); - - detail.setProductImageUrls(imageUrls); - }); + return mainQuery.fetch(); } @Deprecated - public Page getAuctionsByCategory(Pageable pageable, + public Page getAuctionsByCategory(Pageable pageable, AuctionRequest.SearchCondition condition, ProductCategory category) { BooleanBuilder builder = buildSearchCondition(condition); filterByCategory(category, builder); OrderSpecifier sortOrder = getSortOrder(condition); - List result = buildQueryWithPaginationAndSorting(builder, pageable, sortOrder); + List result = buildQueryWithPaginationAndSorting(builder, pageable, sortOrder); int total = queryFactory .selectFrom(auction) 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 efdc25c7..f14522cf 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 @@ -111,13 +111,15 @@ public AuctionResponse getAuctionById(Long auctionId) { **/ @Override public Page getAuctionsByFilter(Pageable pageable, AuctionRequest.SearchCondition searchCondition) { - return auctionRepository.getAuctionsByFilters(pageable, searchCondition); + Page auctions = auctionRepository.getAuctionsByFilters(pageable, searchCondition); + return auctions.map(AuctionResponse.Details::of); } @Deprecated @Override public Page getAuctionsByProductCategory(Pageable pageable, AuctionRequest.SearchCondition searchCondition, ProductCategory category) { - return auctionRepository.getAuctionsByCategory(pageable, searchCondition, category); + Page auctions = auctionRepository.getAuctionsByFilters(pageable, searchCondition); + return auctions.map(AuctionResponse.Details::of); } } 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 364f1982..57ce46bf 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 @@ -68,13 +68,13 @@ private Auction createAuction(User seller, AuctionCategory category, AuctionStat ); } - private Auction createAuction(User seller, LocalDateTime startTime) { + private Auction createAuction(User seller, AuctionCategory auctionCategory) { return Auction.create( - startTime, + LocalDateTime.of(2024, 8, 1, 10, 0), LocalDateTime.of(2024, 9, 1, 10, 0), BigDecimal.valueOf(100.00), - AuctionCategory.PUBLIC_PAID, - AuctionStatus.PENDING, + auctionCategory, + AuctionStatus.ACTIVE, seller ); } @@ -134,9 +134,9 @@ public void testFindAll() { @DisplayName("경매 유형이 []인 경매 목록 조회") public void testFindAuctionsByAuctionCategory() { //given - Auction auction1 = createAuction(seller, AuctionCategory.PUBLIC_FREE, AuctionStatus.ACTIVE); - Auction auction2 = createAuction(seller, AuctionCategory.PUBLIC_PAID, AuctionStatus.PENDING); - Auction auction3 = createAuction(seller, AuctionCategory.PUBLIC_PAID, AuctionStatus.PENDING); + Auction auction1 = createAuction(seller, AuctionCategory.PUBLIC_FREE); + Auction auction2 = createAuction(seller, AuctionCategory.PUBLIC_PAID); + Auction auction3 = createAuction(seller, AuctionCategory.PUBLIC_PAID); auctionRepository.saveAll(List.of(auction1, auction2, auction3)); Product product1 = createProduct(seller, auction1, "TV"); @@ -149,35 +149,36 @@ public void testFindAuctionsByAuctionCategory() { Pageable pageable = PageRequest.of(0, 10); //when - Page auctions = auctionRepository.getAuctionsByFilters(pageable, condition); + Page auctions = auctionRepository.getAuctionsByFilters(pageable, condition); //then assertThat(auctions).hasSize(2); - assertThat(auctions.getContent()).allMatch(auction -> auction.getCategory().equals(AuctionCategory.PUBLIC_PAID.name())); - assertThat(auctions.getContent()).allMatch(auction -> auction.getProductCategory().equals(ProductCategory.TV.name())); + assertThat(auctions.getContent()).allMatch(auction -> auction.getAuctionCategory().equals(AuctionCategory.PUBLIC_PAID)); + assertThat(auctions.getContent()).allMatch(auction -> auction.getProduct().getCategory().equals(ProductCategory.TV)); } @Test @DisplayName("QueryDSL 필터를 통해서 경매 목록 조회") public void testFindAllUsingFilter() { - Auction auction1 = createAuction(seller, AuctionCategory.PUBLIC_FREE, AuctionStatus.ACTIVE); - Auction auction2 = createAuction(seller, AuctionCategory.PUBLIC_PAID, AuctionStatus.PENDING); - + //given + Auction auction1 = createAuction(seller, AuctionCategory.PUBLIC_FREE); + Auction auction2 = createAuction(seller, AuctionCategory.PUBLIC_PAID); auctionRepository.saveAll(List.of(auction1, auction2)); Product product1 = createProduct(seller, auction1, "TV"); Product product2 = createProduct(seller, auction2, "TV"); - productRepository.saveAll(List.of(product1, product2)); AuctionRequest.SearchCondition condition = new AuctionRequest.SearchCondition(AuctionCategory.PUBLIC_FREE, ProductCategory.TV, null, null, null, null, null, null); - Pageable pageable = PageRequest.of(0, 10); - Page auctions = auctionRepository.getAuctionsByFilters(pageable, condition); + + //when + Page auctions = auctionRepository.getAuctionsByFilters(pageable, condition); log.info(auctions.toString()); + //then assertThat(auctions).hasSize(1); - assertThat(auctions.getContent().get(0).getCategory()).isEqualTo(AuctionCategory.PUBLIC_FREE.name()); - assertThat(auctions.getContent().get(0).getProductCategory()).isEqualTo(ProductCategory.TV.name()); + assertThat(auctions.getContent().get(0).getAuctionCategory()).isEqualTo(AuctionCategory.PUBLIC_FREE); + assertThat(auctions.getContent().get(0).getProduct().getCategory()).isEqualTo(ProductCategory.TV); } 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 38a5de91..966a0587 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 @@ -322,80 +322,83 @@ class GetAuctionsByAuctionCategoryTests { @Test @DisplayName("경매 유형 조회 : [성공]") public void testGetAuctionsByAuctionCategory_Success() { - AuctionRequest.SearchCondition condition = new AuctionRequest.SearchCondition(AuctionCategory.PUBLIC_PAID, null, null, null, null, null, null, null); - + //given + AuctionRequest.SearchCondition condition = new AuctionRequest.SearchCondition(null, null, null, null, null, null, AuctionStatus.PENDING, null); List auctions = List.of( createAuction(1L, seller, AuctionStatus.PENDING), createAuction(2L, seller, AuctionStatus.PENDING) ); - List auctionResponses = auctions - .stream() - .map(AuctionResponse.Details::of) - .toList(); Pageable pageable = PageRequest.of(0, 10); - Page expectedPage = new PageImpl<>(auctionResponses, pageable, auctionResponses.size()); + Page auctionPage = new PageImpl<>(auctions, pageable, auctions.size()); + when(auctionRepository.getAuctionsByFilters(pageable, condition)).thenReturn(auctionPage); - when(auctionRepository.getAuctionsByFilters(pageable, condition)).thenReturn(expectedPage); + //when + Page actualPage = auctionService.getAuctionsByFilter(pageable, condition); - Page actualAuctions = auctionService.getAuctionsByFilter(pageable, condition); - assertThat(actualAuctions).isEqualTo(expectedPage); + //then + assertThat(actualPage.getContent()).hasSize(2); + assertThat(actualPage.getContent()) + .allMatch(auctionResponseDetail -> auctionResponseDetail.getStatus().equals(AuctionStatus.PENDING.name())); } @Test @DisplayName("경매 유형 조회 : [성공] -Criteria 사용") public void testGetAuctionsByAuctionCategory_Success_Criteria() { - AuctionRequest.SearchCondition condition = new AuctionRequest.SearchCondition(AuctionCategory.PUBLIC_PAID, null, null, null, null, null, null, null); - + //given + AuctionRequest.SearchCondition condition = new AuctionRequest.SearchCondition(null, null, null, null, null, null, AuctionStatus.PENDING, null); List auctions = List.of( createAuction(1L, seller, AuctionStatus.PENDING), createAuction(2L, seller, AuctionStatus.PENDING) ); - List auctionResponses = auctions - .stream() - .map(AuctionResponse.Details::of) - .toList(); - Pageable pageable = PageRequest.of(0, 10); - Page expectedPage = new PageImpl<>(auctionResponses, pageable, auctionResponses.size()); + Page auctionPage = new PageImpl<>(auctions, pageable, auctions.size()); + when(auctionRepository.getAuctionsByFilters(pageable, condition)).thenReturn(auctionPage); - when(auctionRepository.getAuctionsByFilters(pageable, condition)).thenReturn(expectedPage); + //when + Page actualPage = auctionService.getAuctionsByFilter(pageable, condition); - Page actualAuctions = auctionService.getAuctionsByFilter(pageable, condition); - assertThat(actualAuctions).isEqualTo(expectedPage); + //then + assertThat(actualPage.getContent()).hasSize(2); + assertThat(actualPage.getContent()) + .allMatch(auctionResponseDetail -> auctionResponseDetail.getStatus().equals(AuctionStatus.PENDING.name())); } @Test @DisplayName("경매 유형 조회 : [결과 없음]") public void testGetAuctionsByAuctionCategory_AuctionNotFound() { - List emptyAuctionList = List.of(); + //given + List emptyAuctionList = List.of(); AuctionRequest.SearchCondition condition = new AuctionRequest.SearchCondition(AuctionCategory.PUBLIC_FREE, null, null, null, null, null, null, null); Pageable pageable = PageRequest.of(0, 10); - Page expectedPage = new PageImpl<>(emptyAuctionList, pageable, 0); - when(auctionRepository.getAuctionsByFilters(pageable, condition)) - .thenReturn(expectedPage); + Page auctionPage = new PageImpl<>(emptyAuctionList, pageable, 0); + when(auctionRepository.getAuctionsByFilters(pageable, condition)).thenReturn(auctionPage); - Page actualAuctions = auctionService.getAuctionsByFilter(pageable, condition); + //when + Page actualPage = auctionService.getAuctionsByFilter(pageable, condition); - assertThat(actualAuctions.isEmpty()).isTrue(); + //then + assertThat(actualPage.isEmpty()).isTrue(); } @Test @DisplayName("경매 유형 조회 : [결과 없음] - Criteria 사용") public void testGetAuctionsByAuctionCategory_AuctionNotFound_Criteria() { - List emptyAuctionList = List.of(); + //given + List emptyAuctionList = List.of(); AuctionRequest.SearchCondition condition = new AuctionRequest.SearchCondition(AuctionCategory.PUBLIC_FREE, null, null, null, null, null, null, null); Pageable pageable = PageRequest.of(0, 10); - Page expectedPage = new PageImpl<>(emptyAuctionList, pageable, 0); - when(auctionRepository.getAuctionsByFilters(pageable, condition)) - .thenReturn(expectedPage); + Page auctionPage = new PageImpl<>(emptyAuctionList, pageable, 0); + when(auctionRepository.getAuctionsByFilters(pageable, condition)).thenReturn(auctionPage); - Page actualAuctions = auctionService.getAuctionsByFilter(pageable, condition); + //when + Page actualPage = auctionService.getAuctionsByFilter(pageable, condition); - assertThat(actualAuctions.isEmpty()).isTrue(); + //then + assertThat(actualPage.isEmpty()).isTrue(); } } 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 89226ece..2972ffef 100644 --- a/src/test/java/com/tasksprints/auction/domain/product/ProductRepositoryTest.java +++ b/src/test/java/com/tasksprints/auction/domain/product/ProductRepositoryTest.java @@ -9,6 +9,7 @@ import com.tasksprints.auction.domain.auction.repository.AuctionRepository; import com.tasksprints.auction.domain.product.model.Product; import com.tasksprints.auction.domain.product.model.ProductCategory; +import com.tasksprints.auction.domain.product.model.ProductImage; import com.tasksprints.auction.domain.product.repository.ProductRepository; import com.tasksprints.auction.domain.user.model.User; import com.tasksprints.auction.domain.user.repository.UserRepository; @@ -125,12 +126,12 @@ public void testFindAllUsingProductCategory() { Pageable pageable = PageRequest.of(0, 10); //when - Page auctionsByFilters = auctionRepository.getAuctionsByFilters(pageable, condition); + Page auctionsByFilters = auctionRepository.getAuctionsByFilters(pageable, condition); //then assertThat(auctionsByFilters).hasSize(1); - assertThat(auctionsByFilters.getContent().get(0).getProductCategory()).isEqualTo("TV"); - assertThat(auctionsByFilters.getContent().get(0).getStatus()).isEqualTo("PENDING"); + assertThat(auctionsByFilters.getContent().get(0).getProduct().getCategory()).isEqualTo(ProductCategory.TV); + assertThat(auctionsByFilters.getContent().get(0).getAuctionStatus()).isEqualTo(AuctionStatus.PENDING); } @@ -155,11 +156,11 @@ public void testFindAllUsingConditionFilter() { AuctionStatus.PENDING, null) ; //when - Page auctionsByCategory = auctionRepository.getAuctionsByCategory(pageable, searchCondition, ProductCategory.DSLR); + Page auctionsByCategory = auctionRepository.getAuctionsByCategory(pageable, searchCondition, ProductCategory.DSLR); //then assertThat(auctionsByCategory).hasSize(1); - assertThat(auctionsByCategory.getContent().get(0).getProductCategory()).isEqualTo("DSLR"); + assertThat(auctionsByCategory.getContent().get(0).getProduct().getCategory()).isEqualTo(ProductCategory.DSLR); }