From d3e810461b2d15b130315d182d8fec280868eb61 Mon Sep 17 00:00:00 2001 From: baeksunghyun Date: Wed, 26 Feb 2025 10:35:57 +0900 Subject: [PATCH] =?UTF-8?q?Feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=EB=A7=8C=EC=9C=BC=EB=A1=9C=20=EC=8B=9C=EC=A6=8C?= =?UTF-8?q?=EB=B3=84=20=EC=A0=84=EC=B2=B4=20=EC=BB=A4=EB=B0=8B=20=EC=88=98?= =?UTF-8?q?,=20=EC=97=B0=EC=86=8D=20=EC=BB=A4=EB=B0=8B=20=EC=88=98,=20?= =?UTF-8?q?=EC=B5=9C=EB=8C=80=20=EC=97=B0=EC=86=8D=20=EC=BB=A4=EB=B0=8B=20?= =?UTF-8?q?=EC=88=98=20=EB=B6=88=EB=9F=AC=EC=98=A4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/TotalCommitController.java | 40 ++++++ .../dto/TotalCommitGraphQLResponse.java | 27 ++++- .../dto/TotalCommitResponseDto.java | 15 ++- .../service/TotalCommitService.java | 114 +++++++++++++++++- 4 files changed, 187 insertions(+), 9 deletions(-) diff --git a/src/main/java/cmf/commitField/domain/commit/totalCommit/controller/TotalCommitController.java b/src/main/java/cmf/commitField/domain/commit/totalCommit/controller/TotalCommitController.java index ecac86b..0df38d3 100644 --- a/src/main/java/cmf/commitField/domain/commit/totalCommit/controller/TotalCommitController.java +++ b/src/main/java/cmf/commitField/domain/commit/totalCommit/controller/TotalCommitController.java @@ -7,6 +7,9 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; +import java.time.LocalDateTime; +import java.time.temporal.TemporalAdjusters; + @RestController @RequiredArgsConstructor public class TotalCommitController { @@ -16,4 +19,41 @@ public class TotalCommitController { public TotalCommitResponseDto getTotalCommits(@PathVariable String username) { return totalCommitService.getTotalCommitCount(username); } + + // 봄 시즌(3/1 - 5/31) + @GetMapping("/api/commits/{username}/spring") + public TotalCommitResponseDto getSpringSeasonCommits(@PathVariable String username) { + int currentYear = LocalDateTime.now().getYear(); // 현재는 테스트용으로 2024 대입 + LocalDateTime since = LocalDateTime.of(2024, 3, 1, 0, 0); + LocalDateTime until = LocalDateTime.of(2024, 5, 31, 23, 59, 59); + return totalCommitService.getSeasonCommits(username, since, until); + } + + // 여름 시즌(6/1 - 8/31) + @GetMapping("/api/commits/{username}/summer") + public TotalCommitResponseDto getSummerSeasonCommits(@PathVariable String username) { + int currentYear = LocalDateTime.now().getYear(); // 현재는 테스트용으로 2024 대입 + LocalDateTime since = LocalDateTime.of(2024, 6, 1, 0, 0); + LocalDateTime until = LocalDateTime.of(2024, 8, 31, 23, 59, 59); + return totalCommitService.getSeasonCommits(username, since, until); + } + + // 가을 시즌(9/1 - 11/30) + @GetMapping("/api/commits/{username}/fall") + public TotalCommitResponseDto getFallSeasonCommits(@PathVariable String username) { + int currentYear = LocalDateTime.now().getYear(); // 현재는 테스트용으로 2024 대입 + LocalDateTime since = LocalDateTime.of(2024, 9, 1, 0, 0); + LocalDateTime until = LocalDateTime.of(2024, 11, 30, 23, 59, 59); + return totalCommitService.getSeasonCommits(username, since, until); + } + + // 겨울 시즌(이전 년도 12/1 - 다음 년도 2/28) + @GetMapping("/api/commits/{username}/winter") + public TotalCommitResponseDto getWinterSeasonCommits(@PathVariable String username) { + int currentYear = LocalDateTime.now().getYear(); // 현재는 테스트용으로 2024 대입 + LocalDateTime since = LocalDateTime.of(2024 - 1, 12, 1, 0, 0); + LocalDateTime until = LocalDateTime.of(2024, 2, 1, 23, 59, 59) + .with(TemporalAdjusters.lastDayOfMonth()); + return totalCommitService.getSeasonCommits(username, since, until); + } } diff --git a/src/main/java/cmf/commitField/domain/commit/totalCommit/dto/TotalCommitGraphQLResponse.java b/src/main/java/cmf/commitField/domain/commit/totalCommit/dto/TotalCommitGraphQLResponse.java index 6d45eeb..92bfe8c 100644 --- a/src/main/java/cmf/commitField/domain/commit/totalCommit/dto/TotalCommitGraphQLResponse.java +++ b/src/main/java/cmf/commitField/domain/commit/totalCommit/dto/TotalCommitGraphQLResponse.java @@ -3,6 +3,8 @@ import lombok.Getter; import lombok.NoArgsConstructor; +import java.util.List; + /* 응답구조 토대로 구현 { @@ -37,7 +39,28 @@ public static class CommitUser { @Getter @NoArgsConstructor public static class ContributionsCollection { - private long totalCommitContributions; - private long restrictedContributionsCount; + private long totalCommitContributions; // 공개 레포 커밋 + private long restrictedContributionsCount; // 비공개 레포 커밋 + private ContributionCalendar contributionCalendar; + } + + @Getter + @NoArgsConstructor + public static class ContributionCalendar { + private int totalContributions; + private List weeks; + } + + @Getter + @NoArgsConstructor + public static class Week { + private List contributionDays; + } + + @Getter + @NoArgsConstructor + public static class ContributionDay { + private int contributionCount; + private String date; } } diff --git a/src/main/java/cmf/commitField/domain/commit/totalCommit/dto/TotalCommitResponseDto.java b/src/main/java/cmf/commitField/domain/commit/totalCommit/dto/TotalCommitResponseDto.java index b71a44e..344c52e 100644 --- a/src/main/java/cmf/commitField/domain/commit/totalCommit/dto/TotalCommitResponseDto.java +++ b/src/main/java/cmf/commitField/domain/commit/totalCommit/dto/TotalCommitResponseDto.java @@ -6,8 +6,21 @@ @Getter @NoArgsConstructor -@AllArgsConstructor public class TotalCommitResponseDto { private long totalCommitContributions; private long restrictedContributionsCount; + private int currentStreakDays; + private int maxStreakDays; + + public TotalCommitResponseDto(long totalCommitContributions, long restrictedContributionsCount, int currentStreakDays, int maxStreakDays) { + this.totalCommitContributions = totalCommitContributions; + this.restrictedContributionsCount = restrictedContributionsCount; + this.currentStreakDays = currentStreakDays; + this.maxStreakDays = maxStreakDays; + } + + public TotalCommitResponseDto(long totalCommitContributions, long restrictedContributionsCount) { + this.totalCommitContributions = totalCommitContributions; + this.restrictedContributionsCount = restrictedContributionsCount; + } } diff --git a/src/main/java/cmf/commitField/domain/commit/totalCommit/service/TotalCommitService.java b/src/main/java/cmf/commitField/domain/commit/totalCommit/service/TotalCommitService.java index e37bfcd..00179ac 100644 --- a/src/main/java/cmf/commitField/domain/commit/totalCommit/service/TotalCommitService.java +++ b/src/main/java/cmf/commitField/domain/commit/totalCommit/service/TotalCommitService.java @@ -9,6 +9,12 @@ import org.springframework.stereotype.Service; import org.springframework.web.reactive.function.client.WebClient; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Map; @@ -23,14 +29,11 @@ public class TotalCommitService { private final WebClient webClient = WebClient.builder() .baseUrl(BASE_URL) - .defaultHeader(HttpHeaders.CONTENT_TYPE , MediaType.APPLICATION_JSON_VALUE) + .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .build(); + // 기존 메서드 public TotalCommitResponseDto getTotalCommitCount(String username) { -// String query = String.format(""" -// {"query": "query { user(login: \\\"%s\\\") { contributionsCollection { totalCommitContributions restrictedContributionsCount } } }"} -// """, username); - // GraphQL 쿼리를 Map으로 구성 Map requestBody = Map.of( "query", String.format( "query { user(login: \"%s\") { contributionsCollection { totalCommitContributions restrictedContributionsCount } } }", @@ -45,11 +48,110 @@ public TotalCommitResponseDto getTotalCommitCount(String username) { .bodyToMono(TotalCommitGraphQLResponse.class) .block(); - TotalCommitGraphQLResponse.ContributionsCollection contributions = response.getData().getUser().getContributionsCollection(); + TotalCommitGraphQLResponse.ContributionsCollection contributions = + response.getData().getUser().getContributionsCollection(); return new TotalCommitResponseDto( contributions.getTotalCommitContributions(), contributions.getRestrictedContributionsCount() ); } + + // 시즌별 커밋 분석 + public TotalCommitResponseDto getSeasonCommits(String username, LocalDateTime since, LocalDateTime until) { + String query = String.format(""" + query { + user(login: "%s") { + contributionsCollection(from: "%s", to: "%s") { + totalCommitContributions + restrictedContributionsCount + contributionCalendar { + totalContributions + weeks { + contributionDays { + contributionCount + date + } + } + } + } + } + } + """, username, since.format(DateTimeFormatter.ISO_DATE_TIME), until.format(DateTimeFormatter.ISO_DATE_TIME)); + + Map requestBody = Map.of("query", query); + + TotalCommitGraphQLResponse response = webClient.post() + .header("Authorization", "bearer " + PAT) + .bodyValue(requestBody) + .retrieve() + .bodyToMono(TotalCommitGraphQLResponse.class) + .block(); + + if (response == null || response.getData() == null || response.getData().getUser() == null) { + throw new RuntimeException("Failed to fetch GitHub data"); + } + + TotalCommitGraphQLResponse.ContributionsCollection contributions = + response.getData().getUser().getContributionsCollection(); + + List commitDates = extractCommitDates(contributions.getContributionCalendar()); + StreakResult streaks = calculateStreaks(commitDates); + + return new TotalCommitResponseDto( + contributions.getTotalCommitContributions(), + contributions.getRestrictedContributionsCount(), + streaks.currentStreak, + streaks.maxStreak + ); + } + + private List extractCommitDates(TotalCommitGraphQLResponse.ContributionCalendar calendar) { + List dates = new ArrayList<>(); + calendar.getWeeks().forEach(week -> + week.getContributionDays().forEach(day -> { + if (day.getContributionCount() > 0) { + dates.add(LocalDate.parse(day.getDate())); + } + }) + ); + return dates; + } + + @RequiredArgsConstructor + private static class StreakResult { + final int currentStreak; + final int maxStreak; + } + + private StreakResult calculateStreaks(List commitDates) { + if (commitDates.isEmpty()) { + return new StreakResult(0, 0); + } + + Collections.sort(commitDates); + int currentStreak = 0; + int maxStreak = 0; + int tempStreak = 0; + LocalDate previousDate = null; + + for (LocalDate date : commitDates) { + if (previousDate == null || date.minusDays(1).equals(previousDate)) { + tempStreak++; + } else { + tempStreak = 1; + } + maxStreak = Math.max(maxStreak, tempStreak); + previousDate = date; + } + + // 현재 스트릭 계산 (마지막 커밋이 오늘 또는 어제인 경우) + LocalDate today = LocalDate.now(); + LocalDate lastCommitDate = commitDates.get(commitDates.size() - 1); + if (lastCommitDate.equals(today) || lastCommitDate.equals(today.minusDays(1))) { + currentStreak = tempStreak; + } + + return new StreakResult(currentStreak, maxStreak); + } }