-
Notifications
You must be signed in to change notification settings - Fork 35
[volume-10] Collect, Stack, Zip #235
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: junoade
Are you sure you want to change the base?
Changes from all commits
a9339ff
3f9eb4f
569eb94
8206004
0a98c34
8472ca2
500f4b5
0979b35
35541d6
a99b224
1abf6d0
ff0d7d3
e3d4b05
ca9b019
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,7 @@ | ||
| package com.loopers.application.ranking; | ||
|
|
||
| public enum RankingPeriod { | ||
| DAILY, | ||
| WEEKLY, | ||
| MONTHLY | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package com.loopers.application.ranking; | ||
|
|
||
| import java.time.LocalDate; | ||
|
|
||
| public record RankingQuery( | ||
| RankingPeriod period, | ||
| String key, | ||
| LocalDate date, | ||
| int limit | ||
| ) { } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| package com.loopers.application.ranking.strategy; | ||
|
|
||
| import com.loopers.application.ranking.RankingPeriod; | ||
| import com.loopers.ranking.RankingEntry; | ||
| import com.loopers.ranking.RankingZSetRepository; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
| import java.time.LocalDate; | ||
| import java.time.ZoneId; | ||
| import java.time.format.DateTimeFormatter; | ||
| import java.util.List; | ||
|
|
||
| @Component | ||
| @RequiredArgsConstructor | ||
| public class DailyRankingFetcher implements RankingFetchStrategy { | ||
| private final RankingZSetRepository rankingZSetRepository; | ||
|
|
||
| @Override | ||
| public RankingPeriod getRankingPeriod() { | ||
| return RankingPeriod.DAILY; | ||
| } | ||
|
|
||
| @Override | ||
| public List<RankingEntry> fetchRankingEntries(String key, int limit) { | ||
| LocalDate target = initLocalDate(key); | ||
| return rankingZSetRepository.findTopDailyAllByLimit(target, limit); | ||
| } | ||
|
|
||
| private LocalDate initLocalDate(String date) { | ||
| return (hasValidDate(date)) | ||
| ? LocalDate.now(ZoneId.systemDefault()) | ||
| : LocalDate.parse(date, DateTimeFormatter.BASIC_ISO_DATE); | ||
| } | ||
|
|
||
| private boolean hasValidDate(String date) { | ||
| return date == null || date.isBlank(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| package com.loopers.application.ranking.strategy; | ||
|
|
||
| import com.loopers.application.ranking.RankingPeriod; | ||
| import com.loopers.infrastructure.ranking.ProductRankMonthlyRepository; | ||
| import com.loopers.ranking.RankingEntry; | ||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.data.domain.PageRequest; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| @Slf4j | ||
| @Component | ||
| @RequiredArgsConstructor | ||
| public class MonthlyRankingFetcher implements RankingFetchStrategy { | ||
| private final ProductRankMonthlyRepository monthlyRankingRepository; | ||
|
|
||
| @Override | ||
| public RankingPeriod getRankingPeriod() { | ||
| return RankingPeriod.MONTHLY; | ||
| } | ||
|
|
||
| @Override | ||
| public List<RankingEntry> fetchRankingEntries(String key, int limit) { | ||
| log.debug("Fetching ranking entries for key {}", key); | ||
| return monthlyRankingRepository.findTopByYearMonth(key, PageRequest.of(0, limit)); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| package com.loopers.application.ranking.strategy; | ||
|
|
||
| import com.loopers.application.ranking.RankingPeriod; | ||
| import com.loopers.ranking.RankingEntry; | ||
|
|
||
| import java.util.List; | ||
|
|
||
|
|
||
| public interface RankingFetchStrategy { | ||
| RankingPeriod getRankingPeriod(); | ||
| List<RankingEntry> fetchRankingEntries(String key, int limit); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| package com.loopers.application.ranking.strategy; | ||
|
|
||
| import com.loopers.application.ranking.RankingPeriod; | ||
| import com.loopers.application.ranking.RankingQuery; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
| import java.time.LocalDate; | ||
| import java.time.ZoneId; | ||
| import java.time.format.DateTimeFormatter; | ||
| import java.time.temporal.WeekFields; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.function.Function; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| @Component | ||
| public class RankingFetchStrategyResolver { | ||
| private static final DateTimeFormatter YYYYMMDD = DateTimeFormatter.BASIC_ISO_DATE; | ||
| private static final DateTimeFormatter YYYYMM = DateTimeFormatter.ofPattern("yyyyMM"); | ||
| private final Map<RankingPeriod, RankingFetchStrategy> policies; | ||
|
|
||
| public RankingFetchStrategyResolver(List<RankingFetchStrategy> policies) { | ||
| this.policies = policies.stream() | ||
| .collect(Collectors.toMap(RankingFetchStrategy::getRankingPeriod, Function.identity())); | ||
| } | ||
|
|
||
| /** | ||
| * ๋ญํน ์กฐํ ๋ฐฉ๋ฒ์ ์ ํํฉ๋๋ค. | ||
| * @param period | ||
| * @param date | ||
| * @param size | ||
| * @return | ||
| */ | ||
| public Resolved resolve(RankingPeriod period, String date, int size) { | ||
| LocalDate target = initLocalDate(date); | ||
| int limit = normalizeSize(size); | ||
|
|
||
| String key = switch (period) { | ||
| case DAILY -> target.format(YYYYMMDD); | ||
| case WEEKLY -> yearWeekKey(target); // 2026-W01 | ||
| case MONTHLY -> target.format(YYYYMM); // 202601 | ||
| }; | ||
|
|
||
| RankingFetchStrategy policy = policies.get(period); | ||
| if (policy == null) throw new IllegalArgumentException("Unsupported period: " + period); | ||
|
|
||
| return new Resolved(new RankingQuery(period, key, target, limit), policy); | ||
| } | ||
|
|
||
| public record Resolved(RankingQuery rankingQuery, RankingFetchStrategy policy) {} | ||
|
|
||
| /** | ||
| * ์ต๋ ์ํ์ TOP-100 ์ผ๋ก ์ค์ | ||
| * @param size | ||
| * @return | ||
| */ | ||
| private int normalizeSize(int size) { | ||
| if (size <= 0) return 20; | ||
| return Math.min(size, 100); | ||
| } | ||
|
|
||
| private LocalDate initLocalDate(String date) { | ||
| return (hasValidDate(date)) | ||
| ? LocalDate.now(ZoneId.systemDefault()) | ||
| : LocalDate.parse(date, DateTimeFormatter.BASIC_ISO_DATE); | ||
| } | ||
|
|
||
| private String yearWeekKey(LocalDate date) { | ||
| WeekFields wf = WeekFields.ISO; | ||
| int y = date.get(wf.weekBasedYear()); | ||
| int w = date.get(wf.weekOfWeekBasedYear()); | ||
| return "%d-W%02d".formatted(y, w); | ||
| } | ||
|
|
||
| private boolean hasValidDate(String date) { | ||
| return date == null || date.isBlank(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| package com.loopers.application.ranking.strategy; | ||
|
|
||
| import com.loopers.application.ranking.RankingPeriod; | ||
| import com.loopers.infrastructure.ranking.ProductRankWeeklyRepository; | ||
| import com.loopers.ranking.RankingEntry; | ||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.data.domain.PageRequest; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| @Slf4j | ||
| @Component | ||
| @RequiredArgsConstructor | ||
| public class WeeklyRankingFetcher implements RankingFetchStrategy { | ||
| private final ProductRankWeeklyRepository weeklyRankingRepository; | ||
|
|
||
| @Override | ||
| public RankingPeriod getRankingPeriod() { | ||
| return RankingPeriod.WEEKLY; | ||
| } | ||
|
|
||
| @Override | ||
| public List<RankingEntry> fetchRankingEntries(String key, int limit) { | ||
| log.debug("Fetching ranking entries for key {}", key); | ||
| return weeklyRankingRepository.findTopByYearWeek(key, PageRequest.of(0, limit)); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,65 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||
| package com.loopers.domain.mv; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| import jakarta.persistence.Column; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import jakarta.persistence.EmbeddedId; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import jakarta.persistence.Entity; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import jakarta.persistence.Table; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import lombok.Getter; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import lombok.NoArgsConstructor; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| import java.time.LocalDateTime; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| @Entity | ||||||||||||||||||||||||||||||||||||||||||||||||
| @Table(name = "mv_product_rank_monthly") | ||||||||||||||||||||||||||||||||||||||||||||||||
| @Getter | ||||||||||||||||||||||||||||||||||||||||||||||||
| @NoArgsConstructor | ||||||||||||||||||||||||||||||||||||||||||||||||
| public class ProductRankMonthly { | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| @EmbeddedId | ||||||||||||||||||||||||||||||||||||||||||||||||
| private ProductRankMonthlyId id; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| @Column(name = "view_count", nullable = false) | ||||||||||||||||||||||||||||||||||||||||||||||||
| private long viewCount; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| @Column(name = "like_count", nullable = false) | ||||||||||||||||||||||||||||||||||||||||||||||||
| private long likeCount; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| @Column(name = "order_count", nullable = false) | ||||||||||||||||||||||||||||||||||||||||||||||||
| private long orderCount; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| @Column(name = "score", nullable = false) | ||||||||||||||||||||||||||||||||||||||||||||||||
| private double score; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| @Column(name = "created_at", nullable = false) | ||||||||||||||||||||||||||||||||||||||||||||||||
| private LocalDateTime createdAt; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| @Column(name = "updated_at", nullable = false) | ||||||||||||||||||||||||||||||||||||||||||||||||
| private LocalDateTime updatedAt; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| public static ProductRankMonthly of(String yearMonth, Long productId, | ||||||||||||||||||||||||||||||||||||||||||||||||
| long viewCount, long likeCount, long orderCount) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| ProductRankMonthly e = new ProductRankMonthly(); | ||||||||||||||||||||||||||||||||||||||||||||||||
| e.id = new ProductRankMonthlyId(yearMonth, productId); | ||||||||||||||||||||||||||||||||||||||||||||||||
| e.viewCount = viewCount; | ||||||||||||||||||||||||||||||||||||||||||||||||
| e.likeCount = likeCount; | ||||||||||||||||||||||||||||||||||||||||||||||||
| e.orderCount = orderCount; | ||||||||||||||||||||||||||||||||||||||||||||||||
| e.createdAt = LocalDateTime.now(); | ||||||||||||||||||||||||||||||||||||||||||||||||
| e.updatedAt = LocalDateTime.now(); | ||||||||||||||||||||||||||||||||||||||||||||||||
| return e; | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+39
to
+49
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. ์น๋ช ์ : score ํ๋๊ฐ ์ด๊ธฐํ๋์ง ์์ต๋๋ค.
๋ฐฐ์น ์์
์ ๐ ์ ์ํ๋ ์์ ์ฌํญ public static ProductRankMonthly of(String yearMonth, Long productId,
- long viewCount, long likeCount, long orderCount) {
+ long viewCount, long likeCount, long orderCount, double score) {
ProductRankMonthly e = new ProductRankMonthly();
e.id = new ProductRankMonthlyId(yearMonth, productId);
e.viewCount = viewCount;
e.likeCount = likeCount;
e.orderCount = orderCount;
+ e.score = score;
e.createdAt = LocalDateTime.now();
e.updatedAt = LocalDateTime.now();
return e;
}๋์ผํ ๋ฌธ์ ๊ฐ ๐ Committable suggestion
Suggested change
๐ค Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| public void overwrite(long viewCount, long likeCount, long orderCount) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| this.viewCount = viewCount; | ||||||||||||||||||||||||||||||||||||||||||||||||
| this.likeCount = likeCount; | ||||||||||||||||||||||||||||||||||||||||||||||||
| this.orderCount = orderCount; | ||||||||||||||||||||||||||||||||||||||||||||||||
| this.updatedAt = LocalDateTime.now(); | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+51
to
+56
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. ์น๋ช ์ : score ํ๋๊ฐ ์ ๋ฐ์ดํธ๋์ง ์์ต๋๋ค.
๐ ์ ์ํ๋ ์์ ์ฌํญ-public void overwrite(long viewCount, long likeCount, long orderCount) {
+public void overwrite(long viewCount, long likeCount, long orderCount, double score) {
this.viewCount = viewCount;
this.likeCount = likeCount;
this.orderCount = orderCount;
+ this.score = score;
this.updatedAt = LocalDateTime.now();
}๋์ผํ ๋ฌธ์ ๊ฐ ๐ Committable suggestion
Suggested change
๐ค Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| public Long productId() { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return id.getProductId(); | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| public String yearMonth() { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return id.getYearMonth(); | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| package com.loopers.domain.mv; | ||
|
|
||
| import jakarta.persistence.Column; | ||
| import jakarta.persistence.Embeddable; | ||
| import lombok.*; | ||
|
|
||
| import java.io.Serializable; | ||
|
|
||
| @Embeddable | ||
| @Getter | ||
| @NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
| @AllArgsConstructor | ||
| @EqualsAndHashCode | ||
| public class ProductRankMonthlyId implements Serializable { | ||
|
|
||
| @Column(name = "year_month_key", length = 8, nullable = false) | ||
| private String yearMonth; // e.g. 202601 | ||
|
|
||
| @Column(name = "product_id", nullable = false) | ||
| private Long productId; | ||
| } |
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.
๋ฉ์๋ ์ด๋ฆ์ด ์ค์ ๋์๊ณผ ๋ฐ๋์ ๋๋ค.
hasValidDate()๋ฉ์๋๋ date๊ฐ null์ด๊ฑฐ๋ blank์ผ ๋ true๋ฅผ ๋ฐํํฉ๋๋ค. ์ด๋ "์ ํจํ ๋ ์ง๊ฐ ์๋์ง"๋ฅผ ํ์ธํ๋ ๊ฒ์ด ์๋๋ผ "๋ ์ง๊ฐ ์๋์ง"๋ฅผ ํ์ธํ๋ ๊ฒ์ด๋ฏ๋ก, ๋ฉ์๋ ์ด๋ฆ์ด ๋ก์ง๊ณผ ์ผ์นํ์ง ์์ ํผ๋์ ์ผ๊ธฐํฉ๋๋ค.๋ํ ๋ ์ง ํ์ฑ ์
DateTimeParseException์ ๋ํ ์์ธ ์ฒ๋ฆฌ๊ฐ ์์ด, ์๋ชป๋ ํ์์ ๋ ์ง๊ฐ ์ ๋ฌ๋ ๊ฒฝ์ฐ ์ ์ ํ ์๋ฌ ์๋ต ์์ด ์์ธ๊ฐ ๋ฐ์ํฉ๋๋ค.๐ ์ ์ํ๋ ๊ฐ์ ์ฌํญ
private LocalDate initLocalDate(String date) { - return (hasValidDate(date)) + return (isDateMissing(date)) ? LocalDate.now(ZoneId.systemDefault()) - : LocalDate.parse(date, DateTimeFormatter.BASIC_ISO_DATE); + : parseDate(date); } -private boolean hasValidDate(String date) { +private boolean isDateMissing(String date) { return date == null || date.isBlank(); } +private LocalDate parseDate(String date) { + try { + return LocalDate.parse(date, DateTimeFormatter.BASIC_ISO_DATE); + } catch (DateTimeParseException e) { + throw new IllegalArgumentException( + "Invalid date format. Expected format: yyyyMMdd, but got: " + date, e); + } +}๐ Committable suggestion
๐ค Prompt for AI Agents