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..158c761a 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 @@ -1,17 +1,20 @@ package org.prgrms.nabimarketbe.domain.card.api; -import java.util.List; - +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.RequiredArgsConstructor; import org.prgrms.nabimarketbe.domain.card.dto.request.CardCreateRequestDTO; import org.prgrms.nabimarketbe.domain.card.dto.request.CardStatusUpdateRequestDTO; import org.prgrms.nabimarketbe.domain.card.dto.request.CardUpdateRequestDTO; import org.prgrms.nabimarketbe.domain.card.dto.response.CardCreateResponseDTO; -import org.prgrms.nabimarketbe.domain.card.dto.response.wrapper.CardPagingResponseDTO; +import org.prgrms.nabimarketbe.domain.card.dto.response.CardUpdateResponseDTO; +import org.prgrms.nabimarketbe.domain.card.dto.response.projection.CardFamousResponseDTO; import org.prgrms.nabimarketbe.domain.card.dto.response.wrapper.CardListResponseDTO; +import org.prgrms.nabimarketbe.domain.card.dto.response.wrapper.CardPagingResponseDTO; import org.prgrms.nabimarketbe.domain.card.dto.response.wrapper.CardResponseDTO; -import org.prgrms.nabimarketbe.domain.card.dto.response.wrapper.CardUserResponseDTO; -import org.prgrms.nabimarketbe.domain.card.dto.response.CardUpdateResponseDTO; import org.prgrms.nabimarketbe.domain.card.dto.response.wrapper.CardSuggestionResponseDTO; +import org.prgrms.nabimarketbe.domain.card.dto.response.wrapper.CardUserResponseDTO; import org.prgrms.nabimarketbe.domain.card.entity.CardStatus; import org.prgrms.nabimarketbe.domain.card.service.CardService; import org.prgrms.nabimarketbe.domain.category.entity.CategoryEnum; @@ -31,10 +34,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.enums.ParameterIn; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.RequiredArgsConstructor; +import java.util.List; @RestController @RequestMapping("/api/v1/cards") @@ -58,31 +58,31 @@ public ResponseEntity>> crea return ResponseEntity.ok(ResponseFactory.getSingleResult(card)); } - @GetMapping("/{cardId}") - public ResponseEntity> getCardById( - @RequestHeader(value = "Authorization", required = false) String token, - @PathVariable Long cardId) { - CardUserResponseDTO cardSingleReadResponseDTO = cardService.getCardById(token, cardId); + @GetMapping("/{cardId}") + public ResponseEntity> getCardById( + @RequestHeader(value = "Authorization", required = false) String token, + @PathVariable Long cardId) { + CardUserResponseDTO cardSingleReadResponseDTO = cardService.getCardById(token, cardId); - return ResponseEntity.ok(ResponseFactory.getSingleResult(cardSingleReadResponseDTO)); - } + return ResponseEntity.ok(ResponseFactory.getSingleResult(cardSingleReadResponseDTO)); + } @GetMapping public ResponseEntity> getCardsByCondition( - @RequestParam(required = false) CategoryEnum category, - @RequestParam(required = false) PriceRange priceRange, - @RequestParam(required = false) List status, - @RequestParam(required = false) String cardTitle, - @RequestParam(required = false) String cursorId, - @RequestParam Integer size + @RequestParam(required = false) CategoryEnum category, + @RequestParam(required = false) PriceRange priceRange, + @RequestParam(required = false) List status, + @RequestParam(required = false) String cardTitle, + @RequestParam(required = false) String cursorId, + @RequestParam Integer size ) { CardPagingResponseDTO cardListReadPagingResponseDTO = cardService.getCardsByCondition( - category, - priceRange, - status, - cardTitle, - cursorId, - size + category, + priceRange, + status, + cardTitle, + cursorId, + size ); return ResponseEntity.ok(ResponseFactory.getSingleResult(cardListReadPagingResponseDTO)); @@ -90,11 +90,11 @@ public ResponseEntity> getCardsByCondition( @GetMapping("/{cardId}/available-cards") public ResponseEntity>> getSuggestionAvailableCards( - @RequestHeader(name = "Authorization") String token, - @PathVariable(name = "cardId") Long targetCardId + @RequestHeader(name = "Authorization") String token, + @PathVariable(name = "cardId") Long targetCardId ) { CardListResponseDTO cardListResponseDTO - = cardService.getSuggestionAvailableCards(token, targetCardId); + = cardService.getSuggestionAvailableCards(token, targetCardId); return ResponseEntity.ok(ResponseFactory.getSingleResult(cardListResponseDTO)); } @@ -116,9 +116,9 @@ public ResponseEntity>> upda @PutMapping("/status/{cardId}") public ResponseEntity updateCardStatusById( - @RequestHeader(name = "authorization") String token, - @PathVariable Long cardId, - @RequestBody CardStatusUpdateRequestDTO cardStatusUpdateRequestDTO + @RequestHeader(name = "authorization") String token, + @PathVariable Long cardId, + @RequestBody CardStatusUpdateRequestDTO cardStatusUpdateRequestDTO ) { cardService.updateCardStatusById( token, @@ -131,16 +131,16 @@ public ResponseEntity updateCardStatusById( @GetMapping("/{status}/my-cards") public ResponseEntity> getMyCardsByStatus( - @RequestHeader(name = "authorization") String token, - @PathVariable CardStatus status, - @RequestParam(required = false) String cursorId, - @RequestParam Integer size + @RequestHeader(name = "authorization") String token, + @PathVariable CardStatus status, + @RequestParam(required = false) String cursorId, + @RequestParam Integer size ) { CardPagingResponseDTO cardListReadPagingResponseDTO = cardService.getMyCardsByStatus( - token, - status, - cursorId, - size + token, + status, + cursorId, + size ); return ResponseEntity.ok(ResponseFactory.getSingleResult(cardListReadPagingResponseDTO)); @@ -148,11 +148,18 @@ public ResponseEntity> getMyCardsByStatus( @DeleteMapping("/{cardId}") public ResponseEntity deleteCardById( - @RequestHeader(name = "authorization") String token, - @PathVariable Long cardId + @RequestHeader(name = "authorization") String token, + @PathVariable Long cardId ) { cardService.deleteCardById(token, cardId); return ResponseEntity.ok(ResponseFactory.getSuccessResult()); } + + @GetMapping("/popular") + public ResponseEntity>> getCardsByPopularity() { + CardListResponseDTO cardList = cardService.getCardsByPopularity(); + + return ResponseEntity.ok(ResponseFactory.getSingleResult(cardList)); + } } diff --git a/src/main/java/org/prgrms/nabimarketbe/domain/card/dto/response/projection/CardFamousResponseDTO.java b/src/main/java/org/prgrms/nabimarketbe/domain/card/dto/response/projection/CardFamousResponseDTO.java new file mode 100644 index 00000000..18f22858 --- /dev/null +++ b/src/main/java/org/prgrms/nabimarketbe/domain/card/dto/response/projection/CardFamousResponseDTO.java @@ -0,0 +1,15 @@ +package org.prgrms.nabimarketbe.domain.card.dto.response.projection; + +import lombok.Getter; +import org.prgrms.nabimarketbe.domain.item.entity.PriceRange; + +@Getter +public class CardFamousResponseDTO { + private Long cardId; + + private String itemName; + + private PriceRange priceRange; + + private String thumbnail; +} 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..0e8c18eb 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 @@ -1,7 +1,6 @@ package org.prgrms.nabimarketbe.domain.card.repository; -import java.util.List; - +import org.prgrms.nabimarketbe.domain.card.dto.response.projection.CardFamousResponseDTO; import org.prgrms.nabimarketbe.domain.card.dto.response.wrapper.CardPagingResponseDTO; import org.prgrms.nabimarketbe.domain.card.dto.response.wrapper.CardSuggestionResponseDTO; import org.prgrms.nabimarketbe.domain.card.entity.CardStatus; @@ -9,25 +8,29 @@ import org.prgrms.nabimarketbe.domain.item.entity.PriceRange; import org.prgrms.nabimarketbe.domain.user.entity.User; +import java.util.List; + public interface CardRepositoryCustom { 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 ); CardPagingResponseDTO getMyCardsByStatus( - User user, - CardStatus status, - String cursorId, - Integer size + User user, + CardStatus status, + String cursorId, + Integer size ); List getSuggestionAvailableCards( - Long userId, - Long targetCardId + Long userId, + Long targetCardId ); + + List getCardsByPopularity(); } 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..0b543547 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 @@ -10,6 +10,7 @@ import com.querydsl.core.types.dsl.StringTemplate; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; +import org.prgrms.nabimarketbe.domain.card.dto.response.projection.CardFamousResponseDTO; import org.prgrms.nabimarketbe.domain.card.dto.response.projection.CardInfoResponseDTO; import org.prgrms.nabimarketbe.domain.card.dto.response.projection.CardListReadResponseDTO; import org.prgrms.nabimarketbe.domain.card.dto.response.wrapper.CardPagingResponseDTO; @@ -31,6 +32,8 @@ @RequiredArgsConstructor public class CardRepositoryImpl implements CardRepositoryCustom { + private static final int FAMOUS_CARD_SIZE = 5; + private final JPAQueryFactory jpaQueryFactory; @Override @@ -65,8 +68,8 @@ public CardPagingResponseDTO getCardsByCondition( titleEquals(cardTitle) ) .orderBy(getOrderSpecifier(Sort.by( - Sort.Order.desc("createdDate"), - Sort.Order.desc("cardId") + Sort.Order.desc("createdDate"), + Sort.Order.desc("cardId") ))) .limit(size) .fetch(); @@ -146,6 +149,35 @@ public List getSuggestionAvailableCards( return cardList; } + @Override + public List getCardsByPopularity() { + List cardList = jpaQueryFactory + .select( + Projections.fields( + CardFamousResponseDTO.class, + card.cardId, + card.item.itemName, + card.item.priceRange, + card.thumbnail + ) + ) + .from(card) + .where(statusEquals(CardStatus.TRADE_AVAILABLE)) + .orderBy(card.viewCount.desc(), card.dibCount.desc()) + .limit(FAMOUS_CARD_SIZE) + .fetch(); + + return cardList; + } + + private BooleanExpression statusEquals(CardStatus status) { + if (status == null) { + return null; + } + + return card.status.eq(status); + } + private BooleanExpression cursorId(String cursorId) { if (cursorId == null) { return null; 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..2b03553f 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 @@ -7,6 +7,7 @@ import org.prgrms.nabimarketbe.domain.card.dto.response.CardCreateResponseDTO; import org.prgrms.nabimarketbe.domain.card.dto.response.CardDetailResponseDTO; import org.prgrms.nabimarketbe.domain.card.dto.response.CardUpdateResponseDTO; +import org.prgrms.nabimarketbe.domain.card.dto.response.projection.CardFamousResponseDTO; import org.prgrms.nabimarketbe.domain.card.dto.response.wrapper.CardListResponseDTO; import org.prgrms.nabimarketbe.domain.card.dto.response.wrapper.CardPagingResponseDTO; import org.prgrms.nabimarketbe.domain.card.dto.response.wrapper.CardResponseDTO; @@ -161,7 +162,7 @@ public CardResponseDTO updateCardById( return new CardResponseDTO<>(cardUpdateResponseDTO); } - @Transactional(readOnly = true) + @Transactional public CardUserResponseDTO getCardById( String token, Long cardId @@ -317,7 +318,7 @@ public List getSuggestionResultCardList( ) { Card targetCard = cardRepository.findById(targetId) .orElseThrow(() -> new BaseException(ErrorCode.CARD_NOT_FOUND)); - + Boolean pokeAvailable = targetCard.getPokeAvailable(); PriceRange priceRange = targetCard.getItem().getPriceRange(); @@ -327,6 +328,14 @@ public List getSuggestionResultCardList( return parseCardListWithOnlyOffer(cardList, priceRange); } + @Transactional(readOnly = true) + public CardListResponseDTO getCardsByPopularity() { + List cardList = cardRepository.getCardsByPopularity(); + CardListResponseDTO response = new CardListResponseDTO<>(cardList); + + return response; + } + private List parseCardListWithPokeAndOffer( List cardList, PriceRange targetPriceRange diff --git a/src/main/java/org/prgrms/nabimarketbe/global/error/GlobalExceptionHandler.java b/src/main/java/org/prgrms/nabimarketbe/global/error/GlobalExceptionHandler.java index 03c88dcf..a7306784 100644 --- a/src/main/java/org/prgrms/nabimarketbe/global/error/GlobalExceptionHandler.java +++ b/src/main/java/org/prgrms/nabimarketbe/global/error/GlobalExceptionHandler.java @@ -17,11 +17,11 @@ public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) protected ResponseEntity defaultException(Exception e) { log.error(e.getMessage()); - + return new ResponseEntity<>( ResponseFactory.getFailResult( - ErrorCode.UNKNOWN.getCode(), - ErrorCode.UNKNOWN.getMessage() + ErrorCode.UNKNOWN.getCode(), + ErrorCode.UNKNOWN.getMessage() ), HttpStatus.INTERNAL_SERVER_ERROR ); @@ -29,12 +29,14 @@ protected ResponseEntity defaultException(Exception e) { @ExceptionHandler(BaseException.class) protected ResponseEntity handleCustomException(BaseException e) { + log.error(e.getErrorCode().getMessage()); + ErrorCode errorCode = e.getErrorCode(); return new ResponseEntity<>( ResponseFactory.getFailResult( - errorCode.getCode(), - errorCode.getMessage() + errorCode.getCode(), + errorCode.getMessage() ), errorCode.getStatus() ); @@ -42,14 +44,16 @@ protected ResponseEntity handleCustomException(BaseException e) { @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity handleValidationExceptions(MethodArgumentNotValidException e) { + log.error(e.getMessage()); + BindingResult bindingResult = e.getBindingResult(); String message = bindingResult.getFieldError().getDefaultMessage(); return new ResponseEntity<>( ResponseFactory.getFailResult( - ErrorCode.INVALID_REQUEST.getCode(), - message), + ErrorCode.INVALID_REQUEST.getCode(), + message), HttpStatus.BAD_REQUEST ); } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 268236c1..f08c04e6 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,16 +1,6 @@ spring: profiles: - active: dev - - ## .sql문을 실행하기 위함 - sql: - init: - mode: always - - ## hibernate ddl 이후 datasource intialize - jpa: - defer-datasource-initialization: true - + active: local springdoc: version: '@springdoc.version@' diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index 545f0b5c..d153d878 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -1,9 +1,9 @@ -insert ignore into categories(category_name) values ('MALE_CLOTHES'); -insert ignore into categories(category_name) values ('FEMALE_CLOTHES'); -insert ignore into categories(category_name) values ('GOODS_ACCESSORY'); -insert ignore into categories(category_name) values ('SHOES'); -insert ignore into categories(category_name) values ('SPORTS'); -insert ignore into categories(category_name) values ('BOOKS'); -insert ignore into categories(category_name) values ('ELECTRONICS'); -insert ignore into categories(category_name) values ('FURNITURE_INTERIOR'); -insert ignore into categories(category_name) values ('HOME_ELECTRONICS'); +insert ignore into categories(category_id, category_name) values (1, 'MALE_CLOTHES'); +insert ignore into categories(category_id, category_name) values (2, 'FEMALE_CLOTHES'); +insert ignore into categories(category_id, category_name) values (3, 'GOODS_ACCESSORY'); +insert ignore into categories(category_id, category_name) values (4, 'SHOES'); +insert ignore into categories(category_id, category_name) values (5, 'SPORTS'); +insert ignore into categories(category_id, category_name) values (6, 'BOOKS'); +insert ignore into categories(category_id, category_name) values (7, 'ELECTRONICS'); +insert ignore into categories(category_id, category_name) values (8, 'FURNITURE_INTERIOR'); +insert ignore into categories(category_id, category_name) values (9, 'HOME_ELECTRONICS');