diff --git a/src/main/java/kr/co/knuserver/application/booth/BoothLikeService.java b/src/main/java/kr/co/knuserver/application/booth/BoothLikeService.java index 5118c9b..7e4a308 100644 --- a/src/main/java/kr/co/knuserver/application/booth/BoothLikeService.java +++ b/src/main/java/kr/co/knuserver/application/booth/BoothLikeService.java @@ -84,6 +84,15 @@ public Set> getRanking() { } } + public Set> getTopRanking(int limit) { + try { + return redisTemplate.opsForZSet().reverseRangeWithScores(RANKING_KEY, 0, limit - 1); + } catch (DataAccessException e) { + log.warn("[Ranking] Redis 조회 실패, 빈 셋 반환", e); + return Collections.emptySet(); + } + } + private int getLikeMultiplier() { if (!doubleEventEnabled) { return 1; diff --git a/src/main/java/kr/co/knuserver/application/booth/BoothQueryService.java b/src/main/java/kr/co/knuserver/application/booth/BoothQueryService.java index b7b145c..90e632c 100644 --- a/src/main/java/kr/co/knuserver/application/booth/BoothQueryService.java +++ b/src/main/java/kr/co/knuserver/application/booth/BoothQueryService.java @@ -17,6 +17,7 @@ import kr.co.knuserver.presentation.booth.dto.BoothInfoResponseDto; import kr.co.knuserver.presentation.booth.dto.BoothListResponseDto; import kr.co.knuserver.presentation.booth.dto.BoothRankingResponseDto; +import kr.co.knuserver.presentation.booth.dto.BoothTop3ResponseDto; import org.springframework.data.redis.core.ZSetOperations; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.PageRequest; @@ -96,7 +97,31 @@ public List getBoothRanking() { .toList(); } - + public List getTop3BoothRanking() { + Set> rawRanking = boothLikeService.getTopRanking(10); + if (rawRanking == null || rawRanking.isEmpty()) { + return List.of(); + } + + BoothRanking boothRanking = new BoothRanking(rawRanking); + if (boothRanking.isEmpty()) { + return List.of(); + } + + List booths = boothRepository.findAllById(boothRanking.boothIds()); + + return booths.stream() + .map(booth -> new BoothTop3ResponseDto( + booth.getId(), + booth.getName(), + boothRanking.getLikeCount(booth.getId()) + )) + .sorted(Comparator.comparingLong(BoothTop3ResponseDto::likeCount).reversed() + .thenComparingLong(BoothTop3ResponseDto::boothId)) + .limit(3) + .toList(); + } + public CursorPaginationResponse searchBoothsByKeyword2(String keyword, Long lastId, int size) { Pageable pageable = PageRequest.of(0, size + 1); List booths; diff --git a/src/main/java/kr/co/knuserver/presentation/booth/BoothApiController.java b/src/main/java/kr/co/knuserver/presentation/booth/BoothApiController.java index e38e0c4..8d4f585 100644 --- a/src/main/java/kr/co/knuserver/presentation/booth/BoothApiController.java +++ b/src/main/java/kr/co/knuserver/presentation/booth/BoothApiController.java @@ -11,6 +11,7 @@ import kr.co.knuserver.presentation.booth.dto.BoothInfoResponseDto; import kr.co.knuserver.presentation.booth.dto.BoothListResponseDto; import kr.co.knuserver.presentation.booth.dto.BoothRankingResponseDto; +import kr.co.knuserver.presentation.booth.dto.BoothTop3ResponseDto; import kr.co.knuserver.presentation.booth.dto.BoothUpdateRequestDto; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; @@ -80,9 +81,17 @@ public ResponseEntity .body(ApiResponse.success(result)); } + @Override @GetMapping("/ranking") public ResponseEntity>> getBoothRanking() { List result = boothQueryService.getBoothRanking(); return ResponseEntity.ok(ApiResponse.success(result)); } + + @Override + @GetMapping("/ranking/top3") + public ResponseEntity>> getTop3BoothRanking() { + List result = boothQueryService.getTop3BoothRanking(); + return ResponseEntity.ok(ApiResponse.success(result)); + } } diff --git a/src/main/java/kr/co/knuserver/presentation/booth/docs/BoothApiControllerDocs.java b/src/main/java/kr/co/knuserver/presentation/booth/docs/BoothApiControllerDocs.java index ecbfbb3..2f9f9f7 100644 --- a/src/main/java/kr/co/knuserver/presentation/booth/docs/BoothApiControllerDocs.java +++ b/src/main/java/kr/co/knuserver/presentation/booth/docs/BoothApiControllerDocs.java @@ -11,6 +11,8 @@ import kr.co.knuserver.presentation.booth.dto.BoothCountResponseDto; import kr.co.knuserver.presentation.booth.dto.BoothInfoResponseDto; import kr.co.knuserver.presentation.booth.dto.BoothListResponseDto; +import kr.co.knuserver.presentation.booth.dto.BoothRankingResponseDto; +import kr.co.knuserver.presentation.booth.dto.BoothTop3ResponseDto; import kr.co.knuserver.presentation.booth.dto.BoothUpdateRequestDto; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; @@ -60,4 +62,16 @@ ResponseEntity>> getBoothRanking(); + + @Operation(summary = "가두모집 부스 좋아요 TOP 3 조회", description = "좋아요 수 기준 상위 3개 부스를 반환합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "TOP 3 랭킹 조회 성공") + }) + ResponseEntity>> getTop3BoothRanking(); } diff --git a/src/main/java/kr/co/knuserver/presentation/booth/dto/BoothTop3ResponseDto.java b/src/main/java/kr/co/knuserver/presentation/booth/dto/BoothTop3ResponseDto.java new file mode 100644 index 0000000..b8185bc --- /dev/null +++ b/src/main/java/kr/co/knuserver/presentation/booth/dto/BoothTop3ResponseDto.java @@ -0,0 +1,7 @@ +package kr.co.knuserver.presentation.booth.dto; + +public record BoothTop3ResponseDto( + Long boothId, + String boothName, + long likeCount +) {}