From e56702885121c4cae783efa1c5c358a35ae01047 Mon Sep 17 00:00:00 2001 From: hi-june Date: Mon, 20 Nov 2023 16:45:59 +0900 Subject: [PATCH 1/5] =?UTF-8?q?NABI-258--feat=20:=20OrderSpecifier(?= =?UTF-8?q?=EC=A0=95=EB=A0=AC=20=EA=B8=B0=EC=A4=80)=EC=9D=84=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=ED=95=98=EB=8A=94=20=EB=A9=94=EC=86=8C=EB=93=9C=20uti?= =?UTF-8?q?l=EC=97=90=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/util/QueryDslUtil.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/main/java/org/prgrms/nabimarketbe/global/util/QueryDslUtil.java b/src/main/java/org/prgrms/nabimarketbe/global/util/QueryDslUtil.java index 0d946a24..d26de98b 100644 --- a/src/main/java/org/prgrms/nabimarketbe/global/util/QueryDslUtil.java +++ b/src/main/java/org/prgrms/nabimarketbe/global/util/QueryDslUtil.java @@ -4,8 +4,35 @@ import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.Path; import com.querydsl.core.types.dsl.Expressions; +import org.springframework.data.domain.Sort; + +import java.util.ArrayList; +import java.util.List; + public class QueryDslUtil { + /** + * 정렬을 기준을 반환하기 위한 메소드. + * 여러 정렬 조건들을 반환할 수 있다. + * + * @param sort Sort 객체 + * @param parent compileQuerydsl 빌드를 통해서 생성된 Q타입 클래스의 객체(Sort의 대상이 되는 Q타입 클래스 객체를 전달한다.) + * @return + */ + public static OrderSpecifier[] getOrderSpecifier( + Sort sort, + Path parent + ) { + List orders = new ArrayList<>(); + + for (Sort.Order order : sort) { // Sort에 여러 정렬 기준이 담겨 올 수 있음 + Order direction = order.getDirection().isAscending() ? Order.ASC : Order.DESC; + orders.add(QueryDslUtil.getSortedColumn(direction, parent, order.getProperty())); + } + + return orders.toArray(OrderSpecifier[]::new); + } + /** * Order, Path, fieldName을 전달하면 OrderSpecifier 객체를 리턴하는 Util 클래스. * Sort시 마다 사용할 수 있도록 한다. From 2d5e19999b4ab4df0d609b182891f05af2cdce19 Mon Sep 17 00:00:00 2001 From: hi-june Date: Mon, 20 Nov 2023 16:46:24 +0900 Subject: [PATCH 2/5] =?UTF-8?q?NABI-258--feat=20:=20=EC=A0=95=EB=A0=AC=20?= =?UTF-8?q?=EC=A1=B0=EA=B1=B4=EC=9D=84=20=EB=B0=9B=EC=9D=84=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EB=8A=94=20enum=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/prgrms/nabimarketbe/global/util/OrderCondition.java | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/main/java/org/prgrms/nabimarketbe/global/util/OrderCondition.java diff --git a/src/main/java/org/prgrms/nabimarketbe/global/util/OrderCondition.java b/src/main/java/org/prgrms/nabimarketbe/global/util/OrderCondition.java new file mode 100644 index 00000000..a2558aef --- /dev/null +++ b/src/main/java/org/prgrms/nabimarketbe/global/util/OrderCondition.java @@ -0,0 +1,5 @@ +package org.prgrms.nabimarketbe.global.util; + +public enum OrderCondition { + CARD_CREATED_DESC +} From 641e56f395a2ee466930c221fd18d73552fa785a Mon Sep 17 00:00:00 2001 From: hi-june Date: Mon, 20 Nov 2023 16:47:03 +0900 Subject: [PATCH 3/5] =?UTF-8?q?NABI-258--feat=20:=20=EC=BB=A4=EC=84=9C=20?= =?UTF-8?q?=EA=B8=B0=EB=B0=98=20paging=EC=97=90=20=EA=B3=B5=ED=86=B5?= =?UTF-8?q?=EC=A0=81=EC=9C=BC=EB=A1=9C=20=ED=95=84=EC=9A=94=ED=95=9C=20int?= =?UTF-8?q?erface=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nabimarketbe/global/util/CursorPaging.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/java/org/prgrms/nabimarketbe/global/util/CursorPaging.java diff --git a/src/main/java/org/prgrms/nabimarketbe/global/util/CursorPaging.java b/src/main/java/org/prgrms/nabimarketbe/global/util/CursorPaging.java new file mode 100644 index 00000000..a5552a12 --- /dev/null +++ b/src/main/java/org/prgrms/nabimarketbe/global/util/CursorPaging.java @@ -0,0 +1,12 @@ +package org.prgrms.nabimarketbe.global.util; + +import com.querydsl.core.types.dsl.BooleanExpression; + +public interface CursorPaging { + /** + * cursorId를 받아 cursor Id로 조건절을 만드는 메소드 + * @param cursorId + * @return + */ + BooleanExpression cursorId(String cursorId); +} From f633dcfc4c345450773fa3e6964c4874fbd01ffe Mon Sep 17 00:00:00 2001 From: hi-june Date: Mon, 20 Nov 2023 16:50:49 +0900 Subject: [PATCH 4/5] =?UTF-8?q?NABI-258--feat=20:=20=EC=A0=95=EB=A0=AC=20?= =?UTF-8?q?=EC=A1=B0=EA=B1=B4=EC=97=90=20=EB=94=B0=EB=9D=BC=20paging=20?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/card/api/CardController.java | 7 ++- .../card/repository/CardRepositoryCustom.java | 3 +- .../card/repository/CardRepositoryImpl.java | 47 +++++++------------ .../domain/card/service/CardService.java | 38 ++++++++++++--- .../nabimarketbe/global/error/ErrorCode.java | 3 +- 5 files changed, 59 insertions(+), 39 deletions(-) diff --git a/src/main/java/org/prgrms/nabimarketbe/domain/card/api/CardController.java b/src/main/java/org/prgrms/nabimarketbe/domain/card/api/CardController.java index 058eb1ec..32ec0fe3 100644 --- a/src/main/java/org/prgrms/nabimarketbe/domain/card/api/CardController.java +++ b/src/main/java/org/prgrms/nabimarketbe/domain/card/api/CardController.java @@ -16,6 +16,7 @@ import org.prgrms.nabimarketbe.domain.card.service.CardService; import org.prgrms.nabimarketbe.domain.category.entity.CategoryEnum; import org.prgrms.nabimarketbe.domain.item.entity.PriceRange; +import org.prgrms.nabimarketbe.global.util.OrderCondition; import org.prgrms.nabimarketbe.global.util.ResponseFactory; import org.prgrms.nabimarketbe.global.util.model.CommonResult; import org.prgrms.nabimarketbe.global.util.model.SingleResult; @@ -76,13 +77,17 @@ public ResponseEntity> getCardsByCondition( @RequestParam(required = false) String cursorId, @RequestParam Integer size ) { + // TODO: 클라이언트에게 정렬 조건 받도록 추후에 수정하면 더 유연할 듯 + OrderCondition condition = OrderCondition.CARD_CREATED_DESC; + CardPagingResponseDTO cardListReadPagingResponseDTO = cardService.getCardsByCondition( category, priceRange, status, cardTitle, cursorId, - size + size, + condition ); return ResponseEntity.ok(ResponseFactory.getSingleResult(cardListReadPagingResponseDTO)); diff --git a/src/main/java/org/prgrms/nabimarketbe/domain/card/repository/CardRepositoryCustom.java b/src/main/java/org/prgrms/nabimarketbe/domain/card/repository/CardRepositoryCustom.java index 59c31c8c..c26acd1f 100644 --- a/src/main/java/org/prgrms/nabimarketbe/domain/card/repository/CardRepositoryCustom.java +++ b/src/main/java/org/prgrms/nabimarketbe/domain/card/repository/CardRepositoryCustom.java @@ -8,6 +8,7 @@ import org.prgrms.nabimarketbe.domain.category.entity.CategoryEnum; import org.prgrms.nabimarketbe.domain.item.entity.PriceRange; import org.prgrms.nabimarketbe.domain.user.entity.User; +import org.springframework.data.domain.Pageable; public interface CardRepositoryCustom { CardPagingResponseDTO getCardsByCondition( @@ -16,7 +17,7 @@ CardPagingResponseDTO getCardsByCondition( List status, String title, String cursorId, - Integer size + Pageable pageable ); CardPagingResponseDTO getMyCardsByStatus( diff --git a/src/main/java/org/prgrms/nabimarketbe/domain/card/repository/CardRepositoryImpl.java b/src/main/java/org/prgrms/nabimarketbe/domain/card/repository/CardRepositoryImpl.java index 70a39ff6..db01e89c 100644 --- a/src/main/java/org/prgrms/nabimarketbe/domain/card/repository/CardRepositoryImpl.java +++ b/src/main/java/org/prgrms/nabimarketbe/domain/card/repository/CardRepositoryImpl.java @@ -1,11 +1,10 @@ package org.prgrms.nabimarketbe.domain.card.repository; import com.querydsl.core.types.ConstantImpl; -import com.querydsl.core.types.Order; -import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.Projections; import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.core.types.dsl.Expressions; +import com.querydsl.core.types.dsl.StringExpression; import com.querydsl.core.types.dsl.StringExpressions; import com.querydsl.core.types.dsl.StringTemplate; import com.querydsl.jpa.impl.JPAQueryFactory; @@ -19,10 +18,10 @@ import org.prgrms.nabimarketbe.domain.item.entity.PriceRange; import org.prgrms.nabimarketbe.domain.suggestion.dto.response.projection.SuggestionInfo; import org.prgrms.nabimarketbe.domain.user.entity.User; +import org.prgrms.nabimarketbe.global.util.CursorPaging; import org.prgrms.nabimarketbe.global.util.QueryDslUtil; -import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Pageable; -import java.util.ArrayList; import java.util.List; import static org.prgrms.nabimarketbe.domain.card.entity.QCard.card; @@ -30,7 +29,7 @@ import static org.prgrms.nabimarketbe.domain.suggestion.entity.QSuggestion.suggestion; @RequiredArgsConstructor -public class CardRepositoryImpl implements CardRepositoryCustom { +public class CardRepositoryImpl implements CardRepositoryCustom, CursorPaging { private final JPAQueryFactory jpaQueryFactory; @Override @@ -40,7 +39,7 @@ public CardPagingResponseDTO getCardsByCondition( List status, String cardTitle, String cursorId, - Integer size + Pageable pageable ) { List cardList = jpaQueryFactory.select( Projections.fields( @@ -64,14 +63,11 @@ public CardPagingResponseDTO getCardsByCondition( priceRangeEquals(priceRange), titleEquals(cardTitle) ) - .orderBy(getOrderSpecifier(Sort.by( - Sort.Order.desc("createdDate"), - Sort.Order.desc("cardId") - ))) - .limit(size) + .orderBy(QueryDslUtil.getOrderSpecifier(pageable.getSort(), card)) + .limit(pageable.getPageSize()) .fetch(); - String nextCursor = cardList.size() < size ? null : generateCursor(cardList.get(cardList.size() - 1)); + String nextCursor = cardList.size() < pageable.getPageSize() ? null : generateCursor(cardList.get(cardList.size() - 1)); return new CardPagingResponseDTO(cardList, nextCursor); } @@ -146,23 +142,27 @@ public List getSuggestionAvailableCards( return cardList; } - private BooleanExpression cursorId(String cursorId) { + @Override + public BooleanExpression cursorId(String cursorId) { if (cursorId == null) { return null; } - StringTemplate stringTemplate = Expressions.stringTemplate( + // 생성일자 + StringTemplate dateCursorTemplate = Expressions.stringTemplate( "DATE_FORMAT({0}, {1})", - card.createdDate, // 디폴트는 생성일자 최신순 정렬 + card.createdDate, ConstantImpl.create("%Y%m%d%H%i%s") ); - return stringTemplate.concat(StringExpressions.lpad( + // pk + StringExpression pkCursorTemplate = StringExpressions.lpad( card.cardId.stringValue(), 8, '0' - )) - .lt(cursorId); + ); + + return dateCursorTemplate.concat(pkCursorTemplate).lt(cursorId); } private BooleanExpression categoryEquals(CategoryEnum category) { @@ -208,15 +208,4 @@ private String generateCursor(CardListReadResponseDTO cardListReadResponseDTO) { .replace(":", "") + String.format("%08d", cardListReadResponseDTO.getCardId()); } - - private OrderSpecifier[] getOrderSpecifier(Sort sort) { - List orders = new ArrayList<>(); - - for (Sort.Order order : sort) { // Sort에 여러 정렬 기준을 담을 수 있음 - Order direction = order.getDirection().isAscending() ? Order.ASC : Order.DESC; - orders.add(QueryDslUtil.getSortedColumn(direction, card, order.getProperty())); - } - - return orders.toArray(OrderSpecifier[]::new); - } } diff --git a/src/main/java/org/prgrms/nabimarketbe/domain/card/service/CardService.java b/src/main/java/org/prgrms/nabimarketbe/domain/card/service/CardService.java index ba8d13be..8ae37219 100644 --- a/src/main/java/org/prgrms/nabimarketbe/domain/card/service/CardService.java +++ b/src/main/java/org/prgrms/nabimarketbe/domain/card/service/CardService.java @@ -32,6 +32,9 @@ import org.prgrms.nabimarketbe.domain.user.service.CheckService; import org.prgrms.nabimarketbe.global.error.BaseException; import org.prgrms.nabimarketbe.global.error.ErrorCode; +import org.prgrms.nabimarketbe.global.util.OrderCondition; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -202,20 +205,29 @@ public CardUserResponseDTO getCardById( @Transactional(readOnly = true) public CardPagingResponseDTO getCardsByCondition( - CategoryEnum category, - PriceRange priceRange, - List status, - String title, - String cursorId, - Integer size + CategoryEnum category, + PriceRange priceRange, + List status, + String title, + String cursorId, + Integer size, + OrderCondition orderCondition ) { + + // 전달 받은 orderCondition 과 page size 에 맞게 pageRequest 를 구성 후 repo 에 넘김 + PageRequest pageRequest = PageRequest.of( + 0, + size, + getSortFromOrderCondition(orderCondition) + ); + return cardRepository.getCardsByCondition( category, priceRange, status, title, cursorId, - size + pageRequest ); } @@ -367,4 +379,16 @@ private List addThumbnail( return newCardImages; } + + private Sort getSortFromOrderCondition(OrderCondition orderCondition) { + switch (orderCondition) { + case CARD_CREATED_DESC -> { + return Sort.by( + Sort.Order.desc("createdDate"), + Sort.Order.desc("cardId") + ); + } + default -> throw new BaseException(ErrorCode.INVALID_ORDER_CONDITION); + } + } } diff --git a/src/main/java/org/prgrms/nabimarketbe/global/error/ErrorCode.java b/src/main/java/org/prgrms/nabimarketbe/global/error/ErrorCode.java index 5a402e6d..4075523a 100644 --- a/src/main/java/org/prgrms/nabimarketbe/global/error/ErrorCode.java +++ b/src/main/java/org/prgrms/nabimarketbe/global/error/ErrorCode.java @@ -24,7 +24,8 @@ public enum ErrorCode { CATEGORY_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR, "CA0001", "존재하지 않는 카테고리입니다."), COMPLETE_REQUEST_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR, "CR0001", "존재하지 않는 제안 요청입니다."), COMPLETE_REQUEST_MYSELF_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "CR0002", "자신의 카드에는 거래 성사 요청을 할 수 없습니다."), - BATCH_INSERT_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "B0001", "이미지 저장 중에 문제가 발생했습니다."); + BATCH_INSERT_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "B0001", "이미지 저장 중에 문제가 발생했습니다."), + INVALID_ORDER_CONDITION(HttpStatus.INTERNAL_SERVER_ERROR, "O0001", "유효하지 않은 정렬 조건입니다."); private HttpStatus status; private String code; From ca052ff378c9225b24e4f235ebaae07fbea12bf6 Mon Sep 17 00:00:00 2001 From: hi-june Date: Mon, 20 Nov 2023 18:30:22 +0900 Subject: [PATCH 5/5] =?UTF-8?q?NABI-258--hot-fix=20:=20=EC=A0=95=EB=A0=AC?= =?UTF-8?q?=20=EC=A1=B0=EA=B1=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../card/repository/CardRepositoryImpl.java | 11 +++++++++- .../CompleteRequestRepositoryImpl.java | 22 +++++++++++++++++-- .../repository/SuggestionRepositoryImpl.java | 13 ++++++++++- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/prgrms/nabimarketbe/domain/card/repository/CardRepositoryImpl.java b/src/main/java/org/prgrms/nabimarketbe/domain/card/repository/CardRepositoryImpl.java index db01e89c..6049a09a 100644 --- a/src/main/java/org/prgrms/nabimarketbe/domain/card/repository/CardRepositoryImpl.java +++ b/src/main/java/org/prgrms/nabimarketbe/domain/card/repository/CardRepositoryImpl.java @@ -21,6 +21,7 @@ import org.prgrms.nabimarketbe.global.util.CursorPaging; import org.prgrms.nabimarketbe.global.util.QueryDslUtil; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import java.util.List; @@ -99,7 +100,15 @@ public CardPagingResponseDTO getMyCardsByStatus( card.status.eq(status), card.user.eq(user) ) - .orderBy(card.createdDate.desc()) // 디폴트는 생성일자 최신순 정렬 + .orderBy( + QueryDslUtil.getOrderSpecifier( + Sort.by( + Sort.Order.desc("createdDate"), + Sort.Order.desc("cardId") + ), + card + ) + ) .limit(size) .fetch(); diff --git a/src/main/java/org/prgrms/nabimarketbe/domain/completeRequest/repository/CompleteRequestRepositoryImpl.java b/src/main/java/org/prgrms/nabimarketbe/domain/completeRequest/repository/CompleteRequestRepositoryImpl.java index 4c7877c5..c6fc372b 100644 --- a/src/main/java/org/prgrms/nabimarketbe/domain/completeRequest/repository/CompleteRequestRepositoryImpl.java +++ b/src/main/java/org/prgrms/nabimarketbe/domain/completeRequest/repository/CompleteRequestRepositoryImpl.java @@ -22,6 +22,8 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.prgrms.nabimarketbe.global.util.QueryDslUtil; +import org.springframework.data.domain.Sort; @Slf4j @RequiredArgsConstructor @@ -57,7 +59,15 @@ public HistoryListReadLimitResponseDTO getHistoryBySize(Integer size) { .leftJoin(card).on(card.cardId.eq(completeRequest.fromCard.cardId)) .leftJoin(card).on(card.cardId.eq(completeRequest.toCard.cardId).as("toCardAlias")) .where(completeRequest.completeRequestStatus.eq(CompleteRequestStatus.ACCEPTED)) - .orderBy(completeRequest.modifiedDate.desc()) + .orderBy( + QueryDslUtil.getOrderSpecifier( + Sort.by( + Sort.Order.desc("modifiedDate"), + Sort.Order.desc("completeRequestId") + ), + completeRequest + ) + ) .limit(size) .fetch(); @@ -99,7 +109,15 @@ public HistoryListReadPagingResponseDTO getHistoryByUser( .where(cursorIdLessThan(cursorId), completeRequest.completeRequestStatus.eq(CompleteRequestStatus.ACCEPTED), completeRequest.fromCard.user.eq(user).or(completeRequest.toCard.user.eq(user))) - .orderBy(completeRequest.modifiedDate.desc()) + .orderBy( + QueryDslUtil.getOrderSpecifier( + Sort.by( + Sort.Order.desc("modifiedDate"), + Sort.Order.desc("completeRequestId") + ), + completeRequest + ) + ) .limit(size) .fetch(); diff --git a/src/main/java/org/prgrms/nabimarketbe/domain/suggestion/repository/SuggestionRepositoryImpl.java b/src/main/java/org/prgrms/nabimarketbe/domain/suggestion/repository/SuggestionRepositoryImpl.java index bc183482..84ef3823 100644 --- a/src/main/java/org/prgrms/nabimarketbe/domain/suggestion/repository/SuggestionRepositoryImpl.java +++ b/src/main/java/org/prgrms/nabimarketbe/domain/suggestion/repository/SuggestionRepositoryImpl.java @@ -17,10 +17,13 @@ import org.prgrms.nabimarketbe.domain.suggestion.entity.SuggestionType; import org.prgrms.nabimarketbe.global.error.BaseException; import org.prgrms.nabimarketbe.global.error.ErrorCode; +import org.prgrms.nabimarketbe.global.util.QueryDslUtil; +import org.springframework.data.domain.Sort; import java.util.List; import static org.prgrms.nabimarketbe.domain.card.entity.QCard.card; +import static org.prgrms.nabimarketbe.domain.completeRequest.entity.QCompleteRequest.completeRequest; import static org.prgrms.nabimarketbe.domain.suggestion.entity.QSuggestion.suggestion; @RequiredArgsConstructor @@ -62,7 +65,15 @@ public SuggestionListReadPagingResponseDTO getSuggestionsByType( .join(getQcardByDirectionType(directionType), card) .on(getExpressionByDirectionType(directionType, cardId)) .where(cursorIdLessThan(cursorId), suggestionTypeEquals(suggestionType)) - .orderBy(suggestion.createdDate.desc()) + .orderBy( + QueryDslUtil.getOrderSpecifier( + Sort.by( + Sort.Order.desc("createdDate"), + Sort.Order.desc("suggestionId") + ), + suggestion + ) + ) .limit(size) .fetch();