Skip to content

Commit 54febc1

Browse files
committed
Feat: 시즌 별 연속 커밋 횟수, 최대 연속 커밋 횟수, 총 커밋 수 불러오기
1 parent ad6f53c commit 54febc1

File tree

3 files changed

+152
-32
lines changed

3 files changed

+152
-32
lines changed
Lines changed: 38 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package cmf.commitField.domain.commit.sinceCommit.controller;
22

3+
import cmf.commitField.domain.commit.sinceCommit.dto.CommitAnalysisResponseDto;
34
import cmf.commitField.domain.commit.sinceCommit.dto.SinceCommitResponseDto;
45
import cmf.commitField.domain.commit.sinceCommit.service.SinceCommitService;
56
import lombok.RequiredArgsConstructor;
@@ -24,72 +25,79 @@ public ResponseEntity<List<SinceCommitResponseDto>> getCommits(
2425
@RequestParam String repo,
2526
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime since,
2627
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime until
27-
) {
28+
) {
2829
List<SinceCommitResponseDto> sinceCommits = sinceCommitService.getSinceCommits(owner, repo, since, until);
29-
3030
return ResponseEntity.ok(sinceCommits);
3131
}
3232

33-
// 시즌 별 커밋 수 불러오기
33+
// 연속 커밋 일수와 같은 추가 분석 정보를 포함하여 반환
34+
@GetMapping("/api/github/commits-analysis")
35+
public ResponseEntity<CommitAnalysisResponseDto> getCommitsAnalysis(
36+
@RequestParam String owner,
37+
@RequestParam String repo,
38+
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime since,
39+
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime until
40+
) {
41+
CommitAnalysisResponseDto analysis = sinceCommitService.getCommitAnalysis(owner, repo, since, until);
42+
return ResponseEntity.ok(analysis);
43+
}
44+
45+
// 시즌 별 커밋 정보 불러오기(테스트용으로 2024년 설정)
3446

35-
// 봄 시즌
47+
// 봄 시즌(3/1 - 5/31)
3648
@GetMapping("/api/github/commits-spring")
37-
public ResponseEntity<List<SinceCommitResponseDto>> getSpringSeasonCommits(
49+
public ResponseEntity<CommitAnalysisResponseDto> getSpringSeasonCommits(
3850
@RequestParam String owner,
3951
@RequestParam String repo
4052
) {
4153
int currentYear = LocalDateTime.now().getYear();
4254
LocalDateTime since = LocalDateTime.of(2024, 3, 1, 0, 0);
4355
LocalDateTime until = LocalDateTime.of(2024, 5, 31, 23, 59, 59);
4456

45-
List<SinceCommitResponseDto> sinceCommits = sinceCommitService.getSinceCommits(owner, repo, since, until);
46-
47-
return ResponseEntity.ok(sinceCommits);
57+
CommitAnalysisResponseDto analysis = sinceCommitService.getCommitAnalysis(owner, repo, since, until);
58+
return ResponseEntity.ok(analysis);
4859
}
4960

50-
// 여름 시즌
61+
// 여름 시즌(6/1 - 8/31)
5162
@GetMapping("/api/github/commits-summer")
52-
public ResponseEntity<List<SinceCommitResponseDto>> getSummerSeasonCommits(
63+
public ResponseEntity<CommitAnalysisResponseDto> getSummerSeasonCommits(
5364
@RequestParam String owner,
5465
@RequestParam String repo
5566
) {
5667
int currentYear = LocalDateTime.now().getYear();
57-
LocalDateTime since = LocalDateTime.of(currentYear, 6, 1, 0, 0);
58-
LocalDateTime until = LocalDateTime.of(currentYear, 8, 31, 23, 59, 59);
59-
60-
List<SinceCommitResponseDto> sinceCommits = sinceCommitService.getSinceCommits(owner, repo, since, until);
68+
LocalDateTime since = LocalDateTime.of(2024, 6, 1, 0, 0);
69+
LocalDateTime until = LocalDateTime.of(2024, 8, 31, 23, 59, 59);
6170

62-
return ResponseEntity.ok(sinceCommits);
71+
CommitAnalysisResponseDto analysis = sinceCommitService.getCommitAnalysis(owner, repo, since, until);
72+
return ResponseEntity.ok(analysis);
6373
}
6474

65-
// 가을 시즌
75+
// 가을 시즌(9/1 - 11/30)
6676
@GetMapping("/api/github/commits-fall")
67-
public ResponseEntity<List<SinceCommitResponseDto>> getFallSeasonCommits(
77+
public ResponseEntity<CommitAnalysisResponseDto> getFallSeasonCommits(
6878
@RequestParam String owner,
6979
@RequestParam String repo
7080
) {
7181
int currentYear = LocalDateTime.now().getYear();
72-
LocalDateTime since = LocalDateTime.of(currentYear, 9, 1, 0, 0);
73-
LocalDateTime until = LocalDateTime.of(currentYear, 11, 30, 23, 59, 59);
74-
75-
List<SinceCommitResponseDto> sinceCommits = sinceCommitService.getSinceCommits(owner, repo, since, until);
82+
LocalDateTime since = LocalDateTime.of(2024, 9, 1, 0, 0);
83+
LocalDateTime until = LocalDateTime.of(2024, 11, 30, 23, 59, 59);
7684

77-
return ResponseEntity.ok(sinceCommits);
85+
CommitAnalysisResponseDto analysis = sinceCommitService.getCommitAnalysis(owner, repo, since, until);
86+
return ResponseEntity.ok(analysis);
7887
}
7988

80-
// 겨울 시즌
89+
// 겨울 시즌(이전 년도 12/1 - 다음 년도 2/30)
8190
@GetMapping("/api/github/commits-winter")
82-
public ResponseEntity<List<SinceCommitResponseDto>> getWinterSeasonCommits(
91+
public ResponseEntity<CommitAnalysisResponseDto> getWinterSeasonCommits(
8392
@RequestParam String owner,
8493
@RequestParam String repo
8594
) {
8695
int currentYear = LocalDateTime.now().getYear();
87-
LocalDateTime since = LocalDateTime.of(currentYear, 12, 1, 0, 0);
88-
LocalDateTime until = LocalDateTime.of(currentYear + 1, 2, 1, 23, 59, 59)
89-
.with(TemporalAdjusters.lastDayOfMonth()); // 윤년 고려
96+
LocalDateTime since = LocalDateTime.of(2023, 12, 1, 0, 0);
97+
LocalDateTime until = LocalDateTime.of(2024 + 1, 2, 1, 23, 59, 59)
98+
.with(TemporalAdjusters.lastDayOfMonth());
9099

91-
List<SinceCommitResponseDto> sinceCommits = sinceCommitService.getSinceCommits(owner, repo, since, until);
92-
93-
return ResponseEntity.ok(sinceCommits);
100+
CommitAnalysisResponseDto analysis = sinceCommitService.getCommitAnalysis(owner, repo, since, until);
101+
return ResponseEntity.ok(analysis);
94102
}
95103
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package cmf.commitField.domain.commit.sinceCommit.dto;
2+
3+
import lombok.Getter;
4+
import lombok.RequiredArgsConstructor;
5+
import lombok.Setter;
6+
7+
import java.util.List;
8+
9+
@Getter
10+
@Setter
11+
public class CommitAnalysisResponseDto {
12+
private final List<SinceCommitResponseDto> commits;
13+
private final int totalCommits;
14+
private final int currentStreakDays;
15+
private final int maxStreakDays;
16+
17+
public CommitAnalysisResponseDto(List<SinceCommitResponseDto> commits, int currentStreakDays, int maxStreakDays) {
18+
this.commits = commits;
19+
this.totalCommits = commits.size();
20+
this.currentStreakDays = currentStreakDays;
21+
this.maxStreakDays = maxStreakDays;
22+
}
23+
}

src/main/java/cmf/commitField/domain/commit/sinceCommit/service/SinceCommitService.java

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package cmf.commitField.domain.commit.sinceCommit.service;
22

3+
import cmf.commitField.domain.commit.sinceCommit.dto.CommitAnalysisResponseDto;
34
import cmf.commitField.domain.commit.sinceCommit.dto.SinceCommitResponseDto;
5+
import lombok.AllArgsConstructor;
6+
import lombok.NoArgsConstructor;
7+
import lombok.RequiredArgsConstructor;
48
import lombok.extern.slf4j.Slf4j;
59
import org.springframework.beans.factory.annotation.Value;
610
import org.springframework.core.ParameterizedTypeReference;
@@ -9,9 +13,12 @@
913
import org.springframework.stereotype.Service;
1014
import org.springframework.web.reactive.function.client.WebClient;
1115

16+
import java.time.LocalDate;
1217
import java.time.LocalDateTime;
18+
import java.time.ZoneOffset;
1319
import java.time.format.DateTimeFormatter;
14-
import java.util.List;
20+
import java.util.*;
21+
import java.util.stream.Collectors;
1522

1623
@Slf4j
1724
@Service
@@ -37,13 +44,95 @@ public List<SinceCommitResponseDto> getSinceCommits(String owner, String repo, L
3744
.build(owner, repo))
3845
.header("Authorization", "bearer " + PAT)
3946
.retrieve()
40-
.bodyToMono(new ParameterizedTypeReference<List<SinceCommitResponseDto>>() {})
47+
.bodyToMono(new ParameterizedTypeReference<List<SinceCommitResponseDto>>() {
48+
})
4149
.block();
4250
} catch (Exception e) {
4351
log.error("GitHub API 호출 중 오류 발생: {}", e.getMessage());
4452
throw new RuntimeException("GitHub API 호출 중 오류가 발생했습니다.", e);
4553
}
4654
}
55+
56+
// 연속 커밋 수 계산
57+
58+
// 새로운 분석 메서드 추가
59+
public CommitAnalysisResponseDto getCommitAnalysis(String owner, String repo, LocalDateTime since, LocalDateTime until) {
60+
List<SinceCommitResponseDto> commits = getSinceCommits(owner, repo, since, until);
61+
StreakResult streakResult = calculateStreaks(commits);
62+
return new CommitAnalysisResponseDto(commits, streakResult.currentStreak, streakResult.maxStreak);
63+
}
64+
65+
@RequiredArgsConstructor
66+
private static class StreakResult {
67+
final int currentStreak;
68+
final int maxStreak;
69+
}
70+
71+
private StreakResult calculateStreaks(List<SinceCommitResponseDto> commits) {
72+
if (commits == null || commits.isEmpty()) {
73+
return new StreakResult(0, 0);
74+
}
75+
76+
// 커밋 날짜를 LocalDate로 변환하고 Set으로 중복 제거
77+
Set<LocalDate> commitDates = commits.stream()
78+
.map(commit -> LocalDateTime.parse(
79+
commit.getCommit().getAuthor().getDate(),
80+
DateTimeFormatter.ISO_DATE_TIME)
81+
.toLocalDate())
82+
.collect(Collectors.toCollection(TreeSet::new));
83+
84+
// 오늘 날짜 가져오기 (UTC 기준)
85+
LocalDate today = LocalDate.now(ZoneOffset.UTC);
86+
87+
// 현재 streak 계산
88+
int currentStreak = calculateCurrentStreak(commitDates, today);
89+
90+
// 최대 streak 계산
91+
int maxStreak = calculateMaxStreak(commitDates);
92+
93+
return new StreakResult(currentStreak, maxStreak);
94+
}
95+
96+
private int calculateCurrentStreak(Set<LocalDate> commitDates, LocalDate today) {
97+
// 어제 커밋이 없으면 오늘 커밋만 확인
98+
if (!commitDates.contains(today.minusDays(1))) {
99+
return commitDates.contains(today) ? 1 : 0;
100+
}
101+
102+
int streakCount = 0;
103+
LocalDate checkDate = today;
104+
105+
// 오늘부터 역순으로 연속된 커밋이 있는지 확인
106+
while (commitDates.contains(checkDate)) {
107+
streakCount++;
108+
checkDate = checkDate.minusDays(1);
109+
}
110+
111+
return streakCount;
112+
}
113+
114+
private int calculateMaxStreak(Set<LocalDate> commitDates) {
115+
if (commitDates.isEmpty()) {
116+
return 0;
117+
}
118+
119+
List<LocalDate> sortedDates = new ArrayList<>(commitDates);
120+
Collections.sort(sortedDates);
121+
122+
int maxStreak = 1;
123+
int currentStreak = 1;
124+
125+
for (int i = 1; i < sortedDates.size(); i++) {
126+
if (sortedDates.get(i).minusDays(1).equals(sortedDates.get(i - 1))) {
127+
currentStreak++;
128+
maxStreak = Math.max(maxStreak, currentStreak);
129+
} else {
130+
currentStreak = 1;
131+
}
132+
}
133+
134+
return maxStreak;
135+
}
47136
}
48137
/*
49138
ParameterizedTypeReference를 사용하는 주요 이유

0 commit comments

Comments
 (0)