Skip to content

Commit 9a130fb

Browse files
rlajm1203LJH098westzerorightinjae-348
authored
release 4.2.0 (#371)
* [feat]: 면접관 조회 기능 ROLE에 따른 필터링 추가 * [style]: spotless * [refactor]: 중복 임포트문 삭제 * feat: 배포 자동화 셸 스크립트 작성 * feat: 배포 자동화 셸 스크립트 적용 * feat: 가장 최신 내용의 브랜치로 이동 * feat: nginx proxy 컨테이너 추가 * chore: gitignore에 .pem 추가 * feat: redis config 추가 * refactor: git 디렉터리 에러 해결 * chore: gitignore 에 .key 추가 * refactor: 실행 권한 설정 명령 추가 * rename: 레디스 설정 파일 이름 변경 * refactor: 잘못된 파일 이름 변경 * refactor: 디스코드 발송 에러 해결 * refactor: 디스코드 발송 스크립트 수정 * refactor: 디스코드 발송 스크립트 수정 - env 가 아닌 run 에서 바디 정의 * refactor: discord 알림 발송 워크 플로우 확정 - 디스코드 환경 변수 설정 및 알림 보내기 step 추가 * refactor: discord 알림 발송 워크 플로우 확정 (prod) - 디스코드 환경 변수 설정 및 알림 보내기 step 추가 * refactor: 디스코드 알림 환경 변수 step 제거 * refactor: 디스코드 알림 환경 변수 step 제거 (Prod) * feat: github action 테스트 스크립트 작성 * refactor: 파일 인코딩 변경 * chore: 테스트 스크립트 삭제 * refactor: 필요 없는 주석 삭제 * refactor: 투명 블록 제거 * [chore]: spotless plugin version 6.11.0 -> 6.22.0 변경 * [feat]: 지원 완료 html 템플릿 * [refactor]: html 생성에 템플릿 엔진으로 타임리프 적용 * refactor: 지원서의 합/불 상태 리스트 조회 API에 description 추가 * refactor: PassStateToEnum 은 Response 에 포함되지 않도록 수정 * refactor: ApplicantStateResponse 정의 - Applicant의 state를 전달하는 응답 DTO * fix: backup 디렉터리가 존재하지 않을 경우, 디렉터리를 생성하도록 수정 * refactor: backup 실패 시, printStackTrace 출력하지 않도록 수정 * feat: 사용하지 않는 도커 볼륨 삭제 스크립트 추가 * refactor: 서류 지원 확인 이메일 템플릿 명 변경 * feat: email-first-passed-email 템플릿 추가 * feat: template -> templates * feat: email-first-passed-email -> email-first-passed * chore: api -> infra 모듈로 이동 * style: inline css 적용 * refactor: 멘트 수정 * feat: 최종합격자 이메일 템플릿 추가 * feat: 서류 불합격자 메일 템플릿 추가 * feat: 최종 불합격자 메일 템플릿 추가 * refactor: 최종 합격자 메일 변수 수정 * feat: 합격 상태 별, 이메일 템플릿 추가 * refactor: switch case 문에, break 추가 * feat: DefaultTemplate 클래스 정의 - 기본 이메일 템플릿 * feat: ApplicantEmailService 클래스 작성 - applicant에게 이메일을 전송하는 역할 * refactor: FinalEmailDiscussionEmailScheduler 로직 수정 - 이메일 전송 성공 시 Slack 알림 기능 추가 - ApplicantEmailService 클래스 활용 * refactor: 패키지 이동 및 클래스명 변경 - Recruit-API -> Recruit-Domain - DefaultTemplate -> DefaultEmailTemplate * refactor: EmailTemplateType 수정 - PassSates와 매핑 * refactor: EnumType 이름 변경 * refactor: email 템플릿 infrastructure 모듈에서 domain 모듈로 이전 - 기본 템플릿 * refactor: application.yml 수정 - recruit period 기본값 29기에 맞춰서 수정 - default email template에 필요한 정보 추가 * feat: DefaultEmailProperties 클래스 정의 - DefaultEmail에 들어갈 정보 * feat: DefaultEmailTemplateGenerator 추가 - util로 사용할, 기본 이메일 템플릿 생성 클래스 - 템플릿에 들어갈 내용을, 지원자의 정보에 맞게 생성해준다. * feat: 포트폴리어 파일 경로 추가 * refactor: DefaultEmailGenerator 사용 * comment: 주석 내용 수정 * refactor: switch-case에 걸리지 않을 경우 그대로 리턴 * refactor: DefaultEmailProperties 수정 - LocalDateTime 파싱 로직 추가 * refactor: 시간 정보 text로 변경 - datetime format 사용 * refactor: temporals.format 으로 변경 * feat: Ncp Sms 관련 설정 추가 - sms api url - openfeign에 sms 요청 메소드 추가 * rename: Response -> NcpResponse 클래스 명 변경 * feat: NcpSmsResponse 정의 * feat: NcpSmsResponse 적용 * feat: NcpSmsSender 정의 - NaverCloudPlatform 을 사용한 sms 전송 클래스 * feat: SmsMessageGenerator 정의 * feat: ApplicantSmsService 구현 - 해당 지원자에게 문자 발송 * refactor: EmailSendEvent 수정 - applicantId 필드 추가 * feat: EmailSendEventHandler 정의 - 이메일 발송 완료 이벤트 처리 * feat: sendSms(applicantId) 메소드 추가 * refactor: record @Getter 제거 * feat: NcpClients 클래스 정의 - Ncp 의 여러 클라이언트 관리 * delete: NcpClient 삭제 * refactor: 패키지 명 변경 및, NcpClients 적용 * rename: NcpSmsSender -> NcpSmsHelper * refactor: ncp api url 분리 - base url + uri 로 분리 * refactor: 패키지 이동에 따른 수정 * refactor: 패키지 이동 * refactor: 휴대폰 번호 추출 키 phone -> contacted * refactor: FeignClient 에러 발생 시, Response 값 String 으로 볼 수 있도록 변경 * refactor: charset 변경 * refactor: Message 객체에 Getter 추가 * refactor: NcpSmsClient return type change * refactor: add default constructor * feat: add fromPhoneNumber * feat: 메일 전송 시, 메일 전송 이벤트 발행 * feat: EmailTemplateInvalidStateException 정의 - 해당 State에 맞는 이메일 템플릿이 없을 경우 발생하는 예외 * refactor: 상태에 맞지 않는 템플릿일 경우 던지는 예외 변경 * refactor: retry 횟수 3회로 변경 및 에러 로그 수정 * feat: 최종 합격 공지 대상 필터링 메소드 추가 - FINAL_PASSED , FINAL_FAILED * refactor: 최종 합격자 대상 ot 시간 형식 수정 * feat: 이메일 API 컨트롤러 추가 * style: spotlessApply * fix: Redisson Lock 해제 조건 추가 - 현재 스레드가 락 소유 - 락 상태 on * style: spotlessApply * comment: 주석 추가 * refactor: 메일 발송 성공 시 이벤트 발행 로직 제거 * feat: SecurityConfig에 email 관련 api 추가 * feat: 이벤트 발행 로그 다시 추가 * feat: email api 추가 * feat: sendEmail(year, state) 메소드 추가 * feat: applicantId로 MongoAnswer 찾는 메소드 추가 * feat: applicantId로 지원자 한 명 에게 메일 발송 API 추가 * refactor: 님! 추가 * style: spotlessApply * style: spotlessApply * docs: 포트폴리오 새로운 버전으로 변경 * refactor: 구글폼 링크 추가 * refactor: ot url 설정 추가 * fix: 오타 수정 * fix: orientation url key 수정 * refactor: 불합격자 멘트 수정 * fix: 오타 수정 * refactor: 문자 기본 멘트 변경 * refactor: 띄어쓰기 추가 * [BE-122] 이메일 발송 이벤트 처리를 수정합니다. (#336) * refactor: Transactional 삭제 * refactor: TransactionalEventListener -> EventListener 로 변경 * [feat]: 레디스에 저장할 액세스토큰 엔티티 * [feat]: 화이트리스트 레포지토리 및 레디스 설정 - 해당 레디스 설정을 통해 토큰 삭제 시 Set 모두 삭제됩니다. * [feat]: 매 요청 접속 시 화이트리스트에 액세스 토큰이 있는지 검사 * [feat]: 로그인 시 액세스 토큰을 저장하고 로그아웃 시 삭제 * [refactor]: 기존에 있는 InvalideTokenException을 사용 * [refactor]: 필터에서 레포지토리보다는 포트를 주입받아서 사용 * [feat]: 간소화된 면접 기록 응답 DTO 추가 - SimpleRecordViewResponseDto: 간소화된 면접 기록 DTO - SimpleRecordsViewResponseDto: 페이지 정보 및 간소화된 면접 기록 리스트 반환 DTO * [feat]: 간단한 면접 목록 조회를 위한 executeSimple 메서드 추가 - SimpleRecordsViewResponseDto를 반환하는 executeSimple 메서드 추가 * [feat]: RecordService에 executeSimple 구현 * [feat]: 간소화된 면접 기록 목록 조회 엔드포인트 추가 - 지원자의 면접기록 목록을 페이지 몇 기수별로 조회하는 엔드포인트 추가 - /api/v1/page/{page}/year/{year}/records * [feat]: Record, Applicant, 점수, 페이지 정보를 담는 FilteredRecordsApplicantsDto 추가 * [refactor]: RecordsService의 공통 로직을 private 메서드로 분리 - execute 와 executeSimple 메서드에서 공통되는 부분 private 메서드로 분리 * [feat]: 비밀번호 재설정 기능 구현 * [style]: spotless * [feat]: 이메일로 인증코드 발송 구현 * [feat]: 이메일로 인증코드 발송 구현 * [feat]: 비밀번호 재설정 시 인증 완료 기록 없으면 예외 발생 * [style]: spotless * [chore]: Graphql 의존성 추가 - sprint boot, 확장 스칼라, 테스트 의존성 추가 * [chore]: GraphQL 스키마 초기화 설정 및 로깅 설정 추가 * [feat]: 요청/응답 캐싱 필터에 GraphQL 제외 전략 적용 * [feat]: Method Level 인가 활성화 및 GraphQL 경로 permitAll 설정 - @EnableMethodSecurity 추가로 @PreAuthorize를 통해 Resolver 단에서 인가 적용 - GraphQL 경로는 FilterChain을 통과하되 인증 없이 접근 허용 PreAuthorize를 통해 각 쿼리 별 접근 제한 설정 * [feat]: GraphQL JSON 스칼라 등록 및 Jackson 메시지 컨버터 설정 * [feat]: GraphQL을 통한 필터링된 목록 및 단일 조회 기능 구현 * [feat]: GraphQLExceptionHandler 추가 - RecruitCodeException Handling - 이외의 오류는 Internal Server Error로 처리 * [style]: spotless * [BE-113] 칸반 보드 Columns 조회 시 QueryString으로 year 정보를 받는 V2 API 추가, 지원서 칸반보드 열 생성 V2 API 추가 (#298) * Revert "[BE-113] 칸반 보드 Columns 조회 시 QueryString으로 year 정보를 받는 V2 API 추가, 지원서 …" (#348) This reverts commit 69589ed. * [feat]: 비밀번호 재설정을 위한 이메일 인증 시 존재하지 않는 면접관이면 예외 처리 * [feat]: 회원가입 시 이메일 인증 기능 추가 * [feat]: 시큐리티 경로 추가 * [feat]: 회원가입 시 이메일 인증이 완료되지 않았으면 예외 처리 * [style]: spotless * [refactor]: GraphQL 에외 처리 빌더 메서드를 통한 중복 코드 제거 * [style]: spotless * [refactor]: Graphql 경로 /graphql 에서 /api/graphql로 변경 * [refactor]: Optional 객체에 ifPresentOrElse 적용 * [feat]: 비밀번호 재설정 및 회원가입 완료 시 코드 인증 완료 데이터 삭제 * [refactor]: UserService에 ifPresentOrElse 적용 * [BE-130] 리크루트 모집 on/off 기능 구현 (#352) * feat: application start 엔드포인트 추가 및 usecase 정의 * delete: 클래스 이름 변경에 따른 삭제 * feat: command, dto, states 클래스 정의 * feat: RecruitmentManagementUseCase 구현 및 정의 - 파일 이름 변경 * feat: Recruitment 상태 변경 엔드포인트 추가 * refactor: swagger 설명 수정 * refactor: recruitment 도메인으로 이전 * feat: Recruitment entity, repository, port, adaptor 정의 및 구현 * move: 패키지 이동 * delete: 클래스 삭제 * feat: recruitment 제어에 필요한 클래스 정의 * refactor: RecruitmentDto 및 구조 변경 - year, startAt, endAt 추가 및 state 제거 * refactor: Enum 상태 이름 변경 - NON_START, RECRUITING, END * refactor: Enum 필드 추가 * refactor: 메소드 이름 변경 - SetUp * feat: 이미 예약된 작업이 존재하는지 확인하는 메소드 추가 * feat: RecruitmentEnd, RecruitmentStart 이벤트 정의 * refactor: DomainEvent 상속 * feat: RecruitmentScheduler 정의 - start, end 작업 예약 스케줄러 * feat: findById 메소드 정의 * feat: 생성된 Recruitment 작업 예약 구현 - RecruitmentScheduler 정의 - Recruitment update 메소드 추가 - RecruitmentEventHandler 추가 * feat: Recruitment 도메인 관련 예외 정의 - Exception 클래스 정의 - ErrorCode 정의 * refactor: RecruitmentException 적용 * feat: SchedulingConfig 정의 * refactor: 이벤트 핸들러 로깅 추가 * feat: LatestRecruitmentVo 정의 - 가장 최신 Recruitment을 나타내는 전역 VO * feat: RecruitmentRegister 이벤트, 핸들러 구현 * refactor: RecruitmentRegister 이벤트 적용 * comment: 주석 처리 * rename: 이벤트 이름 재정의 * comment: TODO 코멘트 추가 * delete: 필요 없는 클래스 삭제 * refactor: Event 발행 제거, Task로 직접 처리 * refactor: 필요 없는 메소드 삭제 * feat: findByStates 메소드 추가 * feat: 애플리케이션이 종료되었을 경우, DB에서 수행할 작업을 불러오는 로직 추가 * style: spotlessApply * refactor: findLatestOne 쿼리 수정 * refactor: RecruitmentSetUpDto Validation 추가 * refactor: RecruitmentUseCase setUp 메소드 시그니처 변경 및 command 삭제 * feat: Recruitment 삭제 API 추가 및 구현 * feat: Recruitment 캔슬 로직 추가 * refactor: API 명세 수정 - RecruitmentResponseDto, RecruitmentResponseDto 추가 * refactor: AllArgsConstructor 추가 * refactor: 응답 dto 수정 * feat: quartz 스케줄러 의존성 추가 * feat: getState 메소드 추가 * feat: RecruitmentJob, RecruitmentTrigger 정의 * feat: delete 메소드 추가 및 구현 * feat: QuartzException 정이 - Quartz 내부에서 발생한 예외를 감싸는 예외 * refactor: Spring Scheduler -> Quartz 로 변경 및 모집 삭제 로직 변경 * refactor: RecruitmentSetUpDto 필드 milliSecond 단위로 수정 * refactor: RecruitmentSetUpDto Validation 해제 및 RecruitmentException 추가 * move: 패키지 이동 * refactor: 스프링에서 제공하는 scheduler 사용 * refactor: 상태 변경 메소드 @transactional 추가 * refactor: jobKey 생성 메소드 추가 * refactor: recruitmentJobKey 적용 * refactor: LatestRecruitVo를 사용하도록 변경 * refactor: ApplicantValidator year 필드 삭제 * refactor: Recruitment year 필드 타입 변경 - Long -> Integer * refactor: Recruitment year 필드 타입 변경 - Long -> Integer * refactor: LatestRecruitmentVo 빈 등록 메커니즘 수정 * refactor: Recruitment 생성 검증 로직 수정 * refactor: LatestRecruitmentVo 유효성 검증 로직 수정 * feat: quartz-init.sql 추가 및 quartz 테이블 설정 추가 * refactor: LatestRecruitmentVo 분산락 수정 * refactor: LatestRecruitmentVo validate 수정 * refactor: Recruitment 이벤트 핸들러 로직 수정 - 최신 모집 상태 최신화 로직 추가 * feat: Recruitment 조회 기능 구현 * refactor: swagger 명세 수정 * refactor: swagger 명세 수정 (마크다운 문법 일부 적용) * refactor: 모집 생성 시큐리티 필터체인 적용 및 스웨거 설명 추가 * style: spotlessApply * delete: 필요없는 설정파일 삭제 * refactor: year 환경변수 사용 해제 * refactor: year 환경변수 사용 해제 2 * refactor: year 환경변수 사용 해제 3 * refactor: econovation.recruit.period.start / end 환경변수 삭제 * [BE-133] 등록된 모집이 존재하지 않을경우, 유효성 검증 시 예외가 발생하는 오류 수정 (#353) * fix: 아무런 Recruitment 가 없을 경우, IOBException이 발생하는 에러 수정 * refactor: root log level 수정 * [BE-133] 생성된 모집 조회 페이지네이션 오류를 수정합니다. (#354) * fix: 생성된 모집 조회 페이지네이션 수정 * [BE-113] 칸반 보드 Columns 조회 시 QueryString으로 year 정보를 받는 V2 API 추가, 지원서 칸반보드 열 생성 V2 API 추가 (#357) * [BE-136] 가장 최신 Recruitment 조회 API 추가 및 PageSize 동적 설정 (#360) * style: spotlessApply * refactor: pageSize 동적으로 설정할 수 있도록 수정 * feat: 가장 최근 모집 조회 API 추가 * style: spotlessApply * refactor: swagger 설명 추가 * [BE-137] 모집 정보 조회 필드 추가 (#362) * refactor: year 필드 추가 * style: spotlessApply * [BE-138] Recruitment 등록 시 Columns 을 자동으로 생성합니다. (#364) * refactor: year 환경 변수 제거 * feat: static 생성 함수 추가 * feat: static create 메소드 추가 * feat: ColumnsUtil 정의 - Columns에 관련된 유틸 클래스 * feat: 이벤트 핸들러에, 새로운 모집 등록 시 개발자,디자이너,기획자 컬럼 자동 생성 로직 추가 * test: ColumnsUtil 테스트 코드 작성 * style: spotlessApply * feat: existsColumnsByTitle 메소드 추가 * feat: 이미 컬럼이 존재할 경우 생성하지 않도록 수정 * feat: util 함수 추가 * style: spotlessApply * comment: 주석 추가 * refactor: JPQL 수정 * refactor: Recruitment 등록 시, 컬럼 생성 조건 수정 * style" spotlessApply * refactor: 로그 포맷 수정 * refactor: 비동기 이벤트 핸들러로 수정 * style: spotlessApply * refactor: 시스템 전체 로그 레벨 환경 변수로 변경 (#366) * [BE-139] AsyncConfig 추가 및 비동기 이벤트 핸들 설정 (#367) * refactor: 시스템 전체 로그 레벨 환경 변수로 변경 * refactor: AsyncConfig 추가 및 비동기 이벤트 핸들러 설정 수정 * style: spotlessApply * [BE-139] Quartz 로깅 설정 추가 (#368) * refactor: Quartz 로깅 설정 추가 * [BE-139] RecruitmentRegisteredEventHandler @async 제거 (#369) * refactor: RecruitmentScheduler 예외 로깅 추가 * refactor: Async 사용하지 않도록 수정 * style: spotlessApply --------- Co-authored-by: Jin Hyuk <ekrrdj07@gmail.com> Co-authored-by: westzeroright <kt8267@naver.com> Co-authored-by: injae-348 <ant0593@naver.com> Co-authored-by: JeongInJae <93825184+injae-348@users.noreply.github.com> Co-authored-by: westzeroright <124443419+westzeroright@users.noreply.github.com>
1 parent abddc5d commit 9a130fb

File tree

105 files changed

+2612
-152
lines changed

Some content is hidden

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

105 files changed

+2612
-152
lines changed

server/Recruit-Api/build.gradle

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ dependencies {
2929
implementation 'org.springframework.boot:spring-boot-starter-security'
3030
testImplementation 'org.springframework.security:spring-security-test'
3131

32+
// GraphQL
33+
implementation 'org.springframework.boot:spring-boot-starter-graphql'
34+
implementation 'com.graphql-java:graphql-java-extended-scalars:19.1'
35+
testImplementation 'org.springframework.graphql:spring-graphql-test'
36+
37+
3238
// websocket client
3339
implementation 'org.webjars:webjars-locator-core'
3440
implementation 'org.webjars:sockjs-client:1.0.2'
@@ -49,6 +55,9 @@ dependencies {
4955
implementation project(':Recruit-Domain')
5056
implementation project(':Recruit-Infrastructure')
5157

58+
// quartz-scheduler
59+
implementation("org.springframework.boot:spring-boot-starter-quartz:3.5.3")
60+
5261
testRuntimeOnly 'com.h2database:h2'
5362
}
5463

server/Recruit-Api/src/main/java/com/econovation/recruit/api/applicant/controller/ApplicantController.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import com.econovation.recruit.api.applicant.usecase.TimeTableLoadUseCase;
1313
import com.econovation.recruit.api.applicant.usecase.TimeTableRegisterUseCase;
1414
import com.econovation.recruit.api.applicant.validate.ApplicantValidator;
15+
import com.econovation.recruit.api.recruitment.usecase.RecruitmentUseCase;
16+
import com.econovation.recruit.api.recruitment.util.LatestRecruitmentVo;
1517
import com.econovation.recruitcommon.annotation.ApiErrorExceptionsExample;
1618
import com.econovation.recruitcommon.annotation.TimeTrace;
1719
import com.econovation.recruitcommon.annotation.XssProtected;
@@ -30,7 +32,6 @@
3032
import lombok.extern.slf4j.Slf4j;
3133
import org.axonframework.commandhandling.gateway.CommandGateway;
3234
import org.springdoc.api.annotations.ParameterObject;
33-
import org.springframework.beans.factory.annotation.Value;
3435
import org.springframework.http.HttpStatus;
3536
import org.springframework.http.ResponseEntity;
3637
import org.springframework.web.bind.annotation.*;
@@ -49,16 +50,16 @@ public class ApplicantController {
4950
private final CommandGateway commandGateway;
5051
private final ApplicantValidator applicantValidator;
5152
private final ApplicantCommandUseCase applicantCommandUseCase;
52-
53-
@Value("${econovation.year}")
54-
private Integer year;
53+
private final RecruitmentUseCase applicationManagementUseCase;
54+
private final LatestRecruitmentVo latestRecruitInfo;
5555

5656
@Operation(summary = "지원자가 지원서를 작성합니다.", description = "반환 값은 생성된 지원자의 ID입니다.")
5757
@ApiErrorExceptionsExample(CreateApplicantExceptionDocs.class)
5858
@XssProtected
5959
@PostMapping("/applicants")
6060
@TimeTrace
6161
public ResponseEntity registerMongoApplicant(@RequestBody Map<String, Object> qna) {
62+
int year = latestRecruitInfo.getYear();
6263
applicantValidator.validateRegisterApplicant(qna);
6364
String applicantId = UUID.randomUUID().toString();
6465
commandGateway.send(new CreateAnswerCommand(applicantId, year, qna));
@@ -143,8 +144,9 @@ public ResponseEntity<Map<Integer, List<String>>> getApplicantsByTimeTable() {
143144
@TimeTrace
144145
@PostMapping("/applicants/mail")
145146
public ResponseEntity sendEmail(@RequestBody EmailSendDto emailSendDto) {
147+
int year = latestRecruitInfo.getYear();
146148
commonsEmailSender.send(
147-
emailSendDto.getEmail(), emailSendDto.getApplicantId(), LocalDateTime.now());
149+
emailSendDto.getEmail(), emailSendDto.getApplicantId(), year, LocalDateTime.now());
148150
return new ResponseEntity<>(HttpStatus.OK);
149151
}
150152

@@ -162,11 +164,11 @@ public ResponseEntity<Map<String, String>> updateStatus(
162164

163165
@Operation(
164166
summary = "지원서의 합/불 상태를 조회합니다. (합/불 관리자 페이지 전용)",
165-
description = """
166-
응답으로 오는 passState 값의 종류는 non-processed, non-passed, first-passed, final-passed 입니다.
167+
description =
167168
"""
168-
)
169-
@GetMapping("year/{year}/applicants/pass-state")
169+
응답으로 오는 passState 값의 종류는 non-processed, non-passed, first-passed, final-passed 입니다.
170+
""")
171+
@GetMapping("/year/{year}/applicants/pass-state")
170172
public ResponseEntity<List<GetApplicantsStatusResponse>> getApplicantsStatus(
171173
@PathVariable("year") Integer year, @RequestParam("order") String sortType) {
172174
List<GetApplicantsStatusResponse> result =

server/Recruit-Api/src/main/java/com/econovation/recruit/api/applicant/handler/ApplicantRegisterEventConfirmEmailHandler.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.econovation.recruit.api.applicant.handler;
22

3+
import com.econovation.recruit.api.recruitment.util.LatestRecruitmentVo;
34
import com.econovation.recruitdomain.domains.applicant.event.domainevent.ApplicantRegisterEvent;
45
import com.econovation.recruitinfrastructure.apache.CommonsEmailSender;
56
import java.time.LocalDateTime;
@@ -23,9 +24,7 @@
2324
@Slf4j
2425
public class ApplicantRegisterEventConfirmEmailHandler {
2526
private final CommonsEmailSender commonsEmailSender;
26-
27-
@Value("${econovation.year}")
28-
private Integer year;
27+
private final LatestRecruitmentVo latestRecruitInfo;
2928

3029
@Value("${econovation.recruit.period.passedDate}")
3130
private String confirmRegisterEmail;
@@ -36,8 +35,12 @@ public class ApplicantRegisterEventConfirmEmailHandler {
3635
phase = TransactionPhase.AFTER_COMMIT)
3736
@Transactional(propagation = Propagation.REQUIRES_NEW)
3837
public void handle(ApplicantRegisterEvent applicantRegistEvent) {
38+
int year = latestRecruitInfo.getYear();
3939
LocalDateTime passedDate = LocalDateTime.parse(confirmRegisterEmail);
4040
commonsEmailSender.send(
41-
applicantRegistEvent.getEmail(), applicantRegistEvent.getApplicantId(), passedDate);
41+
applicantRegistEvent.getEmail(),
42+
applicantRegistEvent.getApplicantId(),
43+
year,
44+
passedDate);
4245
}
4346
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.econovation.recruit.api.applicant.resolver;
2+
3+
import com.econovation.recruit.api.applicant.dto.AnswersResponseDto;
4+
import com.econovation.recruit.api.applicant.usecase.ApplicantQueryUseCase;
5+
import java.util.List;
6+
import java.util.Map;
7+
import lombok.RequiredArgsConstructor;
8+
import org.springframework.graphql.data.method.annotation.Argument;
9+
import org.springframework.graphql.data.method.annotation.QueryMapping;
10+
import org.springframework.security.access.prepost.PreAuthorize;
11+
import org.springframework.stereotype.Controller;
12+
13+
@Controller
14+
@RequiredArgsConstructor
15+
public class ApplicantResolver {
16+
17+
private final ApplicantQueryUseCase applicantQueryUseCase;
18+
19+
@QueryMapping
20+
@PreAuthorize("hasAnyRole('ROLE_ROLE_PRESIDENT', 'ROLE_ROLE_OPERATION', 'ROLE_ROLE_TF')")
21+
public AnswersResponseDto getApplicants(
22+
@Argument Integer year,
23+
@Argument Integer page,
24+
@Argument String order,
25+
@Argument String searchKeyword,
26+
@Argument List<String> requestedQnaFields) {
27+
return applicantQueryUseCase.executeFiltered(
28+
year, page, order, searchKeyword, requestedQnaFields);
29+
}
30+
31+
@QueryMapping
32+
@PreAuthorize("hasAnyRole('ROLE_ROLE_PRESIDENT', 'ROLE_ROLE_OPERATION', 'ROLE_ROLE_TF')")
33+
public Map<String, Object> getApplicant(
34+
@Argument String applicantId, @Argument List<String> requestedQnaFields) {
35+
return applicantQueryUseCase.executeFiltered(applicantId, requestedQnaFields);
36+
}
37+
}

server/Recruit-Api/src/main/java/com/econovation/recruit/api/applicant/service/AnswerCommandService.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.econovation.recruit.api.applicant.handler.ApplicantStateUpdateEventHandler;
44
import com.econovation.recruit.api.applicant.usecase.ApplicantCommandUseCase;
5+
import com.econovation.recruit.api.recruitment.util.LatestRecruitmentVo;
56
import com.econovation.recruitdomain.common.aop.domainEvent.Events;
67
import com.econovation.recruitdomain.domains.applicant.domain.MongoAnswer;
78
import com.econovation.recruitdomain.domains.applicant.domain.MongoAnswerAdaptor;
@@ -11,7 +12,6 @@
1112
import java.util.Map;
1213
import java.util.UUID;
1314
import lombok.RequiredArgsConstructor;
14-
import org.springframework.beans.factory.annotation.Value;
1515
import org.springframework.stereotype.Service;
1616
import org.springframework.transaction.annotation.Transactional;
1717

@@ -20,9 +20,7 @@
2020
public class AnswerCommandService implements ApplicantCommandUseCase {
2121
private final MongoAnswerAdaptor answerAdaptor;
2222
private final ApplicantStateUpdateEventHandler applicantStateUpdateEventHandler;
23-
24-
@Value("${econovation.year}")
25-
private Integer year;
23+
private final LatestRecruitmentVo latestRecruitInfo;
2624

2725
@Override
2826
@Transactional
@@ -42,6 +40,7 @@ public String execute(String applicantId, String afterState) {
4240

4341
@Override
4442
public UUID execute(Map<String, Object> qna, UUID id) {
43+
int year = latestRecruitInfo.getYear();
4544
ApplicantState nonProcessed = new ApplicantState();
4645
MongoAnswer answer =
4746
MongoAnswer.builder()

server/Recruit-Api/src/main/java/com/econovation/recruit/api/applicant/service/ApplicantService.java

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,19 @@
77
import com.econovation.recruit.api.applicant.dto.GetApplicantsStatusResponse;
88
import com.econovation.recruit.api.applicant.query.AnswerQuery;
99
import com.econovation.recruit.api.applicant.usecase.ApplicantQueryUseCase;
10+
import com.econovation.recruit.api.recruitment.util.LatestRecruitmentVo;
1011
import com.econovation.recruit.utils.sort.SortHelper;
1112
import com.econovation.recruit.utils.vo.PageInfo;
1213
import com.econovation.recruitdomain.domains.applicant.adaptor.AnswerAdaptor;
1314
import com.econovation.recruitdomain.domains.applicant.domain.MongoAnswer;
15+
import com.econovation.recruitdomain.domains.applicant.exception.ApplicantNotFoundException;
1416
import java.util.Collections;
1517
import java.util.HashMap;
1618
import java.util.List;
1719
import java.util.Map;
1820
import lombok.RequiredArgsConstructor;
1921
import org.axonframework.messaging.responsetypes.ResponseTypes;
2022
import org.axonframework.queryhandling.QueryGateway;
21-
import org.springframework.beans.factory.annotation.Value;
2223
import org.springframework.stereotype.Service;
2324
import org.springframework.transaction.annotation.Transactional;
2425

@@ -28,9 +29,7 @@ public class ApplicantService implements ApplicantQueryUseCase {
2829
private final AnswerAdaptor answerAdaptor;
2930
private final QueryGateway queryGateway;
3031
private final SortHelper<MongoAnswer> sortHelper;
31-
32-
@Value("${econovation.year}")
33-
private Integer year;
32+
private final LatestRecruitmentVo latestRecruitInfo;
3433

3534
@Transactional(readOnly = true)
3635
public Map<String, Object> execute(String answerId) {
@@ -233,6 +232,7 @@ public Map<String, Object> execute(String applicantId, List<String> fields) {
233232

234233
@Override
235234
public List<Map<String, Object>> execute(List<String> fields, Integer page) {
235+
int year = latestRecruitInfo.getYear();
236236
List<MongoAnswer> byYear = answerAdaptor.findByYear(year, page);
237237
return splitByAnswers(fields, byYear);
238238
}
@@ -250,4 +250,39 @@ private List<Map<String, Object>> sortAndAddIds(List<MongoAnswer> result, String
250250
}
251251
return getQnaMapListWithIdAndPassState(result);
252252
}
253+
254+
@Transactional(readOnly = true)
255+
public AnswersResponseDto executeFiltered(
256+
Integer year,
257+
Integer page,
258+
String sortType,
259+
String searchKeyword,
260+
List<String> requestedQnaFields) {
261+
262+
PageInfo pageInfo = getPageInfo(year, page, searchKeyword);
263+
List<MongoAnswer> sortedResult =
264+
answerAdaptor.findByYearAndSearchKeywordAndRequestedFields(
265+
year, page, sortType, searchKeyword, requestedQnaFields);
266+
267+
List<Map<String, Object>> qnaMapList = getQnaMapListWithIdAndPassState(sortedResult);
268+
269+
if (qnaMapList.isEmpty()) {
270+
return AnswersResponseDto.of(Collections.emptyList(), pageInfo);
271+
}
272+
return AnswersResponseDto.of(qnaMapList, pageInfo);
273+
}
274+
275+
@Transactional(readOnly = true)
276+
public Map<String, Object> executeFiltered(
277+
String applicantId, List<String> requestedQnaFields) {
278+
MongoAnswer mongoAnswer =
279+
answerAdaptor
280+
.findByIdAndRequestedFields(applicantId, requestedQnaFields)
281+
.orElseThrow(() -> ApplicantNotFoundException.EXCEPTION);
282+
283+
Map<String, Object> qna = mongoAnswer.getQna();
284+
qna.put("id", mongoAnswer.getId());
285+
qna.put(PASS_STATE_KEY, mongoAnswer.getApplicantStateOrDefault());
286+
return qna;
287+
}
253288
}

server/Recruit-Api/src/main/java/com/econovation/recruit/api/applicant/usecase/ApplicantQueryUseCase.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,13 @@ List<Map<String, Object>> execute(
4646
List<MongoAnswer> getApplicantsByYear(Integer year);
4747

4848
MongoAnswer getApplicantById(String applicantId);
49+
50+
AnswersResponseDto executeFiltered(
51+
Integer year,
52+
Integer page,
53+
String sortType,
54+
String searchKeyword,
55+
List<String> requestedQnaFields);
56+
57+
Map<String, Object> executeFiltered(String applicantId, List<String> requestedQnaFields);
4958
}
Lines changed: 15 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,33 @@
11
package com.econovation.recruit.api.applicant.validate;
22

3+
import com.econovation.recruit.api.recruitment.util.LatestRecruitmentVo;
34
import com.econovation.recruitcommon.exception.RecruitCodeException;
45
import com.econovation.recruitdomain.domains.applicant.domain.MongoAnswerAdaptor;
6+
import com.econovation.recruitdomain.domains.applicant.domain.state.RecruitmentStates;
57
import com.econovation.recruitdomain.domains.applicant.exception.ApplicantDuplicateSubmitException;
68
import com.econovation.recruitdomain.domains.applicant.exception.ApplicantOutOfDateException;
79
import com.econovation.recruitdomain.domains.applicant.exception.ApplicantWrongPositionException;
810
import io.vavr.collection.Seq;
911
import io.vavr.control.Validation;
10-
import java.time.LocalDateTime;
11-
import java.time.ZoneId;
12-
import java.time.ZonedDateTime;
1312
import java.util.Map;
14-
import lombok.RequiredArgsConstructor;
1513
import org.springframework.beans.factory.annotation.Value;
1614
import org.springframework.stereotype.Component;
1715

1816
@Component
19-
@RequiredArgsConstructor
2017
public class ApplicantValidator {
2118
private final MongoAnswerAdaptor answerAdaptor;
19+
private final LatestRecruitmentVo latestRecruitInfo;
20+
private final boolean validateEnabled;
2221

23-
@Value("${econovation.recruit.period.start}")
24-
private String recruitPeriodStart;
22+
public ApplicantValidator(
23+
MongoAnswerAdaptor answerAdaptor,
24+
LatestRecruitmentVo latestRecruitInfo,
25+
@Value("${econovation.recruit.valid.enabled}") boolean validateEnabled) {
2526

26-
@Value("${econovation.recruit.period.end}")
27-
private String recruitPeriodEnd;
28-
29-
@Value("${econovation.year}")
30-
private Integer year;
31-
32-
@Value("${econovation.recruit.valid.enabled}")
33-
private boolean validateEnabled;
27+
this.validateEnabled = validateEnabled;
28+
this.latestRecruitInfo = latestRecruitInfo;
29+
this.answerAdaptor = answerAdaptor;
30+
}
3431

3532
public Validation<Seq<RecruitCodeException>, Map<String, Object>> validateRegisterApplicant(
3633
Map<String, Object> qna) {
@@ -53,6 +50,7 @@ private Validation<RecruitCodeException, Object> validateIsRightPosition(
5350
private Validation<RecruitCodeException, Map<String, Object>> validateDuplicateStudentId(
5451
Map<String, Object> qna) {
5552
String studentId = qna.get("classOf").toString();
53+
Integer year = latestRecruitInfo.getYear().intValue();
5654
if (answerAdaptor.existsByAnswer(studentId, year)) {
5755
throw ApplicantDuplicateSubmitException.EXCEPTION;
5856
}
@@ -64,24 +62,11 @@ private Validation<RecruitCodeException, Map<String, Object>> validateOutdated(
6462
if (!validateEnabled) {
6563
return Validation.valid(qna);
6664
}
67-
ZoneId koreaZoneId = ZoneId.of("Asia/Seoul");
68-
ZonedDateTime currentKoreaTime = ZonedDateTime.now(koreaZoneId);
6965

70-
// 비교할 날짜와 시간 설정 (시작 시간과 끝 시간을 설정해두어야 한다.)
71-
LocalDateTime recruitPeriodStartDateTime = LocalDateTime.parse(recruitPeriodStart);
72-
LocalDateTime recruitPeriodEndDateTime = LocalDateTime.parse(recruitPeriodEnd);
73-
ZonedDateTime recruitPeriodStartZoneDateTime =
74-
ZonedDateTime.of(recruitPeriodStartDateTime, koreaZoneId);
75-
ZonedDateTime recruitPeriodEndZoneDateTime =
76-
ZonedDateTime.of(recruitPeriodEndDateTime, koreaZoneId);
77-
78-
// 현재 시간이 2023년 09월 16일 00시 00분 00초 (한국 시간) 이후인지 확인
79-
boolean isOutdated =
80-
currentKoreaTime.isBefore(recruitPeriodStartZoneDateTime)
81-
|| currentKoreaTime.isAfter(recruitPeriodEndZoneDateTime);
82-
if (isOutdated) {
66+
if (!latestRecruitInfo.getState().equals(RecruitmentStates.RECRUITING)) {
8367
throw ApplicantOutOfDateException.EXCEPTION;
8468
}
69+
8570
return Validation.valid(qna);
8671
}
8772
}

server/Recruit-Api/src/main/java/com/econovation/recruit/api/card/controller/BoardRestController.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ public ResponseEntity deleteNavigation(
9595
return new ResponseEntity(HttpStatus.OK);
9696
}
9797

98+
@Deprecated(since = "2025-03-02")
9899
@Operation(summary = "지원서 칸반보드 열(세로줄) 생성", description = "지원서 칸반보드 열(세로줄) 생성")
99100
@ApiErrorExceptionsExample(CreateColumnsExceptionDocs.class)
100101
@PostMapping("/boards/navigations/{navigation-id}/columns")
@@ -113,6 +114,7 @@ public ResponseEntity<String> updateBoardColumn(
113114
return new ResponseEntity(COLUMN_SUCCESS_LOCATION_CHANGE_MESSAGE, HttpStatus.OK);
114115
}
115116

117+
@Deprecated(since = "2025-03-02")
116118
@Operation(
117119
summary = "지원서 세로줄 조회(by NavigationId)",
118120
description = "navigationId에 해당하는 모든 세로줄을 조회합니다., 세로줄이 없으면 빈 배열을 반환합니다.")

0 commit comments

Comments
 (0)