Skip to content

Commit 5b27896

Browse files
authored
fix: 부원 지원 로직; 모집 일정 검증 추가
* refactor: 모집 기간 확인 로직 추가, ApplicantService로 합격 결과 확인 메소드 이동 - 관련 에러 코드 추가 * test: 변경된 로직에 따라 테스트 케이스 수정
1 parent 6d4b633 commit 5b27896

File tree

8 files changed

+171
-136
lines changed

8 files changed

+171
-136
lines changed

src/main/java/dmu/dasom/api/domain/applicant/service/ApplicantService.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import dmu.dasom.api.domain.applicant.dto.ApplicantResponseDto;
66
import dmu.dasom.api.domain.applicant.dto.ApplicantStatusUpdateRequestDto;
77
import dmu.dasom.api.domain.email.enums.MailType;
8+
import dmu.dasom.api.domain.recruit.dto.ResultCheckRequestDto;
9+
import dmu.dasom.api.domain.recruit.dto.ResultCheckResponseDto;
810
import dmu.dasom.api.global.dto.PageResponse;
911

1012
public interface ApplicantService {
@@ -21,4 +23,6 @@ public interface ApplicantService {
2123

2224
ApplicantDetailsResponseDto getApplicantByStudentNo(final String studentNo);
2325

26+
ResultCheckResponseDto checkResult(final ResultCheckRequestDto request);
27+
2428
}

src/main/java/dmu/dasom/api/domain/applicant/service/ApplicantServiceImpl.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
import dmu.dasom.api.domain.email.enums.MailType;
1313
import dmu.dasom.api.domain.email.service.EmailService;
1414
import dmu.dasom.api.domain.google.service.GoogleApiService;
15+
import dmu.dasom.api.domain.recruit.dto.ResultCheckRequestDto;
16+
import dmu.dasom.api.domain.recruit.dto.ResultCheckResponseDto;
17+
import dmu.dasom.api.domain.recruit.enums.ResultCheckType;
18+
import dmu.dasom.api.domain.recruit.service.RecruitService;
1519
import dmu.dasom.api.global.dto.PageResponse;
1620
import jakarta.mail.MessagingException;
1721
import lombok.RequiredArgsConstructor;
@@ -22,6 +26,7 @@
2226
import org.springframework.stereotype.Service;
2327
import org.springframework.transaction.annotation.Transactional;
2428

29+
import java.time.LocalDateTime;
2530
import java.util.Optional;
2631

2732
import java.util.List;
@@ -37,13 +42,17 @@ public class ApplicantServiceImpl implements ApplicantService {
3742
private final ApplicantRepository applicantRepository;
3843
private final EmailService emailService;
3944
private final GoogleApiService googleApiService;
45+
private final RecruitService recruitService;
4046

4147
@Value("${google.spreadsheet.id}")
4248
private String spreadSheetId;
4349

4450
// 지원자 저장
4551
@Override
4652
public void apply(final ApplicantCreateRequestDto request) {
53+
if (!recruitService.isRecruitmentActive())
54+
throw new CustomException(ErrorCode.RECRUITMENT_NOT_ACTIVE);
55+
4756
final Optional<Applicant> applicant = findByStudentNo(request.getStudentNo());
4857

4958
// 이미 지원한 학번이 존재할 경우
@@ -139,6 +148,41 @@ public ApplicantDetailsResponseDto getApplicantByStudentNo(final String studentN
139148
.orElseThrow(() -> new CustomException(ErrorCode.ARGUMENT_NOT_VALID));
140149
}
141150

151+
// 합격 결과 확인
152+
@Override
153+
public ResultCheckResponseDto checkResult(final ResultCheckRequestDto request) {
154+
// 결과 발표 시간 검증
155+
final LocalDateTime resultAnnouncementSchedule = recruitService.getResultAnnouncementSchedule(request.getType());
156+
157+
// 설정 된 시간이 현재 시간보다 이전인 경우 예외 발생
158+
final LocalDateTime now = LocalDateTime.now();
159+
if (now.isBefore(resultAnnouncementSchedule))
160+
throw new CustomException(ErrorCode.INVALID_INQUIRY_PERIOD);
161+
162+
// 지원자 정보 조회
163+
final ApplicantDetailsResponseDto applicant = getApplicantByStudentNo(request.getStudentNo());
164+
165+
// 연락처 뒷자리가 일치하지 않을 경우 예외 발생
166+
if (!applicant.getContact().split("-")[2].equals(request.getContactLastDigit()))
167+
throw new CustomException(ErrorCode.ARGUMENT_NOT_VALID);
168+
169+
// 예약 코드 생성
170+
String reservationCode = recruitService.generateReservationCode(request.getStudentNo(), request.getContactLastDigit());
171+
172+
// 합격 여부 반환
173+
return ResultCheckResponseDto.builder()
174+
.type(request.getType())
175+
.studentNo(applicant.getStudentNo())
176+
.name(applicant.getName())
177+
.reservationCode(reservationCode)
178+
.isPassed(request.getType().equals(ResultCheckType.DOCUMENT_PASS) ?
179+
applicant.getStatus()
180+
.equals(ApplicantStatus.DOCUMENT_PASSED) :
181+
applicant.getStatus()
182+
.equals(ApplicantStatus.INTERVIEW_PASSED))
183+
.build();
184+
}
185+
142186
// Repository에서 ID로 지원자 조회
143187
private Applicant findById(final Long id) {
144188
return applicantRepository.findById(id)

src/main/java/dmu/dasom/api/domain/common/exception/ErrorCode.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ public enum ErrorCode {
3434
SLOT_FULL(400, "C025", "해당 슬롯이 가득 찼습니다."),
3535
RESERVATION_NOT_FOUND(400, "C026", "예약을 찾을 수 없습니다."),
3636
SLOT_NOT_ACTIVE(400, "C027", "해당 슬롯이 비활성화 되었습니다."),
37-
FILE_ENCODE_FAIL(400, "C028", "파일 인코딩에 실패하였습니다.")
37+
FILE_ENCODE_FAIL(400, "C028", "파일 인코딩에 실패하였습니다."),
38+
RECRUITMENT_NOT_ACTIVE(400, "C029", "모집 기간이 아닙니다."),
3839
;
3940

4041
private final int status;

src/main/java/dmu/dasom/api/domain/recruit/controller/RecruitController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public ResponseEntity<List<RecruitConfigResponseDto>> getRecruitSchedule() {
7878
})
7979
@GetMapping("/result")
8080
public ResponseEntity<ResultCheckResponseDto> checkResult(@ModelAttribute final ResultCheckRequestDto request) {
81-
return ResponseEntity.ok(recruitService.checkResult(request));
81+
return ResponseEntity.ok(applicantService.checkResult(request));
8282
}
8383

8484
// 면접 일정 생성

src/main/java/dmu/dasom/api/domain/recruit/service/RecruitService.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@
44
import dmu.dasom.api.domain.recruit.dto.ResultCheckResponseDto;
55
import dmu.dasom.api.domain.recruit.dto.RecruitConfigResponseDto;
66
import dmu.dasom.api.domain.recruit.dto.RecruitScheduleModifyRequestDto;
7-
import dmu.dasom.api.domain.recruit.entity.Recruit;
8-
import dmu.dasom.api.domain.recruit.enums.ConfigKey;
7+
import dmu.dasom.api.domain.recruit.enums.ResultCheckType;
98

10-
import java.time.LocalDate;
11-
import java.time.LocalTime;
9+
import java.time.LocalDateTime;
1210
import java.util.List;
1311

1412
public interface RecruitService {
@@ -17,6 +15,10 @@ public interface RecruitService {
1715

1816
void modifyRecruitSchedule(final RecruitScheduleModifyRequestDto requestDto);
1917

20-
ResultCheckResponseDto checkResult(final ResultCheckRequestDto request);
18+
String generateReservationCode(String studentNo, String contactLastDigits);
19+
20+
LocalDateTime getResultAnnouncementSchedule(ResultCheckType type);
21+
22+
boolean isRecruitmentActive();
2123

2224
}

src/main/java/dmu/dasom/api/domain/recruit/service/RecruitServiceImpl.java

Lines changed: 17 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
package dmu.dasom.api.domain.recruit.service;
22

3-
import dmu.dasom.api.domain.applicant.dto.ApplicantDetailsResponseDto;
4-
import dmu.dasom.api.domain.applicant.enums.ApplicantStatus;
5-
import dmu.dasom.api.domain.applicant.service.ApplicantService;
63
import dmu.dasom.api.domain.common.exception.CustomException;
74
import dmu.dasom.api.domain.common.exception.ErrorCode;
8-
import dmu.dasom.api.domain.recruit.dto.ResultCheckRequestDto;
9-
import dmu.dasom.api.domain.recruit.dto.ResultCheckResponseDto;
105
import dmu.dasom.api.domain.recruit.dto.RecruitConfigResponseDto;
116
import dmu.dasom.api.domain.recruit.dto.RecruitScheduleModifyRequestDto;
127
import dmu.dasom.api.domain.recruit.entity.Recruit;
@@ -17,7 +12,6 @@
1712
import org.springframework.stereotype.Service;
1813
import org.springframework.transaction.annotation.Transactional;
1914

20-
import java.time.LocalDate;
2115
import java.time.LocalDateTime;
2216
import java.time.LocalTime;
2317
import java.time.format.DateTimeFormatter;
@@ -30,7 +24,6 @@
3024
public class RecruitServiceImpl implements RecruitService {
3125

3226
private final RecruitRepository recruitRepository;
33-
private final ApplicantService applicantService;
3427

3528
// 모집 일정 설정 조회
3629
@Override
@@ -57,43 +50,28 @@ public void modifyRecruitSchedule(final RecruitScheduleModifyRequestDto request)
5750
config.updateDateTime(dateTime);
5851
}
5952

60-
// 합격 결과 확인
53+
// 모집 기간 여부 확인
6154
@Override
62-
public ResultCheckResponseDto checkResult(final ResultCheckRequestDto request) {
63-
// 예약 코드 생성
64-
String reservationCode = generateReservationCode(request.getStudentNo(), request.getContactLastDigit());
55+
public boolean isRecruitmentActive() {
56+
final LocalDateTime recruitStartPeriod = parseDateTimeFormat(findByKey(ConfigKey.RECRUITMENT_PERIOD_START).getValue());
57+
final LocalDateTime recruitEndPeriod = parseDateTimeFormat(findByKey(ConfigKey.RECRUITMENT_PERIOD_END).getValue());
58+
final LocalDateTime now = LocalDateTime.now();
59+
60+
return now.isAfter(recruitStartPeriod) && now.isBefore(recruitEndPeriod);
61+
}
62+
63+
@Override
64+
public String generateReservationCode(String studentNo, String contactLastDigits) {
65+
return studentNo + contactLastDigits; // 학번 전체 + 전화번호 뒤 4자리
66+
}
6567

66-
// 결과 발표 시간 검증
67-
final Recruit recruit = switch (request.getType()) {
68+
@Override
69+
public LocalDateTime getResultAnnouncementSchedule(ResultCheckType type) {
70+
final Recruit recruit = switch (type) {
6871
case DOCUMENT_PASS -> findByKey(ConfigKey.DOCUMENT_PASS_ANNOUNCEMENT);
6972
case INTERVIEW_PASS -> findByKey(ConfigKey.INTERVIEW_PASS_ANNOUNCEMENT);
7073
};
71-
final LocalDateTime parsedTime = parseDateTimeFormat(recruit.getValue());
72-
73-
// 설정 된 시간이 현재 시간보다 이전인 경우 예외 발생
74-
final LocalDateTime now = LocalDateTime.now();
75-
if (now.isBefore(parsedTime))
76-
throw new CustomException(ErrorCode.INVALID_INQUIRY_PERIOD);
77-
78-
// 지원자 정보 조회
79-
final ApplicantDetailsResponseDto applicant = applicantService.getApplicantByStudentNo(request.getStudentNo());
80-
81-
// 연락처 뒷자리가 일치하지 않을 경우 예외 발생
82-
if (!applicant.getContact().split("-")[2].equals(request.getContactLastDigit()))
83-
throw new CustomException(ErrorCode.ARGUMENT_NOT_VALID);
84-
85-
// 합격 여부 반환
86-
return ResultCheckResponseDto.builder()
87-
.type(request.getType())
88-
.studentNo(applicant.getStudentNo())
89-
.name(applicant.getName())
90-
.reservationCode(reservationCode)
91-
.isPassed(request.getType().equals(ResultCheckType.DOCUMENT_PASS) ?
92-
applicant.getStatus()
93-
.equals(ApplicantStatus.DOCUMENT_PASSED) :
94-
applicant.getStatus()
95-
.equals(ApplicantStatus.INTERVIEW_PASSED))
96-
.build();
74+
return parseDateTimeFormat(recruit.getValue());
9775
}
9876

9977
// DB에 저장된 모든 Recruit 객체를 찾아 반환
@@ -126,9 +104,4 @@ private LocalDateTime parseDateTimeFormat(String value) {
126104
}
127105
}
128106

129-
public String generateReservationCode(String studentNo, String contactLastDigits) {
130-
return studentNo + contactLastDigits; // 학번 전체 + 전화번호 뒤 4자리
131-
}
132-
133-
134107
}

src/test/java/dmu/dasom/api/domain/applicant/ApplicantServiceTest.java

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
import dmu.dasom.api.domain.email.enums.MailType;
1414
import dmu.dasom.api.domain.email.service.EmailService;
1515
import dmu.dasom.api.domain.google.service.GoogleApiService;
16+
import dmu.dasom.api.domain.recruit.dto.ResultCheckRequestDto;
17+
import dmu.dasom.api.domain.recruit.dto.ResultCheckResponseDto;
18+
import dmu.dasom.api.domain.recruit.enums.ResultCheckType;
19+
import dmu.dasom.api.domain.recruit.service.RecruitServiceImpl;
1620
import dmu.dasom.api.global.dto.PageResponse;
1721
import jakarta.mail.MessagingException;
1822
import org.junit.jupiter.api.BeforeEach;
@@ -44,6 +48,9 @@ class ApplicantServiceTest {
4448
@Mock
4549
private EmailService emailService;
4650

51+
@Mock
52+
private RecruitServiceImpl recruitService;
53+
4754
@InjectMocks
4855
private ApplicantServiceImpl applicantService;
4956

@@ -61,6 +68,7 @@ void apply_success() {
6168
// given
6269
ApplicantCreateRequestDto request = mock(ApplicantCreateRequestDto.class);
6370
when(request.getStudentNo()).thenReturn("20210000");
71+
when(recruitService.isRecruitmentActive()).thenReturn(true);
6472

6573
Applicant mockApplicant = Applicant.builder()
6674
.name("홍길동")
@@ -100,6 +108,7 @@ void apply_fail() {
100108
ApplicantCreateRequestDto request = mock(ApplicantCreateRequestDto.class);
101109
when(request.getStudentNo()).thenReturn("20210000");
102110
when(applicantRepository.findByStudentNo("20210000")).thenReturn(Optional.of(mock(Applicant.class)));
111+
when(recruitService.isRecruitmentActive()).thenReturn(true);
103112
when(request.getIsOverwriteConfirmed()).thenReturn(false);
104113

105114
// when
@@ -120,6 +129,7 @@ void apply_overwrite() {
120129
when(request.getStudentNo()).thenReturn("20210000");
121130
Applicant existingApplicant = mock(Applicant.class); // 기존 Applicant 객체 모킹
122131
when(applicantRepository.findByStudentNo("20210000")).thenReturn(Optional.of(existingApplicant));
132+
when(recruitService.isRecruitmentActive()).thenReturn(true);
123133
when(request.getIsOverwriteConfirmed()).thenReturn(true);
124134

125135
// when
@@ -334,6 +344,7 @@ void apply_overwrite_withGoogleSheets() {
334344
// given
335345
ApplicantCreateRequestDto request = mock(ApplicantCreateRequestDto.class);
336346
when(request.getStudentNo()).thenReturn("20210000");
347+
when(recruitService.isRecruitmentActive()).thenReturn(true);
337348

338349
Applicant existingApplicant = mock(Applicant.class); // 기존 Applicant 객체 모킹
339350
when(applicantRepository.findByStudentNo("20210000")).thenReturn(Optional.of(existingApplicant));
@@ -348,4 +359,89 @@ void apply_overwrite_withGoogleSheets() {
348359
verify(googleApiService).updateSheet(List.of(existingApplicant));
349360
}
350361

362+
@Test
363+
@DisplayName("합격 결과 확인 - 성공")
364+
void checkResult_success() {
365+
// given
366+
LocalDateTime pastDateTime = LocalDateTime.now().minusHours(1);
367+
when(recruitService.getResultAnnouncementSchedule(ResultCheckType.DOCUMENT_PASS)).thenReturn(pastDateTime);
368+
369+
ResultCheckRequestDto request = mock(ResultCheckRequestDto.class);
370+
when(request.getType()).thenReturn(ResultCheckType.DOCUMENT_PASS);
371+
when(request.getStudentNo()).thenReturn("20210000");
372+
when(request.getContactLastDigit()).thenReturn("1234");
373+
374+
Applicant applicant = mock(Applicant.class);
375+
when(applicantRepository.findByStudentNo("20210000")).thenReturn(Optional.of(applicant));
376+
377+
ApplicantDetailsResponseDto responseDto = mock(ApplicantDetailsResponseDto.class);
378+
when(responseDto.getContact()).thenReturn("010-5678-1234");
379+
when(responseDto.getStatus()).thenReturn(ApplicantStatus.DOCUMENT_PASSED);
380+
when(responseDto.getStudentNo()).thenReturn("20210000");
381+
when(responseDto.getName()).thenReturn("TestName");
382+
383+
when(applicant.toApplicantDetailsResponse()).thenReturn(responseDto);
384+
385+
when(recruitService.generateReservationCode("20210000", "1234")).thenReturn("202100001234");
386+
387+
// when
388+
ResultCheckResponseDto result = applicantService.checkResult(request);
389+
390+
// then
391+
assertNotNull(result);
392+
assertTrue(result.getIsPassed());
393+
verify(recruitService).getResultAnnouncementSchedule(ResultCheckType.DOCUMENT_PASS);
394+
verify(applicantRepository).findByStudentNo("20210000");
395+
}
396+
397+
@Test
398+
@DisplayName("합격 결과 확인 - 실패 (확인 기간 아님)")
399+
void checkResult_fail_inquiryPeriod() {
400+
// given
401+
LocalDateTime futureDateTime = LocalDateTime.now().plusHours(1);
402+
when(recruitService.getResultAnnouncementSchedule(ResultCheckType.DOCUMENT_PASS)).thenReturn(futureDateTime);
403+
404+
ResultCheckRequestDto request = mock(ResultCheckRequestDto.class);
405+
when(request.getType()).thenReturn(ResultCheckType.DOCUMENT_PASS);
406+
407+
// when
408+
CustomException exception = assertThrows(CustomException.class, () -> {
409+
applicantService.checkResult(request);
410+
});
411+
412+
// then
413+
assertEquals(ErrorCode.INVALID_INQUIRY_PERIOD, exception.getErrorCode());
414+
verify(recruitService).getResultAnnouncementSchedule(ResultCheckType.DOCUMENT_PASS);
415+
}
416+
417+
@Test
418+
@DisplayName("합격 결과 확인 - 실패 (연락처 뒷자리 불일치)")
419+
void checkResult_fail_contactMismatch() {
420+
// given
421+
LocalDateTime pastDateTime = LocalDateTime.now().minusHours(1);
422+
when(recruitService.getResultAnnouncementSchedule(ResultCheckType.DOCUMENT_PASS)).thenReturn(pastDateTime);
423+
424+
ResultCheckRequestDto request = mock(ResultCheckRequestDto.class);
425+
when(request.getType()).thenReturn(ResultCheckType.DOCUMENT_PASS);
426+
when(request.getStudentNo()).thenReturn("20210000");
427+
when(request.getContactLastDigit()).thenReturn("0000");
428+
429+
Applicant applicant = mock(Applicant.class);
430+
when(applicantRepository.findByStudentNo("20210000")).thenReturn(Optional.of(applicant));
431+
432+
ApplicantDetailsResponseDto responseDto = mock(ApplicantDetailsResponseDto.class);
433+
when(responseDto.getContact()).thenReturn("010-5678-1234");
434+
when(applicant.toApplicantDetailsResponse()).thenReturn(responseDto);
435+
436+
// when
437+
CustomException exception = assertThrows(CustomException.class, () -> {
438+
applicantService.checkResult(request);
439+
});
440+
441+
// then
442+
assertEquals(ErrorCode.ARGUMENT_NOT_VALID, exception.getErrorCode());
443+
verify(recruitService).getResultAnnouncementSchedule(ResultCheckType.DOCUMENT_PASS);
444+
verify(applicantRepository).findByStudentNo("20210000");
445+
}
446+
351447
}

0 commit comments

Comments
 (0)