Skip to content

Commit 6f4a795

Browse files
committed
feat: 주간·월간 랭킹 API 추가 및 RankingService 인터페이스 구현
- RankingService 인터페이스를 구현한 RankingServiceImpl 추가 - 주간·월간 랭킹 조회를 위한 getWeeklyRankings, getMonthlyRankings 메서드 구현 - 입력값에 따라 랭킹 타입을 동적으로 분기하도록 RankingFacade 개선 - DAILY, WEEKLY, MONTHLY 랭킹 타입을 지원하도록 RankingV1ApiSpec 및 RankingV1Controller 확장
1 parent e0ef326 commit 6f4a795

6 files changed

Lines changed: 83 additions & 37 deletions

File tree

apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
import com.loopers.domain.brand.BrandService;
44
import com.loopers.domain.product.Product;
55
import com.loopers.domain.product.ProductService;
6-
import com.loopers.infrastructure.rank.RankingService;
76
import com.loopers.event.ProductViewEvent;
7+
import com.loopers.infrastructure.rank.RankingService;
88
import lombok.RequiredArgsConstructor;
99
import org.springframework.context.ApplicationEventPublisher;
1010
import org.springframework.data.domain.Page;

apps/commerce-api/src/main/java/com/loopers/application/rank/RankingFacade.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,12 @@ public List<RankingInfo> getTopRankings(String date, int page, int size) {
3939
})
4040
.toList();
4141
}
42+
43+
public List<RankingInfo> getRankings(String type, String date, int page, int size) {
44+
return switch (type.toUpperCase()) {
45+
case "WEEKLY" -> rankingService.getWeeklyRankings(date, page, size);
46+
case "MONTHLY" -> rankingService.getMonthlyRankings(date, page, size);
47+
default -> getTopRankings(date, page, size);
48+
};
49+
}
4250
}
Lines changed: 6 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,15 @@
11
package com.loopers.infrastructure.rank;
22

3-
import java.time.LocalDateTime;
4-
import java.time.format.DateTimeFormatter;
3+
import com.loopers.application.rank.RankingInfo;
54
import java.util.List;
6-
import java.util.Set;
7-
import java.util.concurrent.TimeUnit;
8-
import lombok.RequiredArgsConstructor;
9-
import org.springframework.data.redis.connection.zset.Aggregate;
10-
import org.springframework.data.redis.connection.zset.Weights;
11-
import org.springframework.data.redis.core.RedisTemplate;
12-
import org.springframework.stereotype.Component;
135

14-
@Component
15-
@RequiredArgsConstructor
16-
public class RankingService {
6+
public interface RankingService {
177

18-
private final RedisTemplate<String, String> redisTemplate;
8+
List<Long> getTopRankingIds(String date, int page, int size);
199

20-
public List<Long> getTopRankingIds(String date, int page, int size) {
21-
String key = "ranking:all:" + date;
22-
int start = (page - 1) * size;
23-
int end = start + size - 1;
10+
List<RankingInfo> getWeeklyRankings(String date, int page, int size);
2411

25-
Set<String> rankedIds = redisTemplate.opsForZSet().reverseRange(key, start, end);
12+
List<RankingInfo> getMonthlyRankings(String date, int page, int size);
2613

27-
if (rankedIds == null || rankedIds.isEmpty()) {
28-
return List.of();
29-
}
30-
31-
return rankedIds.stream()
32-
.map(Long::valueOf)
33-
.toList();
34-
}
35-
36-
public Integer getProductRank(Long productId) {
37-
String today = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
38-
String key = "ranking:all:" + today;
39-
40-
Long rank = redisTemplate.opsForZSet().reverseRank(key, String.valueOf(productId));
41-
42-
return (rank != null) ? rank.intValue() + 1 : null;
43-
}
14+
Integer getProductRank(Long productId);
4415
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package com.loopers.infrastructure.rank;
2+
3+
import com.loopers.application.rank.RankingInfo;
4+
import com.loopers.domain.rank.monthly.MonthlyRankingMVRepository;
5+
import com.loopers.domain.rank.weekly.WeeklyRankingMVRepository;
6+
import java.time.LocalDateTime;
7+
import java.time.format.DateTimeFormatter;
8+
import java.util.List;
9+
import java.util.Set;
10+
import lombok.RequiredArgsConstructor;
11+
import org.springframework.data.domain.PageRequest;
12+
import org.springframework.data.redis.core.RedisTemplate;
13+
import org.springframework.stereotype.Component;
14+
15+
@RequiredArgsConstructor
16+
@Component
17+
public class RankingServiceImpl implements RankingService {
18+
19+
private final RedisTemplate<String, String> redisTemplate;
20+
private final WeeklyRankingMVRepository weeklyRepository;
21+
private final MonthlyRankingMVRepository monthlyRepository;
22+
23+
@Override
24+
public List<Long> getTopRankingIds(String date, int page, int size) {
25+
String key = "ranking:all:" + date;
26+
int start = (page - 1) * size;
27+
int end = start + size - 1;
28+
29+
Set<String> rankedIds = redisTemplate.opsForZSet().reverseRange(key, start, end);
30+
31+
if (rankedIds == null || rankedIds.isEmpty()) {
32+
return List.of();
33+
}
34+
35+
return rankedIds.stream()
36+
.map(Long::valueOf)
37+
.toList();
38+
}
39+
40+
@Override
41+
public List<RankingInfo> getWeeklyRankings(String date, int page, int size) {
42+
return weeklyRepository.findByBaseDateOrderByCurrentRankAsc(date, PageRequest.of(page - 1, size))
43+
.stream()
44+
.map(RankingInfo::from)
45+
.toList();
46+
}
47+
48+
@Override
49+
public List<RankingInfo> getMonthlyRankings(String date, int page, int size) {
50+
return monthlyRepository.findByBaseDateOrderByCurrentRankAsc(date, PageRequest.of(page - 1, size))
51+
.stream()
52+
.map(RankingInfo::from)
53+
.toList();
54+
}
55+
56+
@Override
57+
public Integer getProductRank(Long productId) {
58+
String today = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
59+
String key = "ranking:all:" + today;
60+
61+
Long rank = redisTemplate.opsForZSet().reverseRank(key, String.valueOf(productId));
62+
63+
return (rank != null) ? rank.intValue() + 1 : null;
64+
}
65+
}

apps/commerce-api/src/main/java/com/loopers/interfaces/api/rank/RankingV1ApiSpec.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public interface RankingV1ApiSpec {
1212

1313
@Operation(summary = "실시간 랭킹 조회", description = "특정 날짜의 인기 상품 랭킹을 조회합니다.")
1414
ApiResponse<List<RankingResponse>> getRankings(
15+
@Parameter(description = "랭킹 타입 (DAILY, WEEKLY, MONTHLY)", example = "WEEKLY") String type,
1516
@Parameter(description = "조회 날짜 (yyyyMMdd)", example = "20251225") String date,
1617
@Parameter(description = "페이지 번호") int page,
1718
@Parameter(description = "페이지 크기") int size

apps/commerce-api/src/main/java/com/loopers/interfaces/api/rank/RankingV1Controller.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,12 @@ public class RankingV1Controller implements RankingV1ApiSpec {
2121
@GetMapping
2222
@Override
2323
public ApiResponse<List<RankingResponse>> getRankings(
24+
@RequestParam(value = "type", defaultValue = "DAILY") String type,
2425
@RequestParam(value = "date") String date,
2526
@RequestParam(value = "page", defaultValue = "1") int page,
2627
@RequestParam(value = "size", defaultValue = "20") int size
2728
) {
28-
List<RankingInfo> infos = rankingFacade.getTopRankings(date, page, size);
29+
List<RankingInfo> infos = rankingFacade.getRankings(type, date, page, size);
2930
List<RankingV1Dto.RankingResponse> response = infos.stream()
3031
.map(RankingV1Dto.RankingResponse::from)
3132
.toList();

0 commit comments

Comments
 (0)