-
Notifications
You must be signed in to change notification settings - Fork 1
Feature/283 봉사 시간 랭킹 기능 구현 #310
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
Merged
Merged
Changes from 1 commit
Commits
Show all changes
39 commits
Select commit
Hold shift + click to select a range
24ca061
chore: 컨벤션에 위배된 메서드 네임 수정
7zrv 3d3bb57
depend: queryDsl의 SQLExpression 사용을 위한 의존성 추가
7zrv 627720f
chore: 테스트 클래스명 수정
7zrv 9a0702f
feat: 전체, 월별, 주간 봉사 시간 랭킹 쿼리 구현
7zrv 902d44c
feat: 랭킹 계산 유스케이스 구현
7zrv a754527
feat: 랭킹 캐싱을 위한 redis 레포지토리와 유스케이스 구현
7zrv 92a6ce4
feat: 랭킹 계산, 캐싱을 위한 스케줄러 구현
7zrv 0be813a
feat: 봉사 랭킹 조회 유스케이스 구현
7zrv e65c87d
feat: 테스트용 DB 설정 추가
7zrv c2c8e0d
feat: 누락된 테스트 클래스 추가
7zrv 33c7151
feat: test container 추가
7zrv 756d27e
chore: application-test.yml 수정
7zrv 26da3b0
refactor: 랭킹 산출 쿼리 리팩토링
7zrv 366523b
feat: 봉사 랭킹 엔드 포인트 구현
7zrv 4abb947
feat: 봉사 시간 랭킹 스케줄러 로깅 추가
7zrv 482ddfa
refactor: 봉사 시간 랭킹 반환 예외 메세지 수정
7zrv b753096
feat: 봉사자 닉네임 리스트 반환 쿼리 메서드 생성
7zrv b627ba1
feat: 봉사자 닉네임 리스트 반환 유스케이스 구현
7zrv d32c9e0
chore: 봉사자 닉네임 반환 관련 클래스의 명시적인 이름 수정
7zrv 81a1e1c
feat: 봉사 시간 랭킹 Dto nickname 필드 추가
7zrv 98fa3af
refactor: 봉사자 닉네임 반환 유스케이스 리팩토링
7zrv e2ec50d
refactor: 봉사자 닉네임 반환 유스케이스 리팩토링
7zrv 529ec4f
refactor: 봉사 랭킹의 닉네임 필드 추가에 따른 테스트 수정
7zrv eeaf438
refactor: 봉사 랭킹 객체 맵퍼 리팩토링
7zrv 5587c96
refactor: 봉사 시간 랭킹 유스케이스 리팩토링
7zrv c27cfbc
chore: 불필요한 클래스 삭제및 build.gradle 개행 수정
7zrv 9f4953d
feat: test container 설정 추가
7zrv b6d7ac5
fix: 에러 발생 test 수정
7zrv 78e3cf1
chore: cicd workflow 수정
7zrv c91552e
fix: ci 파이프 라인 수정
7zrv f2ff999
fix: ci 파이프 라인 수정
7zrv cb62123
fix: ci 파이프 라인 수정
7zrv 450d909
fix: 봉사시간 계산 테스트 날짜 수정
7zrv 0914343
fix: ci 테스트 레포트 분석을 위한 설정 추가
7zrv 03126bd
fix: 동시성 테스트 스레드 획득 에러 수정
7zrv e625296
fix: cicd 빌드 로그 옵션 제거
7zrv e6e3077
fix: 에러 발생 테스트 주석처리
7zrv 9a9331c
chore: 불필요한 구문 제거및 개행 추가
7zrv 4557b6d
chore: 코드 리뷰사항 반영
7zrv File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
10 changes: 10 additions & 0 deletions
10
...com/somemore/domains/volunteerrecord/dto/response/VolunteerMonthlyRankingResponseDto.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package com.somemore.domains.volunteerrecord.dto.response; | ||
|
|
||
| import java.util.UUID; | ||
|
|
||
| public record VolunteerMonthlyRankingResponseDto( | ||
| UUID volunteerId, | ||
| int totalHours, | ||
| long ranking | ||
| ) { | ||
| } |
28 changes: 28 additions & 0 deletions
28
...n/java/com/somemore/domains/volunteerrecord/dto/response/VolunteerRankingResponseDto.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| package com.somemore.domains.volunteerrecord.dto.response; | ||
|
|
||
| import com.fasterxml.jackson.databind.PropertyNamingStrategies; | ||
| import com.fasterxml.jackson.databind.annotation.JsonNaming; | ||
| import com.somemore.domains.center.dto.response.PreferItemResponseDto; | ||
| import lombok.Builder; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) | ||
| @Builder | ||
| public record VolunteerRankingResponseDto( | ||
| List<VolunteerTotalRankingResponseDto> volunteerTotalRankingResponse, | ||
| List<VolunteerMonthlyRankingResponseDto> volunteerMonthlyResponse, | ||
| List<VolunteerWeeklyRankingResponseDto> volunteerWeeklyRankingResponse | ||
| ) { | ||
| public static VolunteerRankingResponseDto of( | ||
| List<VolunteerTotalRankingResponseDto> totalRanking, | ||
| List<VolunteerMonthlyRankingResponseDto> monthlyRanking, | ||
| List<VolunteerWeeklyRankingResponseDto> weeklyRanking){ | ||
|
|
||
| return VolunteerRankingResponseDto.builder() | ||
| .volunteerTotalRankingResponse(totalRanking) | ||
| .volunteerMonthlyResponse(monthlyRanking) | ||
| .volunteerWeeklyRankingResponse(weeklyRanking) | ||
| .build(); | ||
| } | ||
| } | ||
11 changes: 11 additions & 0 deletions
11
...a/com/somemore/domains/volunteerrecord/dto/response/VolunteerTotalRankingResponseDto.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package com.somemore.domains.volunteerrecord.dto.response; | ||
|
|
||
| import java.util.UUID; | ||
|
|
||
| public record VolunteerTotalRankingResponseDto( | ||
| UUID volunteerId, | ||
| int totalHours, | ||
| long ranking | ||
| ) { | ||
|
|
||
| } |
10 changes: 10 additions & 0 deletions
10
.../com/somemore/domains/volunteerrecord/dto/response/VolunteerWeeklyRankingResponseDto.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package com.somemore.domains.volunteerrecord.dto.response; | ||
|
|
||
| import java.util.UUID; | ||
|
|
||
| public record VolunteerWeeklyRankingResponseDto( | ||
| UUID volunteerId, | ||
| int totalHours, | ||
| long ranking | ||
| ) { | ||
| } |
38 changes: 38 additions & 0 deletions
38
...n/java/com/somemore/domains/volunteerrecord/repository/mapper/VolunteerRankingMapper.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| package com.somemore.domains.volunteerrecord.repository.mapper; | ||
|
|
||
| import com.somemore.domains.volunteerrecord.dto.response.VolunteerMonthlyRankingResponseDto; | ||
| import com.somemore.domains.volunteerrecord.dto.response.VolunteerTotalRankingResponseDto; | ||
| import com.somemore.domains.volunteerrecord.dto.response.VolunteerWeeklyRankingResponseDto; | ||
|
|
||
| import java.util.UUID; | ||
|
|
||
| public class VolunteerRankingMapper { | ||
|
|
||
| private VolunteerRankingMapper() { | ||
| throw new UnsupportedOperationException("유틸리티 클래스는 인스턴스화할 수 없습니다."); | ||
| } | ||
|
|
||
| public static VolunteerTotalRankingResponseDto toTotalRankingResponse(Object[] result) { | ||
| return new VolunteerTotalRankingResponseDto( | ||
| (UUID) result[0], | ||
| ((Number) result[1]).intValue(), | ||
| ((Number) result[2]).longValue() | ||
| ); | ||
| } | ||
|
|
||
| public static VolunteerWeeklyRankingResponseDto toWeeklyRankingResponse(Object[] result) { | ||
| return new VolunteerWeeklyRankingResponseDto( | ||
| (UUID) result[0], | ||
| ((Number) result[1]).intValue(), | ||
| ((Number) result[2]).longValue() | ||
| ); | ||
| } | ||
|
|
||
| public static VolunteerMonthlyRankingResponseDto toMonthlyRankingResponse(Object[] result) { | ||
| return new VolunteerMonthlyRankingResponseDto( | ||
| (UUID) result[0], | ||
| ((Number) result[1]).intValue(), | ||
| ((Number) result[2]).longValue() | ||
| ); | ||
| } | ||
| } |
48 changes: 48 additions & 0 deletions
48
src/main/java/com/somemore/domains/volunteerrecord/service/CalculateRankingService.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| package com.somemore.domains.volunteerrecord.service; | ||
|
|
||
| import com.somemore.domains.volunteerrecord.dto.response.VolunteerMonthlyRankingResponseDto; | ||
| import com.somemore.domains.volunteerrecord.dto.response.VolunteerRankingResponseDto; | ||
| import com.somemore.domains.volunteerrecord.dto.response.VolunteerTotalRankingResponseDto; | ||
| import com.somemore.domains.volunteerrecord.dto.response.VolunteerWeeklyRankingResponseDto; | ||
| import com.somemore.domains.volunteerrecord.repository.VolunteerRecordRepository; | ||
| import com.somemore.domains.volunteerrecord.repository.mapper.VolunteerRankingMapper; | ||
| import com.somemore.domains.volunteerrecord.usecase.CalculateRankingUseCase; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.stereotype.Service; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| @RequiredArgsConstructor | ||
| @Service | ||
| @Transactional(readOnly = true) | ||
| public class CalculateRankingService implements CalculateRankingUseCase { | ||
|
|
||
| private final VolunteerRecordRepository volunteerRecordRepository; | ||
|
|
||
| @Override | ||
| public VolunteerRankingResponseDto calculateRanking() { | ||
|
|
||
| List<Object[]> volunteerTotalRanking = volunteerRecordRepository.findTotalTopRankingWithTies(); | ||
| List<Object[]> volunteerMonthlyRanking = volunteerRecordRepository.findMonthlyTopRankingWithTies(); | ||
| List<Object[]> volunteerWeeklyRanking = volunteerRecordRepository.findWeeklyTopRankingWithTies(); | ||
|
|
||
| List<VolunteerTotalRankingResponseDto> totalRankingDtos = volunteerTotalRanking.stream() | ||
| .map(VolunteerRankingMapper::toTotalRankingResponse) | ||
| .toList(); | ||
|
|
||
| List<VolunteerMonthlyRankingResponseDto> monthlyRankingDtos = volunteerMonthlyRanking.stream() | ||
| .map(VolunteerRankingMapper::toMonthlyRankingResponse) | ||
| .toList(); | ||
|
|
||
| List<VolunteerWeeklyRankingResponseDto> weeklyRankingDtos = volunteerWeeklyRanking.stream() | ||
| .map(VolunteerRankingMapper::toWeeklyRankingResponse) | ||
| .toList(); | ||
|
|
||
| return VolunteerRankingResponseDto.of( | ||
| totalRankingDtos, | ||
| monthlyRankingDtos, | ||
| weeklyRankingDtos | ||
| ); | ||
| } | ||
| } |
7 changes: 7 additions & 0 deletions
7
src/main/java/com/somemore/domains/volunteerrecord/usecase/CalculateRankingUseCase.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package com.somemore.domains.volunteerrecord.usecase; | ||
|
|
||
| import com.somemore.domains.volunteerrecord.dto.response.VolunteerRankingResponseDto; | ||
|
|
||
| public interface CalculateRankingUseCase { | ||
| VolunteerRankingResponseDto calculateRanking(); | ||
| } |
119 changes: 119 additions & 0 deletions
119
src/test/java/com/somemore/domains/volunteerrecord/service/CalculateRankingServiceTest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| package com.somemore.domains.volunteerrecord.service; | ||
|
|
||
| import com.somemore.domains.volunteerrecord.domain.VolunteerRecord; | ||
| import com.somemore.domains.volunteerrecord.dto.response.VolunteerRankingResponseDto; | ||
| import com.somemore.domains.volunteerrecord.repository.VolunteerRecordRepository; | ||
| import com.somemore.support.IntegrationTestSupport; | ||
| import org.junit.jupiter.api.DisplayName; | ||
| import org.junit.jupiter.api.Test; | ||
| import org.springframework.beans.factory.annotation.Autowired; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
|
|
||
| import java.time.LocalDate; | ||
| import java.util.UUID; | ||
|
|
||
| import static org.assertj.core.api.Assertions.assertThat; | ||
|
|
||
| @Transactional | ||
| class CalculateRankingServiceTest extends IntegrationTestSupport { | ||
|
|
||
| @Autowired | ||
| private CalculateRankingService calculateRankingService; | ||
|
|
||
| @Autowired | ||
| private VolunteerRecordRepository volunteerRecordRepository; | ||
|
|
||
| @DisplayName("전체 기간 봉사 시간 합계로 4위까지의 랭킹을 반환할 수 있다. 동점자를 같이 반환한다.") | ||
| @Test | ||
| void calculateTotalVolunteerRanking() { | ||
|
|
||
| // given | ||
| volunteerRecordRepository.save(VolunteerRecord.create(UUID.randomUUID(), "봉사1", LocalDate.of(2025, 1, 10), 100)); | ||
| volunteerRecordRepository.save(VolunteerRecord.create(UUID.randomUUID(), "봉사2", LocalDate.of(2025, 1, 11), 100)); | ||
| volunteerRecordRepository.save(VolunteerRecord.create(UUID.randomUUID(), "봉사3", LocalDate.of(2025, 1, 12), 90)); | ||
| volunteerRecordRepository.save(VolunteerRecord.create(UUID.randomUUID(), "봉사4", LocalDate.of(2025, 1, 13), 60)); | ||
| volunteerRecordRepository.save(VolunteerRecord.create(UUID.randomUUID(), "봉사5", LocalDate.of(2025, 1, 14), 50)); | ||
| volunteerRecordRepository.save(VolunteerRecord.create(UUID.randomUUID(), "봉사6", LocalDate.of(2025, 1, 15), 30)); | ||
|
|
||
| // when | ||
| VolunteerRankingResponseDto ranking = calculateRankingService.calculateRanking(); | ||
|
|
||
| // then | ||
| assertThat(ranking.volunteerTotalRankingResponse()).hasSize(5); | ||
|
|
||
| assertThat(ranking.volunteerTotalRankingResponse().get(0).totalHours()).isEqualTo(100L); | ||
| assertThat(ranking.volunteerTotalRankingResponse().get(0).ranking()).isEqualTo(1); | ||
|
|
||
| assertThat(ranking.volunteerTotalRankingResponse().get(1).totalHours()).isEqualTo(100L); | ||
| assertThat(ranking.volunteerTotalRankingResponse().get(1).ranking()).isEqualTo(1); | ||
|
|
||
| assertThat(ranking.volunteerTotalRankingResponse().get(3).totalHours()).isEqualTo(60L); | ||
| assertThat(ranking.volunteerTotalRankingResponse().get(3).ranking()).isEqualTo(3); | ||
| } | ||
|
|
||
| @DisplayName("주간 봉사 시간으로 4위까지의 랭킹을 반환할 수 있다. 동점자를 같이 반환한다.") | ||
| @Test | ||
| void calculateWeeklyVolunteerRanking() { | ||
|
|
||
| // given | ||
| LocalDate currentDate = LocalDate.now(); | ||
|
|
||
| // 이번 주 데이터 | ||
| volunteerRecordRepository.save(VolunteerRecord.create(UUID.randomUUID(), "이번주봉사1", currentDate, 100)); | ||
| volunteerRecordRepository.save(VolunteerRecord.create(UUID.randomUUID(), "이번주봉사2", currentDate, 100)); | ||
| volunteerRecordRepository.save(VolunteerRecord.create(UUID.randomUUID(), "이번주봉사3", currentDate, 90)); | ||
| volunteerRecordRepository.save(VolunteerRecord.create(UUID.randomUUID(), "이번주봉사4", currentDate, 60)); | ||
| volunteerRecordRepository.save(VolunteerRecord.create(UUID.randomUUID(), "이번주봉사5", currentDate, 50)); | ||
|
|
||
| // 지난 주 데이터 | ||
| volunteerRecordRepository.save(VolunteerRecord.create(UUID.randomUUID(), "지난주봉사", currentDate.minusWeeks(1), 200)); | ||
|
|
||
| // when | ||
| VolunteerRankingResponseDto ranking = calculateRankingService.calculateRanking(); | ||
|
|
||
| // then | ||
| assertThat(ranking.volunteerWeeklyRankingResponse()).hasSize(5); | ||
|
|
||
| assertThat(ranking.volunteerWeeklyRankingResponse().get(0).totalHours()).isEqualTo(100L); | ||
| assertThat(ranking.volunteerWeeklyRankingResponse().get(0).ranking()).isEqualTo(1); | ||
|
|
||
| assertThat(ranking.volunteerWeeklyRankingResponse().get(1).totalHours()).isEqualTo(100L); | ||
| assertThat(ranking.volunteerWeeklyRankingResponse().get(1).ranking()).isEqualTo(1); | ||
|
|
||
| assertThat(ranking.volunteerWeeklyRankingResponse().get(3).totalHours()).isEqualTo(60L); | ||
| assertThat(ranking.volunteerWeeklyRankingResponse().get(3).ranking()).isEqualTo(3); | ||
| } | ||
|
|
||
| @DisplayName("월간 봉사 시간으로 4위까지의 랭킹을 반환할 수 있다. 동점자를 같이 반환한다.") | ||
| @Test | ||
| void calculateMonthlyVolunteerRanking() { | ||
|
|
||
| // given | ||
| LocalDate currentDate = LocalDate.now(); | ||
|
|
||
| // 이번 달 데이터 | ||
| volunteerRecordRepository.save(VolunteerRecord.create(UUID.randomUUID(), "이번달봉사1", currentDate, 100)); | ||
| volunteerRecordRepository.save(VolunteerRecord.create(UUID.randomUUID(), "이번달봉사2", currentDate, 100)); | ||
| volunteerRecordRepository.save(VolunteerRecord.create(UUID.randomUUID(), "이번달봉사3", currentDate, 90)); | ||
| volunteerRecordRepository.save(VolunteerRecord.create(UUID.randomUUID(), "이번달봉사4", currentDate, 60)); | ||
| volunteerRecordRepository.save(VolunteerRecord.create(UUID.randomUUID(), "이번달봉사5", currentDate, 50)); | ||
|
|
||
| // 지난 달 데이터 | ||
| volunteerRecordRepository.save(VolunteerRecord.create(UUID.randomUUID(), "지난달봉사", currentDate.minusMonths(1), 200)); | ||
|
|
||
| // when | ||
| VolunteerRankingResponseDto ranking = calculateRankingService.calculateRanking(); | ||
|
|
||
| // then | ||
| assertThat(ranking.volunteerMonthlyResponse()).hasSize(5); | ||
|
|
||
| assertThat(ranking.volunteerMonthlyResponse().get(0).totalHours()).isEqualTo(100L); | ||
| assertThat(ranking.volunteerMonthlyResponse().get(0).ranking()).isEqualTo(1); | ||
|
|
||
| assertThat(ranking.volunteerMonthlyResponse().get(1).totalHours()).isEqualTo(100L); | ||
| assertThat(ranking.volunteerMonthlyResponse().get(1).ranking()).isEqualTo(1); | ||
|
|
||
| assertThat(ranking.volunteerMonthlyResponse().get(3).totalHours()).isEqualTo(60L); | ||
| assertThat(ranking.volunteerMonthlyResponse().get(3).ranking()).isEqualTo(3); | ||
| } | ||
| } |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
얘만 Ranking이 빠진 거 같은데 혹시 다른 의도가 있으신건가용?
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.
아닙니다! 그냥... 바빴나봐요 찾아주셔서 감사합니다 ㅎㅎ