-
Notifications
You must be signed in to change notification settings - Fork 35
[volume-10] Collect, Stack, Zip #243
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: adminhelper
Are you sure you want to change the base?
Changes from all commits
d9ae7ad
c5754ff
deda1e2
617746d
e882552
d5a2599
febeab0
072f563
d824c55
1855942
3028233
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,13 @@ | ||||||||||
| name: PR Agent | ||||||||||
| on: | ||||||||||
| pull_request: | ||||||||||
| types: [opened, synchronize] | ||||||||||
| jobs: | ||||||||||
| pr_agent_job: | ||||||||||
| runs-on: ubuntu-latest | ||||||||||
| steps: | ||||||||||
| - name: PR Agent action step | ||||||||||
| uses: Codium-ai/pr-agent@main | ||||||||||
| env: | ||||||||||
| OPENAI_KEY: ${{ secrets.OPENAI_KEY }} | ||||||||||
| GITHUB_TOKEN: ${{ secrets.G_TOKEN }} | ||||||||||
|
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: # ์ํฌํ๋ก์ฐ ํ์ผ ์ ์ฒด ๋ด์ฉ ํ์ธ
cat -n .github/workflows/main.ymlRepository: Loopers-dev-lab/loopers-spring-java-template Length of output: 487 ๐ Script executed: # ๋ฆฌํฌ์งํ ๋ฆฌ์์ G_TOKEN ์ฐธ์กฐ ๊ฒ์
rg "G_TOKEN" --type yaml --type mdRepository: Loopers-dev-lab/loopers-spring-java-template Length of output: 70 ๐ Script executed: # ์ํฌํ๋ก์ฐ ํ์ผ์์ GITHUB_TOKEN ์ฌ์ฉ ํจํด ํ์ธ
rg "GITHUB_TOKEN|secrets\." .github/workflows/ -A 2 -B 2Repository: Loopers-dev-lab/loopers-spring-java-template Length of output: 341 ๐ Web query:
๐ก Result: Summary โ GITHUB_TOKEN for CodiumโAI / pr-agent (GitHub Action)
Sources
๐ Script executed: # ์ํฌํ๋ก์ฐ ํ์ผ์ ์ ์ฒด ๊ตฌ์กฐ ํ์ธ (์จ๊ฒจ์ง ๋ถ๋ถ์ด ์๋์ง ํ์ธ)
wc -l .github/workflows/main.ymlRepository: Loopers-dev-lab/loopers-spring-java-template Length of output: 117
Codium-ai/pr-agent๋ ์๋์ผ๋ก ์ ๊ณต๋๋ ๊ถ์ฅ ์์ env:
OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
- GITHUB_TOKEN: ${{ secrets.G_TOKEN }}
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}๐ Committable suggestion
Suggested change
๐ค Prompt for AI Agents |
||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package com.loopers.application.ranking; | ||
|
|
||
| public record ProductRankSnapshot( | ||
| long rank, | ||
| Long productId, | ||
| double score | ||
| ) { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| package com.loopers.application.ranking; | ||
|
|
||
| import com.loopers.domain.ranking.MvProductRankMonthly; | ||
| import com.loopers.domain.ranking.MvProductRankWeekly; | ||
| import com.loopers.infrastructure.ranking.MvProductRankMonthlyJpaRepository; | ||
| import com.loopers.infrastructure.ranking.MvProductRankWeeklyJpaRepository; | ||
| import java.util.List; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.stereotype.Service; | ||
|
|
||
| @Service | ||
| @RequiredArgsConstructor | ||
| public class RankingMaterializedViewService { | ||
|
|
||
| private final MvProductRankWeeklyJpaRepository weeklyRepository; | ||
| private final MvProductRankMonthlyJpaRepository monthlyRepository; | ||
|
|
||
| public List<ProductRankSnapshot> getRankings(RankingPeriod period, String periodKey, int page, int size) { | ||
| List<ProductRankSnapshot> snapshots = fetch(period, periodKey); | ||
| int safeSize = Math.max(size, 1); | ||
| long skip = (long) Math.max(page, 0) * safeSize; | ||
| return snapshots.stream() | ||
| .skip(skip) | ||
| .limit(safeSize) | ||
| .toList(); | ||
| } | ||
|
Comment on lines
+18
to
+26
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. ๋ฉ๋ชจ๋ฆฌ ๋ด ํ์ด์ง๋ค์ด์ ์ ์ฑ๋ฅ ๋ฌธ์ ๋ฅผ ์ ๋ฐํฉ๋๋ค. fetch() ๋ฉ์๋๊ฐ ํด๋น ๊ธฐ๊ฐ์ ๋ชจ๋ ๋ ์ฝ๋๋ฅผ ๊ฐ์ ธ์จ ํ stream().skip().limit()์ผ๋ก ๋ฉ๋ชจ๋ฆฌ์์ ํ์ด์ง๋ค์ด์ ์ ์ํํฉ๋๋ค. ๋ญํน ๋ฐ์ดํฐ๊ฐ ์์ฒ ๊ฐ ์ด์์ผ ๊ฒฝ์ฐ ๋ถํ์ํ ๋ฐ์ดํฐ๋ฅผ ๋ชจ๋ ๋ก๋ํ์ฌ ๋ฉ๋ชจ๋ฆฌ์ DB ๋ถํ๊ฐ ์ฆ๊ฐํฉ๋๋ค. ๋ฆฌํฌ์งํ ๋ฆฌ ๋ ๋ฒจ์์ Pageable์ ์ฌ์ฉํ DB ํ์ด์ง๋ค์ด์ ์ผ๋ก ๋ณ๊ฒฝํ์ธ์. ๐ ์ ์: ๋ฆฌํฌ์งํ ๋ฆฌ์์ ํ์ด์ง๋ค์ด์ ์ฒ๋ฆฌ๋ฆฌํฌ์งํ ๋ฆฌ ๋ฉ์๋๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ์์ : // MvProductRankWeeklyJpaRepository
Page<MvProductRankWeekly> findByIdPeriodKey(String periodKey, Pageable pageable);๊ทธ๋ฆฌ๊ณ ์๋น์ค ๋ฉ์๋๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ์์ : public List<ProductRankSnapshot> getRankings(RankingPeriod period, String periodKey, int page, int size) {
- List<ProductRankSnapshot> snapshots = fetch(period, periodKey);
int safeSize = Math.max(size, 1);
- long skip = (long) Math.max(page, 0) * safeSize;
- return snapshots.stream()
- .skip(skip)
- .limit(safeSize)
- .toList();
+ int safePage = Math.max(page, 0);
+ Pageable pageable = PageRequest.of(safePage, safeSize, Sort.by("rank").ascending());
+ return fetchPaged(period, periodKey, pageable);
}
๐ค Prompt for AI Agents |
||
|
|
||
| public long count(RankingPeriod period, String periodKey) { | ||
| return fetch(period, periodKey).size(); | ||
| } | ||
|
Comment on lines
+28
to
+30
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. count ๊ตฌํ์ด ๋นํจ์จ์ ์ ๋๋ค. ๋ชจ๋ ๋ ์ฝ๋๋ฅผ fetch()๋ก ๊ฐ์ ธ์จ ํ size()๋ฅผ ํธ์ถํ๋ ๊ฒ์ ๋งค์ฐ ๋นํจ์จ์ ์ ๋๋ค. ๋ฆฌํฌ์งํ ๋ฆฌ์ COUNT ์ฟผ๋ฆฌ๋ฅผ ์ถ๊ฐํ์ฌ DB ๋ ๋ฒจ์์ ์ง๊ณํ์ธ์. ๐ ์ ์: COUNT ์ฟผ๋ฆฌ ์ถ๊ฐ๋ฆฌํฌ์งํ ๋ฆฌ์ ๋ค์ ๋ฉ์๋ ์ถ๊ฐ: // MvProductRankWeeklyJpaRepository
long countByIdPeriodKey(String periodKey);๊ทธ๋ฆฌ๊ณ ์๋น์ค ๋ฉ์๋๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ์์ : public long count(RankingPeriod period, String periodKey) {
- return fetch(period, periodKey).size();
+ if (period == RankingPeriod.WEEKLY) {
+ return weeklyRepository.countByIdPeriodKey(periodKey);
+ }
+ if (period == RankingPeriod.MONTHLY) {
+ return monthlyRepository.countByIdPeriodKey(periodKey);
+ }
+ throw new IllegalArgumentException("Unsupported period for MV: " + period);
}๐ค Prompt for AI Agents |
||
|
|
||
| private List<ProductRankSnapshot> fetch(RankingPeriod period, String periodKey) { | ||
| if (period == RankingPeriod.WEEKLY) { | ||
| return weeklyRepository.findByIdPeriodKeyOrderByRankAsc(periodKey).stream() | ||
| .map(this::fromWeekly) | ||
| .toList(); | ||
| } | ||
| if (period == RankingPeriod.MONTHLY) { | ||
| return monthlyRepository.findByIdPeriodKeyOrderByRankAsc(periodKey).stream() | ||
| .map(this::fromMonthly) | ||
| .toList(); | ||
| } | ||
| throw new IllegalArgumentException("Unsupported period for MV: " + period); | ||
| } | ||
|
|
||
| private ProductRankSnapshot fromWeekly(MvProductRankWeekly entity) { | ||
| return new ProductRankSnapshot(entity.getRank(), entity.getProductId(), entity.getScore()); | ||
| } | ||
|
|
||
| private ProductRankSnapshot fromMonthly(MvProductRankMonthly entity) { | ||
| return new ProductRankSnapshot(entity.getRank(), entity.getProductId(), entity.getScore()); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,93 @@ | ||||||||||||||||||||||||||
| package com.loopers.application.ranking; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| import java.time.DayOfWeek; | ||||||||||||||||||||||||||
| import java.time.LocalDate; | ||||||||||||||||||||||||||
| import java.time.ZoneId; | ||||||||||||||||||||||||||
| import java.time.format.DateTimeFormatter; | ||||||||||||||||||||||||||
| import java.time.temporal.TemporalAdjusters; | ||||||||||||||||||||||||||
| import java.time.temporal.WeekFields; | ||||||||||||||||||||||||||
| import java.util.Arrays; | ||||||||||||||||||||||||||
| import java.util.Locale; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| public enum RankingPeriod { | ||||||||||||||||||||||||||
| DAILY("daily") { | ||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||
| public LocalDate resolveStartDate(String date) { | ||||||||||||||||||||||||||
| return parse(date); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||
| public String resolveKey(String date) { | ||||||||||||||||||||||||||
| return parse(date).format(FORMATTER); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| WEEKLY("weekly") { | ||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||
| public LocalDate resolveStartDate(String date) { | ||||||||||||||||||||||||||
| LocalDate target = parse(date); | ||||||||||||||||||||||||||
| return target.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||
| public String resolveKey(String date) { | ||||||||||||||||||||||||||
| return toYearMonthWeek(resolveStartDate(date)); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| MONTHLY("monthly") { | ||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||
| public LocalDate resolveStartDate(String date) { | ||||||||||||||||||||||||||
| LocalDate target = parse(date); | ||||||||||||||||||||||||||
| return target.withDayOfMonth(1); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||
| public String resolveKey(String date) { | ||||||||||||||||||||||||||
| return toYearMonth(resolveStartDate(date)); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| private static final ZoneId ZONE_ID = ZoneId.of("Asia/Seoul"); | ||||||||||||||||||||||||||
| private static final DateTimeFormatter FORMATTER = DateTimeFormatter.BASIC_ISO_DATE; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| private final String value; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| RankingPeriod(String value) { | ||||||||||||||||||||||||||
| this.value = value; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| public static RankingPeriod from(String value) { | ||||||||||||||||||||||||||
| if (value == null) { | ||||||||||||||||||||||||||
| return DAILY; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| return Arrays.stream(values()) | ||||||||||||||||||||||||||
| .filter(period -> period.value.equalsIgnoreCase(value)) | ||||||||||||||||||||||||||
| .findFirst() | ||||||||||||||||||||||||||
| .orElse(DAILY); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| public abstract LocalDate resolveStartDate(String date); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| public abstract String resolveKey(String date); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| public boolean isDaily() { | ||||||||||||||||||||||||||
| return this == DAILY; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| private static LocalDate parse(String date) { | ||||||||||||||||||||||||||
| if (date == null || date.isBlank()) { | ||||||||||||||||||||||||||
| return LocalDate.now(ZONE_ID); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| return LocalDate.parse(date, FORMATTER); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| private static String toYearMonthWeek(LocalDate target) { | ||||||||||||||||||||||||||
| WeekFields weekFields = WeekFields.of(Locale.KOREA); | ||||||||||||||||||||||||||
| int weekBasedYear = target.get(weekFields.weekBasedYear()); | ||||||||||||||||||||||||||
| int week = target.get(weekFields.weekOfWeekBasedYear()); | ||||||||||||||||||||||||||
| return String.format("%04d-W%02d", weekBasedYear, week); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
Comment on lines
+83
to
+88
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.
๐ ์์ ์ ์ private static String toYearMonthWeek(LocalDate target) {
- WeekFields weekFields = WeekFields.of(Locale.KOREA);
+ WeekFields weekFields = WeekFields.ISO;
int weekBasedYear = target.get(weekFields.weekBasedYear());
int week = target.get(weekFields.weekOfWeekBasedYear());
return String.format("%04d-W%02d", weekBasedYear, week);
}๐ Committable suggestion
Suggested change
๐ค Prompt for AI Agents |
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| private static String toYearMonth(LocalDate target) { | ||||||||||||||||||||||||||
| return String.format("%04d-%02d", target.getYear(), target.getMonthValue()); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| package com.loopers.domain.ranking; | ||
|
|
||
| import jakarta.persistence.Column; | ||
| import jakarta.persistence.EmbeddedId; | ||
| import jakarta.persistence.Entity; | ||
| import jakarta.persistence.Table; | ||
| import java.time.LocalDateTime; | ||
| import lombok.AccessLevel; | ||
| import lombok.Getter; | ||
| import lombok.NoArgsConstructor; | ||
|
|
||
| @Getter | ||
| @Entity | ||
| @Table(name = "mv_product_rank_monthly") | ||
| @NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
| public class MvProductRankMonthly { | ||
|
|
||
| @EmbeddedId | ||
| private ProductRankId id; | ||
|
|
||
| @Column(name = "like_count", nullable = false) | ||
| private long likeCount; | ||
|
|
||
| @Column(name = "sales_count", nullable = false) | ||
| private long salesCount; | ||
|
|
||
| @Column(name = "score", nullable = false) | ||
| private double score; | ||
|
|
||
| @Column(name = "rank", nullable = false) | ||
| private int rank; | ||
|
|
||
| @Column(name = "aggregated_at", nullable = false) | ||
| private LocalDateTime aggregatedAt; | ||
|
|
||
| private MvProductRankMonthly( | ||
| ProductRankId id, | ||
| long likeCount, | ||
| long salesCount, | ||
| double score, | ||
| int rank, | ||
| LocalDateTime aggregatedAt | ||
| ) { | ||
| this.id = id; | ||
| this.likeCount = likeCount; | ||
| this.salesCount = salesCount; | ||
| this.score = score; | ||
| this.rank = rank; | ||
| this.aggregatedAt = aggregatedAt; | ||
| } | ||
|
|
||
| public static MvProductRankMonthly create( | ||
| String yearMonth, | ||
| Long productId, | ||
| long likeCount, | ||
| long salesCount, | ||
| double score, | ||
| int rank, | ||
| LocalDateTime aggregatedAt | ||
| ) { | ||
| return new MvProductRankMonthly( | ||
| ProductRankId.of(yearMonth, productId), | ||
| likeCount, | ||
| salesCount, | ||
| score, | ||
| rank, | ||
| aggregatedAt | ||
| ); | ||
| } | ||
|
|
||
| public String getPeriodKey() { | ||
| return id != null ? id.getPeriodKey() : null; | ||
| } | ||
|
|
||
| public Long getProductId() { | ||
| return id != null ? id.getProductId() : null; | ||
| } | ||
| } |
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.
์ก์ ๋ฒ์ ์ ํน์ ์ปค๋ฐ SHA๋ก ๊ณ ์ ํ์ธ์.
@main๋ธ๋์น๋ฅผ ์ฌ์ฉํ๋ฉด ํญ์ ์ต์ ์ฝ๋๊ฐ ์คํ๋์ด ๊ณต๊ธ๋ง ๋ณด์ ์ํ์ด ๋ฐ์ํ ์ ์์ต๋๋ค. GitHub Actions ๋ณด์ ๋ชจ๋ฒ ์ฌ๋ก์ ๋ฐ๋ผ third-party ์ก์ ์ ํน์ ์ปค๋ฐ SHA๋ก ๊ณ ์ ํด์ผ ํฉ๋๋ค.๐ ๊ถ์ฅ ์์ ์ฌํญ
์ต์ ๋ฆด๋ฆฌ์ค์ ์ปค๋ฐ SHA๋ฅผ ํ์ธํ๋ ค๋ฉด ๋ค์ ์คํฌ๋ฆฝํธ๋ฅผ ์คํํ์ธ์:
๐ค Prompt for AI Agents