Skip to content

Commit a75c7ef

Browse files
committed
feat: 인기상품 조회 API 구현
1 parent cc0c139 commit a75c7ef

4 files changed

Lines changed: 126 additions & 0 deletions

File tree

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package com.loopers.application;
2+
3+
import com.loopers.interfaces.api.ranking.RankingV1Dto;
4+
import lombok.RequiredArgsConstructor;
5+
import org.springframework.data.redis.core.StringRedisTemplate;
6+
import org.springframework.data.redis.core.ZSetOperations;
7+
import org.springframework.stereotype.Component;
8+
9+
import java.time.LocalDate;
10+
import java.time.ZoneId;
11+
import java.time.format.DateTimeFormatter;
12+
import java.util.ArrayList;
13+
import java.util.List;
14+
import java.util.Set;
15+
16+
@Component
17+
@RequiredArgsConstructor
18+
public class RankingFacade {
19+
private final StringRedisTemplate redisTemplate;
20+
private static final ZoneId KST = ZoneId.of("Asia/Seoul");
21+
private static final DateTimeFormatter YYYYMMDD = DateTimeFormatter.BASIC_ISO_DATE;
22+
23+
public RankingV1Dto.ProductRankingPageResponse getDailyProductRanking(int page, int size) {
24+
if (page < 1) page = 1;
25+
if (size < 1) size = 20;
26+
27+
String date = LocalDate.now(KST).format(YYYYMMDD);
28+
29+
String key = "ranking:all:" + date;
30+
ZSetOperations<String, String> zset = redisTemplate.opsForZSet();
31+
32+
Long total = zset.size(key);
33+
long totalElements = (total == null) ? 0 : total;
34+
35+
if (totalElements == 0) {
36+
return new RankingV1Dto.ProductRankingPageResponse(date, page, size, 0, 0, List.of());
37+
}
38+
39+
long start = (long) (page - 1) * size;
40+
long end = start + size - 1;
41+
42+
if (start >= totalElements) {
43+
int totalPages = (int) Math.ceil((double) totalElements / size);
44+
return new RankingV1Dto.ProductRankingPageResponse(date, page, size, totalElements, totalPages, List.of());
45+
}
46+
47+
Set<ZSetOperations.TypedTuple<String>> tuples =
48+
zset.reverseRangeWithScores(key, start, end);
49+
50+
List<RankingV1Dto.ProductRankingResponse> items = new ArrayList<>();
51+
if (tuples != null) {
52+
long rank = start + 1;
53+
for (var t : tuples) {
54+
String member = t.getValue();
55+
Double score = t.getScore();
56+
if (member == null || score == null) continue;
57+
58+
items.add(new RankingV1Dto.ProductRankingResponse(
59+
rank++,
60+
Long.parseLong(member),
61+
score
62+
));
63+
}
64+
}
65+
66+
int totalPages = (int) Math.ceil((double) totalElements / size);
67+
return new RankingV1Dto.ProductRankingPageResponse(date, page, size, totalElements, totalPages, items);
68+
}
69+
70+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.loopers.interfaces.api.ranking;
2+
3+
import com.loopers.interfaces.api.ApiResponse;
4+
5+
public interface RankingV1ApiSpec {
6+
7+
ApiResponse<RankingV1Dto.ProductRankingPageResponse> getDailyProductRanking(int size, int page);
8+
9+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.loopers.interfaces.api.ranking;
2+
3+
import com.loopers.application.RankingFacade;
4+
import com.loopers.interfaces.api.ApiResponse;
5+
import lombok.RequiredArgsConstructor;
6+
import org.springframework.web.bind.annotation.GetMapping;
7+
import org.springframework.web.bind.annotation.RequestMapping;
8+
import org.springframework.web.bind.annotation.RequestParam;
9+
import org.springframework.web.bind.annotation.RestController;
10+
11+
@RestController
12+
@RequestMapping("/api/v1/rankings")
13+
@RequiredArgsConstructor
14+
public class RankingV1Controller implements RankingV1ApiSpec {
15+
private final RankingFacade rankingFacade;
16+
17+
@GetMapping
18+
@Override
19+
public ApiResponse<RankingV1Dto.ProductRankingPageResponse> getDailyProductRanking(
20+
@RequestParam int size,
21+
@RequestParam int page
22+
) {
23+
RankingV1Dto.ProductRankingPageResponse response = rankingFacade.getDailyProductRanking(page, size);
24+
25+
return ApiResponse.success(response);
26+
}
27+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.loopers.interfaces.api.ranking;
2+
3+
import java.util.List;
4+
5+
public class RankingV1Dto {
6+
public record ProductRankingResponse(
7+
Long rank,
8+
Long productId,
9+
double score
10+
) {}
11+
12+
public record ProductRankingPageResponse(
13+
String date,
14+
int page,
15+
int size,
16+
long totalElements,
17+
int totalPages,
18+
List<ProductRankingResponse> items
19+
) {}
20+
}

0 commit comments

Comments
 (0)