-
Notifications
You must be signed in to change notification settings - Fork 35
[volume-10] Collect, Stack, Zip #244
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: Kimjipang
Are you sure you want to change the base?
Changes from all commits
06c79ae
cf58635
9bec435
72ae951
52e3752
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| package com.loopers.application.ranking; | ||
|
|
||
| import com.loopers.domain.ranking.Period; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.data.redis.core.StringRedisTemplate; | ||
| import org.springframework.data.redis.core.ZSetOperations; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import java.util.Set; | ||
|
|
||
| @Component | ||
| @RequiredArgsConstructor | ||
| public class DailyRankingService { | ||
| private final StringRedisTemplate redisTemplate; | ||
|
|
||
| public RankingV1Dto.ProductRankingPageResponse getDailyRanking(String date, Period period, int page, int size) { | ||
| if (page < 1) page = 1; | ||
| if (size < 1) size = 20; | ||
|
|
||
| String key = "ranking:all:" + date; | ||
| ZSetOperations<String, String> zset = redisTemplate.opsForZSet(); | ||
|
|
||
| Long total = zset.size(key); | ||
| long totalElements = (total == null) ? 0 : total; | ||
|
|
||
| if (totalElements == 0) { | ||
| return new RankingV1Dto.ProductRankingPageResponse(date, Period.DAILY, date, date, page, size, 0, 0, List.of()); | ||
| } | ||
|
|
||
| long start = (long) (page - 1) * size; | ||
| long end = start + size - 1; | ||
|
|
||
| if (start >= totalElements) { | ||
| int totalPages = (int) Math.ceil((double) totalElements / size); | ||
| return new RankingV1Dto.ProductRankingPageResponse(date, Period.DAILY, date, date, page, size, totalElements, totalPages, List.of()); | ||
| } | ||
|
|
||
| Set<ZSetOperations.TypedTuple<String>> tuples = | ||
| zset.reverseRangeWithScores(key, start, end); | ||
|
|
||
| List<RankingV1Dto.ProductRankingResponse> items = new ArrayList<>(); | ||
| if (tuples != null) { | ||
| long rank = start + 1; | ||
| for (var t : tuples) { | ||
| String member = t.getValue(); | ||
| Double score = t.getScore(); | ||
| if (member == null || score == null) continue; | ||
|
|
||
| items.add(new RankingV1Dto.ProductRankingResponse( | ||
| rank++, | ||
| Long.parseLong(member), | ||
| score | ||
| )); | ||
| } | ||
| } | ||
|
|
||
| int totalPages = (int) Math.ceil((double) totalElements / size); | ||
| return new RankingV1Dto.ProductRankingPageResponse(date, Period.DAILY, date, date, page, size, totalElements, totalPages, items); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| package com.loopers.application.ranking; | ||
|
|
||
| import com.loopers.domain.ranking.MonthlyRankingMv; | ||
| import com.loopers.domain.ranking.MonthlyRankingRepository; | ||
| import com.loopers.domain.ranking.Period; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
| import java.time.LocalDate; | ||
| import java.time.format.DateTimeFormatter; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
|
|
||
| @Component | ||
| @RequiredArgsConstructor | ||
| public class MonthlyRankingService { | ||
| private static final DateTimeFormatter BASIC = DateTimeFormatter.BASIC_ISO_DATE; | ||
|
|
||
| private final MonthlyRankingRepository monthlyRankingRepository; | ||
|
|
||
| public RankingV1Dto.ProductRankingPageResponse getMonthlyTop100(String date, Period period, int page, int size) { | ||
| int safePage = Math.max(page, 1); | ||
| int safeSize = Math.max(size, 1); | ||
|
|
||
| LocalDate end = LocalDate.parse(date, BASIC); | ||
| String endDate = end.format(BASIC); | ||
|
|
||
| String startDate = end.minusDays(30).format(BASIC); | ||
|
|
||
| System.out.println("startDate = " + startDate); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ํ๋ก๋์ ๋ก๊น ๋ฐฉ์์ผ๋ก ๊ต์ฒด ํ์
๐ ์ ์ํ๋ ์์ ์ฌํญํด๋์ค ์๋จ์ logger ์ ์ธ์ ์ถ๊ฐ: +import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
@Component
@RequiredArgsConstructor
public class MonthlyRankingService {๊ทธ๋ฆฌ๊ณ System.out.println์ ๋ก๊ฑฐ๋ก ๊ต์ฒด: - System.out.println("startDate = " + startDate);
+ log.debug("Monthly ranking calculation - startDate: {}, endDate: {}", startDate, endDate);
๐ค Prompt for AI Agents |
||
|
|
||
| long totalElements = monthlyRankingRepository.countMonthly(startDate, endDate); | ||
|
|
||
| if (totalElements == 0) { | ||
| return new RankingV1Dto.ProductRankingPageResponse( | ||
| date, | ||
| period, | ||
| startDate, | ||
| endDate, | ||
| safePage, | ||
| safeSize, | ||
| 0, | ||
| 0, | ||
| List.of() | ||
| ); | ||
| } | ||
|
|
||
| int totalPages = (int) Math.ceil((double) totalElements / safeSize); | ||
|
|
||
| long offset = (long) (safePage - 1) * safeSize; | ||
| if (offset >= totalElements) { | ||
| return new RankingV1Dto.ProductRankingPageResponse( | ||
| date, | ||
| period, | ||
| startDate, | ||
| endDate, | ||
| safePage, | ||
| safeSize, | ||
| totalElements, | ||
| totalPages, | ||
| List.of() | ||
| ); | ||
| } | ||
|
|
||
| List<MonthlyRankingMv> rows = monthlyRankingRepository.getMonthlyTop100(endDate, startDate, safePage, safeSize); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ๐งฉ Analysis chain๐ Script executed: #!/bin/bash
# Find repository interfaces and service implementation
echo "=== ํ์ผ ์์น ํ์ธ ==="
find . -type f -name "*RankingRepository*" -o -name "MonthlyRankingService.java" | head -20
echo -e "\n=== MonthlyRankingService ํ์ผ ์ฐพ๊ธฐ ==="
fd MonthlyRankingService.java
echo -e "\n=== ๋ฆฌํฌ์งํ ๋ฆฌ ํ์ผ๋ค ์ฐพ๊ธฐ ==="
fd "RankingRepository" --type fRepository: Loopers-dev-lab/loopers-spring-java-template Length of output: 1111 ๐ Script executed: #!/bin/bash
# MonthlyRankingService์ line 32์ 65 ํ์ธ
echo "=== MonthlyRankingService.java ๋ด์ฉ (๋ผ์ธ 25-75) ==="
cat -n apps/commerce-api/src/main/java/com/loopers/application/ranking/MonthlyRankingService.java | sed -n '25,75p'Repository: Loopers-dev-lab/loopers-spring-java-template Length of output: 2190 ๐ Script executed: #!/bin/bash
# ๋ฆฌํฌ์งํ ๋ฆฌ ์ธํฐํ์ด์ค ๋ฉ์๋ ์๊ทธ๋์ฒ ํ์ธ
echo "=== MonthlyRankingRepository getMonthlyTop100 ๋ฉ์๋ ==="
rg "getMonthlyTop100" -A 2 -B 1
echo -e "\n=== MonthlyRankingRepository countMonthly ๋ฉ์๋ ==="
rg "countMonthly" -A 2 -B 1
echo -e "\n=== WeeklyRankingRepository getWeeklyTop100 ๋ฉ์๋ ==="
rg "getWeeklyTop100" -A 2 -B 1
echo -e "\n=== WeeklyRankingRepository countWeekly ๋ฉ์๋ ==="
rg "countWeekly" -A 2 -B 1Repository: Loopers-dev-lab/loopers-spring-java-template Length of output: 9011 ๋ฆฌํฌ์งํ ๋ฆฌ ๋ฉ์๋ ๊ฐ ํ๋ผ๋ฏธํฐ ์์ ์ผ๊ด์ฑ ํต์ผ ํ์ Line 65์ ๐ค Prompt for AI Agents |
||
|
|
||
| List<RankingV1Dto.ProductRankingResponse> items = new ArrayList<>(rows.size()); | ||
| long rank = offset + 1; | ||
|
|
||
| for (MonthlyRankingMv row : rows) { | ||
| items.add(new RankingV1Dto.ProductRankingResponse( | ||
| rank++, | ||
| row.getProductId(), | ||
| row.getScore().doubleValue() | ||
| )); | ||
| } | ||
|
|
||
| return new RankingV1Dto.ProductRankingPageResponse( | ||
| date, | ||
| period, | ||
| startDate, | ||
| endDate, | ||
| safePage, | ||
| safeSize, | ||
| totalElements, | ||
| totalPages, | ||
| items | ||
| ); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,70 +1,31 @@ | ||
| package com.loopers.application.ranking; | ||
|
|
||
| import com.loopers.interfaces.api.ranking.RankingV1Dto; | ||
| import com.loopers.domain.ranking.Period; | ||
| import com.loopers.support.error.CoreException; | ||
| import com.loopers.support.error.ErrorType; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.data.redis.core.StringRedisTemplate; | ||
| import org.springframework.data.redis.core.ZSetOperations; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
| import java.time.LocalDate; | ||
| import java.time.ZoneId; | ||
| import java.time.format.DateTimeFormatter; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import java.util.Set; | ||
|
|
||
| @Component | ||
| @RequiredArgsConstructor | ||
| public class RankingFacade { | ||
| private final StringRedisTemplate redisTemplate; | ||
| private static final ZoneId KST = ZoneId.of("Asia/Seoul"); | ||
| private static final DateTimeFormatter YYYYMMDD = DateTimeFormatter.BASIC_ISO_DATE; | ||
|
|
||
| public RankingV1Dto.ProductRankingPageResponse getDailyProductRanking(int page, int size) { | ||
| if (page < 1) page = 1; | ||
| if (size < 1) size = 20; | ||
|
|
||
| String date = LocalDate.now(KST).format(YYYYMMDD); | ||
|
|
||
| String key = "ranking:all:" + date; | ||
| ZSetOperations<String, String> zset = redisTemplate.opsForZSet(); | ||
|
|
||
| Long total = zset.size(key); | ||
| long totalElements = (total == null) ? 0 : total; | ||
|
|
||
| if (totalElements == 0) { | ||
| return new RankingV1Dto.ProductRankingPageResponse(date, page, size, 0, 0, List.of()); | ||
| } | ||
|
|
||
| long start = (long) (page - 1) * size; | ||
| long end = start + size - 1; | ||
|
|
||
| if (start >= totalElements) { | ||
| int totalPages = (int) Math.ceil((double) totalElements / size); | ||
| return new RankingV1Dto.ProductRankingPageResponse(date, page, size, totalElements, totalPages, List.of()); | ||
| } | ||
|
|
||
| Set<ZSetOperations.TypedTuple<String>> tuples = | ||
| zset.reverseRangeWithScores(key, start, end); | ||
|
|
||
| List<RankingV1Dto.ProductRankingResponse> items = new ArrayList<>(); | ||
| if (tuples != null) { | ||
| long rank = start + 1; | ||
| for (var t : tuples) { | ||
| String member = t.getValue(); | ||
| Double score = t.getScore(); | ||
| if (member == null || score == null) continue; | ||
|
|
||
| items.add(new RankingV1Dto.ProductRankingResponse( | ||
| rank++, | ||
| Long.parseLong(member), | ||
| score | ||
| )); | ||
| private final DailyRankingService dailyRankingService; | ||
| private final WeeklyRankingService weeklyRankingService; | ||
| private final MonthlyRankingService monthlyRankingService; | ||
|
|
||
| public RankingV1Dto.ProductRankingPageResponse getProductRanking(String date, Period period, int page, int size) { | ||
| switch (period) { | ||
| case DAILY -> { | ||
| return dailyRankingService.getDailyRanking(date, period, page, size); | ||
| } | ||
| case WEEKLY -> { | ||
| return weeklyRankingService.getWeeklyTop100(date, period, page, size); | ||
| } | ||
| case MONTHLY -> { | ||
| return monthlyRankingService.getMonthlyTop100(date, period, page, size); | ||
| } | ||
| default -> throw new CoreException(ErrorType.BAD_REQUEST, "์ง์ํ์ง ์๋ ๊ธฐ๊ฐ์ ๋๋ค."); | ||
| } | ||
|
|
||
| int totalPages = (int) Math.ceil((double) totalElements / size); | ||
| return new RankingV1Dto.ProductRankingPageResponse(date, page, size, totalElements, totalPages, items); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,20 +1,25 @@ | ||
| package com.loopers.interfaces.api.ranking; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public class RankingV1Dto { | ||
| public record ProductRankingResponse( | ||
| Long rank, | ||
| Long productId, | ||
| double score | ||
| ) {} | ||
|
|
||
| public record ProductRankingPageResponse( | ||
| String date, | ||
| int page, | ||
| int size, | ||
| long totalElements, | ||
| int totalPages, | ||
| List<ProductRankingResponse> items | ||
| ) {} | ||
| } | ||
| package com.loopers.application.ranking; | ||
|
|
||
| import com.loopers.domain.ranking.Period; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public class RankingV1Dto { | ||
| public record ProductRankingResponse( | ||
| Long rank, | ||
| Long productId, | ||
| double score | ||
| ) {} | ||
|
|
||
| public record ProductRankingPageResponse( | ||
| String date, | ||
| Period period, | ||
| String startDate, | ||
| String endDate, | ||
| int page, | ||
| int size, | ||
| long totalElements, | ||
| int totalPages, | ||
| List<ProductRankingResponse> items | ||
| ) {} | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| package com.loopers.application.ranking; | ||
|
|
||
| import com.loopers.domain.ranking.Period; | ||
| import com.loopers.domain.ranking.WeeklyRankingMv; | ||
| import com.loopers.domain.ranking.WeeklyRankingRepository; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
| import java.time.LocalDate; | ||
| import java.time.format.DateTimeFormatter; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
|
|
||
| @Component | ||
| @RequiredArgsConstructor | ||
| public class WeeklyRankingService { | ||
| private static final DateTimeFormatter BASIC = DateTimeFormatter.BASIC_ISO_DATE; | ||
|
|
||
| private final WeeklyRankingRepository weeklyRankingRepository; | ||
|
|
||
| public RankingV1Dto.ProductRankingPageResponse getWeeklyTop100(String date, Period period, int page, int size) { | ||
| int safePage = Math.max(page, 1); | ||
| int safeSize = Math.max(size, 1); | ||
|
|
||
| LocalDate end = LocalDate.parse(date, BASIC); | ||
| String endDate = end.format(BASIC); | ||
|
|
||
| String startDate = end.minusDays(6).format(BASIC); | ||
|
|
||
| System.out.println("startDate = " + startDate); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ํ๋ก๋์ ๋ก๊น ๋ฐฉ์์ผ๋ก ๊ต์ฒด ํ์ MonthlyRankingService์ ๋์ผํ ์ด์: ๐ ์ ์ํ๋ ์์ ์ฌํญ+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
@Component
@RequiredArgsConstructor
public class WeeklyRankingService {- System.out.println("startDate = " + startDate);
+ log.debug("Weekly ranking calculation - startDate: {}, endDate: {}", startDate, endDate);
๐ค Prompt for AI Agents |
||
|
|
||
| long totalElements = weeklyRankingRepository.countWeekly(startDate, endDate); | ||
|
|
||
| if (totalElements == 0) { | ||
| return new RankingV1Dto.ProductRankingPageResponse( | ||
| date, | ||
| period, | ||
| startDate, | ||
| endDate, | ||
| safePage, | ||
| safeSize, | ||
| 0, | ||
| 0, | ||
| List.of() | ||
| ); | ||
| } | ||
|
|
||
| int totalPages = (int) Math.ceil((double) totalElements / safeSize); | ||
|
|
||
| long offset = (long) (safePage - 1) * safeSize; | ||
| if (offset >= totalElements) { | ||
| return new RankingV1Dto.ProductRankingPageResponse( | ||
| date, | ||
| period, | ||
| startDate, | ||
| endDate, | ||
| safePage, | ||
| safeSize, | ||
| totalElements, | ||
| totalPages, | ||
| List.of() | ||
| ); | ||
| } | ||
|
|
||
| List<WeeklyRankingMv> rows = weeklyRankingRepository.getWeeklyTop100(endDate, startDate, safePage, safeSize); | ||
|
|
||
| List<RankingV1Dto.ProductRankingResponse> items = new ArrayList<>(rows.size()); | ||
| long rank = offset + 1; | ||
|
|
||
| for (WeeklyRankingMv row : rows) { | ||
| items.add(new RankingV1Dto.ProductRankingResponse( | ||
| rank++, | ||
| row.getProductId(), | ||
| row.getScore().doubleValue() | ||
| )); | ||
| } | ||
|
|
||
| return new RankingV1Dto.ProductRankingPageResponse( | ||
| date, | ||
| period, | ||
| startDate, | ||
| endDate, | ||
| safePage, | ||
| safeSize, | ||
| totalElements, | ||
| totalPages, | ||
| items | ||
| ); | ||
| } | ||
|
|
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Long.parseLong(member)์์ ์์ธ ๋ฐ์ ๊ฐ๋ฅ์ฑRedis์์ ๊ฐ์ ธ์จ
member๊ฐ์ด ์ ํจํ ์ซ์๊ฐ ์๋ ๊ฒฝ์ฐNumberFormatException์ด ๋ฐ์ํ ์ ์์ต๋๋ค. ๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ์ด ๋ณด์ฅ๋๋ค๋ฉด ๋ฌธ์ ๊ฐ ์์ง๋ง, ๋ฐฉ์ด์ ์ฝ๋ฉ์ ์ํด ์์ธ ์ฒ๋ฆฌ๋ฅผ ๊ณ ๋ คํด ๋ณด์ธ์.๐ ๋ฐฉ์ด์ ์์ธ ์ฒ๋ฆฌ ์ ์
for (var t : tuples) { String member = t.getValue(); Double score = t.getScore(); if (member == null || score == null) continue; + Long productId; + try { + productId = Long.parseLong(member); + } catch (NumberFormatException e) { + continue; // ๋๋ ๋ก๊น ํ ๊ฑด๋๋ฐ๊ธฐ + } + items.add(new RankingV1Dto.ProductRankingResponse( rank++, - Long.parseLong(member), + productId, score )); }๐ค Prompt for AI Agents