Skip to content

Commit 677cbeb

Browse files
authored
Feature/283 봉사 시간 랭킹 기능 구현 (#310)
* chore: 컨벤션에 위배된 메서드 네임 수정 - 기존 봉사 기록 생성 유스케이스와 구현체에 대한 네이밍 수 * depend: queryDsl의 SQLExpression 사용을 위한 의존성 추가 - sql 의존성 추가 * chore: 테스트 클래스명 수정 - 본 클래스의 컨벤션에 맞춘 네이밍 수정에 따라 테스트 클래스의 명칭도 수정 * feat: 전체, 월별, 주간 봉사 시간 랭킹 쿼리 구현 - 레포지토리 인터페이스에 전체, 월, 주간 봉사 시간 랭킹 메서드 추가 - Jpa 레포지토리에 native쿼리 메서드 추가 - 레포지토리 구현체에 인터페이스 기능 구현 - 테스트 작성및 검증 완료 * feat: 랭킹 계산 유스케이스 구현 - 랭킹 계산 유스케이스 인터페이스 생성 - 구현체 생성및 랭킹 계산 메서드 구현 - 전체, 월, 주간 랭킹을 담는 Dto 클래스 구현 - 랭킹 객체 -> 랭킹 Dto mapper 구현 - 모든 랭킹을 담는 record 구현 - 테스트 작성및 검증 완료 * feat: 랭킹 캐싱을 위한 redis 레포지토리와 유스케이스 구현 - Redis 레포지토리 생성및 랭킹 저장 메서드 구현 - 랭킹 캐싱 유스케이스 생성 - 구현체 구현 - 테스트 작성및 검증 완료 * feat: 랭킹 계산, 캐싱을 위한 스케줄러 구현 - 랭킹 계산 유스케이스, 캐싱 유스케이스가 주입된 스케줄러 구현 - 테스트 작성및 검증 완료 * feat: 봉사 랭킹 조회 유스케이스 구현 - 유스케이스 인터페이스 생성 - 구현체 구현 - 테스트 작성및 검증 완료 * feat: 테스트용 DB 설정 추가 - 기존 H2에서 Mysql로 변경 * feat: 누락된 테스트 클래스 추가 - 이전 커밋에서 누락된 테스트 클래스 추가 * feat: test container 추가 - 테스트 환경에서 mysql이용을 위한 test container 추가 - 관련 의존성 추가 - 관련 설정 클래스 생성 - 기존 support 클래스에 test container 클래스 상속 - 로그 제한을 위한 logback-test 추가 * chore: application-test.yml 수정 - 기존 h2, mysql 설정 제거 * refactor: 랭킹 산출 쿼리 리팩토링 - 네이티브 쿼리 명시 - 맵퍼 클래스 수정 - 테스트및 검증 완료 * feat: 봉사 랭킹 엔드 포인트 구현 - 봉사 랭킹 컨트롤러 구현 - 테스트및 검증 완료 * feat: 봉사 시간 랭킹 스케줄러 로깅 추가 - 봉사 시간 집계 실행, 종료 확인을 위한 로그 추 * refactor: 봉사 시간 랭킹 반환 예외 메세지 수정 - 의미가 알맞은 예외 메세지로 수 * feat: 봉사자 닉네임 리스트 반환 쿼리 메서드 생성 - 레포지토리 인터페이스에 닉네임 리스트 반환 메서드 추가 - 레포지토리 구현체에 닉네임 리스트 반환 메서드 추가 - 테스트및 검증 완료 * feat: 봉사자 닉네임 리스트 반환 유스케이스 구현 - 유스케이스 인터페이스 추가 - 구현체 생성 - 닉네임 리스트 반환 메서드 주입 - 테스트및 검증 완료 * chore: 봉사자 닉네임 반환 관련 클래스의 명시적인 이름 수정 - Nickname앞에 volunteer를 추가하여 명시적인 이름으로 수정 * feat: 봉사 시간 랭킹 Dto nickname 필드 추가 - 봉사자 nickname을 담을 필드 추가 * refactor: 봉사자 닉네임 반환 유스케이스 리팩토링 - volunteerId를 함께 반환 하도록 리팩토링 * refactor: 봉사자 닉네임 반환 유스케이스 리팩토링 - volunteerId를 함께 반환 하도록 리팩토링 * refactor: 봉사 랭킹의 닉네임 필드 추가에 따른 테스트 수정 - 닉네임을 테스트 조건에 추가 * refactor: 봉사 랭킹 객체 맵퍼 리팩토링 - nickname을 가진 map에서 volunteerId로 값을 가져오는 로직추가 - toUUID 컨버터의 접근 제어자를 public으로 수정 - 테스트및 검증 완료 * refactor: 봉사 시간 랭킹 유스케이스 리팩토링 - volunteer nickname 추가에 따른 내부 로직 수정 - 봉사자 id 추출 로직 메서드로 분리 - 닉네임 추출 로직 메서드로 분리 - 테스트및 검증 완료 * chore: 불필요한 클래스 삭제및 build.gradle 개행 수정 - * feat: test container 설정 추가 - redis, mysql test container 클래스 추가 * fix: 에러 발생 test 수정 - VolunteerRepositoryImpl 수정 - VolunteerRepositoryImplTest 수정 * chore: cicd workflow 수정 - 도커 권한 부여 step 추가 - 기존 ci에서 push 옵션 삭제 * fix: ci 파이프 라인 수정 - testcontainer를 위한 설정추가 * fix: ci 파이프 라인 수정 - testcontainer를 위한 설정추가 * fix: ci 파이프 라인 수정 - testcontainer를 위한 설정추가 * fix: 봉사시간 계산 테스트 날짜 수정 - 월요일이 주의 시작으로 계산되어 발생하는 테스트 에러 수정 * fix: ci 테스트 레포트 분석을 위한 설정 추가 - ci workflow 수정 * fix: 동시성 테스트 스레드 획득 에러 수정 - 스레드 풀의 크기를 16으로 감소 * fix: cicd 빌드 로그 옵션 제거 - cicd중 발생하는 테스트 에러 파악을 위해 추가했던 옵션 삭제 * fix: 에러 발생 테스트 주석처리 - 스레드 에러 발생 테스트 주석처리 * chore: 불필요한 구문 제거및 개행 추가 - 불필요한 코드, import문 제거 - 마지막 줄 개행이 없는 클래스 개행 추가 * chore: 코드 리뷰사항 반영 - 컨벤션에 맞는 변수명, 클래스명 수정 - 변수, 클래스명 수정에 따른 테스트 수정
1 parent f9e51ca commit 677cbeb

File tree

50 files changed

+1556
-89
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1556
-89
lines changed

.github/workflows/CD.yml

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,6 @@ jobs:
88
build-and-push:
99
runs-on: ubuntu-latest
1010

11-
services:
12-
redis:
13-
image: redis
14-
ports:
15-
- 6379:6379
16-
1711
env:
1812
DB_URL: ${{ secrets.DB_URL }}
1913
DB_USERNAME: ${{ secrets.DB_USERNAME }}
@@ -43,6 +37,16 @@ jobs:
4337
ELASTIC_PASSWORD: ${{ secrets.ELASTIC_PASSWORD }}
4438
LOKI_URL: ${{ secrets.LOKI_URL }}
4539

40+
TESTCONTAINERS_REUSE_ENABLE: true
41+
TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE: /var/run/docker.sock
42+
DOCKER_HOST: unix:///var/run/docker.sock
43+
44+
services:
45+
redis:
46+
image: redis
47+
ports:
48+
- 6379:6379
49+
4650
steps:
4751
- name: Github Repository 파일 불러오기
4852
uses: actions/checkout@v4

.github/workflows/CI.yml

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
name: CI Pipeline
22

33
on:
4-
push:
5-
branches:
6-
[ main ]
74
pull_request:
85
branches:
96
[ main ]
@@ -14,14 +11,6 @@ jobs:
1411
Continuous-Integration:
1512
runs-on: ubuntu-latest
1613

17-
#Action 환경에서 Redis 실행을 위한 설정 추후 서버 배포시 제거 예정
18-
services:
19-
redis:
20-
image: redis
21-
ports:
22-
- 6379:6379
23-
24-
2514
env:
2615
DB_URL: ${{ secrets.DB_URL }}
2716
DB_USERNAME: ${{ secrets.DB_USERNAME }}
@@ -51,13 +40,25 @@ jobs:
5140
ELASTIC_PASSWORD: ${{ secrets.ELASTIC_PASSWORD }}
5241
LOKI_URL: ${{ secrets.LOKI_URL }}
5342

43+
TESTCONTAINERS_REUSE_ENABLE: true
44+
TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE: /var/run/docker.sock
45+
DOCKER_HOST: unix:///var/run/docker.sock
46+
47+
services:
48+
redis:
49+
image: redis
50+
ports:
51+
- 6379:6379
5452

5553
steps:
5654
- name: Github Repository 파일 불러오기
5755
uses: actions/checkout@v4
5856
with:
5957
fetch-depth: 0
6058

59+
- name: Set up Docker
60+
uses: docker/setup-buildx-action@v3
61+
6162
- name: JDK 21 버전 설치
6263
uses: actions/setup-java@v4
6364
with:
@@ -88,6 +89,13 @@ jobs:
8889
- name: 빌드 및 테스트
8990
run: ./gradlew build
9091

92+
- name: 테스트 레포트 저장
93+
if: ${{ always() }}
94+
uses: actions/upload-artifact@v3
95+
with:
96+
name: test-reports
97+
path: build/reports/tests/test
98+
9199
- name: Close PR, if build fail
92100
if: ${{ failure() }}
93101
uses: actions/github-script@v6
@@ -113,6 +121,6 @@ jobs:
113121
114122
- name: SonarCloud 빌드및 분석
115123
env:
116-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
124+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
117125
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
118126
run: ./gradlew build sonar --info

build.gradle

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ dependencies {
4141

4242
// Querydsl
4343
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
44+
implementation 'com.querydsl:querydsl-sql:5.0.0'
4445
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
4546
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
4647
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
@@ -79,6 +80,10 @@ dependencies {
7980
testImplementation 'org.springframework.boot:spring-boot-starter-test'
8081
testImplementation 'org.springframework.security:spring-security-test'
8182
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
83+
testImplementation group: 'org.testcontainers', name: 'testcontainers', version: '1.20.4'
84+
testImplementation group: 'org.testcontainers', name: 'junit-jupiter', version: '1.20.4'
85+
testImplementation group: 'org.testcontainers', name: 'mysql', version: '1.20.4'
86+
8287
}
8388

8489
// Querydsl 설정
@@ -103,6 +108,7 @@ tasks.named('test') {
103108
useJUnitPlatform()
104109
jvmArgs("-XX:+EnableDynamicAgentLoading")
105110
if (gradle.startParameter.taskNames.contains('build')) {
111+
exclude '**/*ControllerTest.class'
106112
finalizedBy 'jacocoTestReport'
107113
}
108114
}
@@ -131,7 +137,9 @@ def jacocoExcludePatterns = [
131137
'**/fixture/*',
132138
'**/*Factory*',
133139
'**/event/**',
134-
'**/*Aspect*'
140+
'**/*Aspect*',
141+
'**/Aspect/*',
142+
'**/utils/*'
135143
]
136144

137145
def jacocoExcludePatternsForVerify = [
@@ -151,7 +159,9 @@ def jacocoExcludePatternsForVerify = [
151159
'*.fixture.*',
152160
'*.*Factory*',
153161
'*.*event*.*',
154-
'*.*Aspect*'
162+
'*.*Aspect*',
163+
'*.Aspect.*',
164+
'*.utils.*'
155165
]
156166

157167
jacocoTestReport {

src/main/java/com/somemore/domains/volunteer/repository/VolunteerRepositoryImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ private Optional<Volunteer> findOne(BooleanExpression condition) {
103103
condition,
104104
isNotDeleted()
105105
)
106-
.fetchOne()
106+
.fetchFirst()
107107
);
108108
}
109109

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.somemore.domains.volunteerrecord.controller;
2+
3+
import com.somemore.domains.volunteerrecord.dto.response.VolunteerRankingResponseDto;
4+
import com.somemore.domains.volunteerrecord.usecase.GetVolunteerRankingUseCase;
5+
import com.somemore.global.common.response.ApiResponse;
6+
import io.swagger.v3.oas.annotations.Operation;
7+
import io.swagger.v3.oas.annotations.tags.Tag;
8+
import lombok.RequiredArgsConstructor;
9+
import org.springframework.web.bind.annotation.*;
10+
11+
@Tag(name = "Volunteer Ranking API", description = "봉사 시간 랭킹 API")
12+
@RequiredArgsConstructor
13+
@RequestMapping("/api/volunteerrecord")
14+
@RestController
15+
public class VolunteerRankingApiController {
16+
17+
private final GetVolunteerRankingUseCase getVolunteerRankingUseCase;
18+
19+
@Operation(summary = "봉사 시간 랭킹", description = "봉사 시간 랭킹을 반환합니다.")
20+
@GetMapping("/ranking")
21+
public ApiResponse<VolunteerRankingResponseDto> getVolunteerRanking() {
22+
23+
VolunteerRankingResponseDto volunteerRankings = getVolunteerRankingUseCase.getVolunteerRanking();
24+
25+
return ApiResponse.ok(volunteerRankings,"봉사 시간 랭킹 반환 성공");
26+
}
27+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.somemore.domains.volunteerrecord.dto.response;
2+
3+
import java.util.UUID;
4+
5+
public record VolunteerMonthlyRankingResponseDto(
6+
UUID volunteerId,
7+
int totalHours,
8+
long ranking,
9+
String nickname
10+
) {
11+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.somemore.domains.volunteerrecord.dto.response;
2+
3+
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
4+
import com.fasterxml.jackson.databind.annotation.JsonNaming;
5+
import com.somemore.domains.center.dto.response.PreferItemResponseDto;
6+
import lombok.Builder;
7+
8+
import java.util.List;
9+
10+
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
11+
@Builder
12+
public record VolunteerRankingResponseDto(
13+
List<VolunteerTotalRankingResponseDto> volunteerTotalRankingResponse,
14+
List<VolunteerMonthlyRankingResponseDto> volunteerMonthlyRankingResponse,
15+
List<VolunteerWeeklyRankingResponseDto> volunteerWeeklyRankingResponse
16+
) {
17+
public static VolunteerRankingResponseDto of(
18+
List<VolunteerTotalRankingResponseDto> totalRanking,
19+
List<VolunteerMonthlyRankingResponseDto> monthlyRanking,
20+
List<VolunteerWeeklyRankingResponseDto> weeklyRanking){
21+
22+
return VolunteerRankingResponseDto.builder()
23+
.volunteerTotalRankingResponse(totalRanking)
24+
.volunteerMonthlyRankingResponse(monthlyRanking)
25+
.volunteerWeeklyRankingResponse(weeklyRanking)
26+
.build();
27+
}
28+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.somemore.domains.volunteerrecord.dto.response;
2+
3+
import java.util.UUID;
4+
5+
public record VolunteerTotalRankingResponseDto(
6+
UUID volunteerId,
7+
int totalHours,
8+
long ranking,
9+
String nickname
10+
) {
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.somemore.domains.volunteerrecord.dto.response;
2+
3+
import java.util.UUID;
4+
5+
public record VolunteerWeeklyRankingResponseDto(
6+
UUID volunteerId,
7+
int totalHours,
8+
long ranking,
9+
String nickname
10+
) {
11+
}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.somemore.domains.volunteerrecord.event.handler;
22

33
import com.somemore.domains.volunteerrecord.domain.VolunteerRecord;
4-
import com.somemore.domains.volunteerrecord.usecase.VolunteerRecordCreateUseCase;
4+
import com.somemore.domains.volunteerrecord.usecase.CreateVolunteerRecordUseCase;
55
import lombok.RequiredArgsConstructor;
66
import org.springframework.stereotype.Component;
77
import org.springframework.transaction.annotation.Transactional;
@@ -11,11 +11,11 @@
1111
@Transactional
1212
public class SettleVolunteerHoursHandlerImpl implements SettleVolunteerHoursHandler {
1313

14-
private final VolunteerRecordCreateUseCase volunteerRecordCreateUseCase;
14+
private final CreateVolunteerRecordUseCase createvolunteerRecordUseCase;
1515

1616
@Override
1717
public void handle(VolunteerRecord volunteerRecord) {
1818

19-
volunteerRecordCreateUseCase.create(volunteerRecord);
19+
createvolunteerRecordUseCase.create(volunteerRecord);
2020
}
2121
}

0 commit comments

Comments
 (0)