Skip to content

Commit 795aced

Browse files
authored
feat: 합격 조회 API 구현
* feat: Applicant - 학번으로 지원자 조회 로직 구현 * feat: 지원 결과 확인 요청 DTO 구현 * feat: 지원 결과 확인 응답 DTO 구현 * feat: 결과 조회 타입 정의 * feat: 결과 조회 관련 에러코드 추가 * feat: 합격 결과 확인 로직 구현 - 현재 시간이 설정 된 시간(합격 발표 시간) 이전일 경우 예외 발생 - 연락처 뒷자리가 일치하지 않을 경우 예외 발생 * feat: 합격 결과 확인 엔드포인트 구현, 명세 작성 * test: 학번으로 지원자 조회 테스트 케이스 작성 * test: 합격 결과 확인 테스트 케이스 작성
1 parent a1cab45 commit 795aced

File tree

11 files changed

+295
-11
lines changed

11 files changed

+295
-11
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,6 @@ public interface ApplicantService {
1919

2020
void sendEmailsToApplicants(MailType mailType);
2121

22+
ApplicantDetailsResponseDto getApplicantByStudentNo(final String studentNo);
23+
2224
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,13 @@ public void sendEmailsToApplicants(MailType mailType) {
110110
}
111111
}
112112

113+
// 학번으로 지원자 조회
114+
@Override
115+
public ApplicantDetailsResponseDto getApplicantByStudentNo(final String studentNo) {
116+
return findByStudentNo(studentNo)
117+
.map(Applicant::toApplicantDetailsResponse)
118+
.orElseThrow(() -> new CustomException(ErrorCode.ARGUMENT_NOT_VALID));
119+
}
113120

114121
// Repository에서 ID로 지원자 조회
115122
private Applicant findById(final Long 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
@@ -23,7 +23,8 @@ public enum ErrorCode {
2323
SEND_EMAIL_FAIL(400, "C014", "이메일 전송에 실패하였습니다."),
2424
MAIL_TYPE_NOT_VALID(400, "C015", "메일 타입이 올바르지 않습니다."),
2525
INVALID_DATETIME_FORMAT(400, "C016", "날짜 형식이 올바르지 않습니다."),
26-
INVALID_TIME_FORMAT(400, "C017", "시간 형식이 올바르지 않습니다.")
26+
INVALID_TIME_FORMAT(400, "C017", "시간 형식이 올바르지 않습니다."),
27+
INVALID_INQUIRY_PERIOD(400, "C018", "조회 기간이 아닙니다.")
2728
;
2829

2930
private final int status;

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

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import dmu.dasom.api.domain.applicant.dto.ApplicantCreateRequestDto;
44
import dmu.dasom.api.domain.applicant.service.ApplicantService;
55
import dmu.dasom.api.domain.common.exception.ErrorResponse;
6+
import dmu.dasom.api.domain.recruit.dto.ResultCheckRequestDto;
7+
import dmu.dasom.api.domain.recruit.dto.ResultCheckResponseDto;
68
import dmu.dasom.api.domain.recruit.dto.RecruitConfigResponseDto;
79
import dmu.dasom.api.domain.recruit.service.RecruitService;
810
import io.swagger.v3.oas.annotations.Operation;
@@ -29,19 +31,22 @@ public class RecruitController {
2931
// 지원하기
3032
@Operation(summary = "부원 지원하기")
3133
@ApiResponses(value = {
32-
@ApiResponse(responseCode = "200", description = "지원 성공"),
33-
@ApiResponse(responseCode = "400", description = "실패 케이스",
34-
content = @Content(
35-
mediaType = "application/json",
36-
schema = @Schema(implementation = ErrorResponse.class),
37-
examples = {
38-
@ExampleObject(
39-
name = "학번 중복",
40-
value = "{ \"code\": \"C013\", \"message\": \"이미 등록된 학번입니다.\" }")}))})
34+
@ApiResponse(responseCode = "200", description = "지원 성공"),
35+
@ApiResponse(responseCode = "400", description = "실패 케이스",
36+
content = @Content(
37+
mediaType = "application/json",
38+
schema = @Schema(implementation = ErrorResponse.class),
39+
examples = {
40+
@ExampleObject(
41+
name = "학번 중복",
42+
value = "{ \"code\": \"C013\", \"message\": \"이미 등록된 학번입니다.\" }")
43+
}))
44+
})
4145
@PostMapping("/apply")
4246
public ResponseEntity<Void> apply(@Valid @RequestBody final ApplicantCreateRequestDto request) {
4347
applicantService.apply(request);
44-
return ResponseEntity.ok().build();
48+
return ResponseEntity.ok()
49+
.build();
4550
}
4651

4752
// 모집 일정 조회
@@ -52,4 +57,23 @@ public ResponseEntity<List<RecruitConfigResponseDto>> getRecruitSchedule() {
5257
return ResponseEntity.ok(recruitService.getRecruitSchedule());
5358
}
5459

60+
// 합격 결과 확인
61+
@Operation(summary = "합격 결과 확인")
62+
@ApiResponses(value = {
63+
@ApiResponse(responseCode = "200", description = "합격 결과 확인 성공"),
64+
@ApiResponse(responseCode = "400", description = "실패 케이스",
65+
content = @Content(
66+
mediaType = "application/json",
67+
schema = @Schema(implementation = ErrorResponse.class),
68+
examples = {
69+
@ExampleObject(
70+
name = "학번 조회 실패 혹은 전화번호 뒷자리 일치하지 않음",
71+
value = "{ \"code\": \"C007\", \"message\": \"요청한 값이 올바르지 않습니다.\" }")
72+
}))
73+
})
74+
@GetMapping("/result")
75+
public ResponseEntity<ResultCheckResponseDto> checkResult(@ModelAttribute final ResultCheckRequestDto request) {
76+
return ResponseEntity.ok(recruitService.checkResult(request));
77+
}
78+
5579
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package dmu.dasom.api.domain.recruit.dto;
2+
3+
import dmu.dasom.api.domain.recruit.enums.ResultCheckType;
4+
import io.swagger.v3.oas.annotations.Parameter;
5+
import io.swagger.v3.oas.annotations.media.Schema;
6+
import jakarta.validation.constraints.NotNull;
7+
import lombok.AccessLevel;
8+
import lombok.AllArgsConstructor;
9+
import lombok.Getter;
10+
import lombok.NoArgsConstructor;
11+
import org.springdoc.core.annotations.ParameterObject;
12+
13+
@AllArgsConstructor
14+
@Getter
15+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
16+
@ParameterObject
17+
@Schema(name = "ResultCheckRequestDto", description = "지원 결과 확인 요청 DTO")
18+
public class ResultCheckRequestDto {
19+
20+
@NotNull
21+
@Parameter(description = "지원 결과 조회 타입")
22+
@Schema(example = "DOCUMENT_PASS")
23+
private ResultCheckType type;
24+
25+
@NotNull
26+
@Parameter(description = "학번")
27+
@Schema(example = "20210000")
28+
private String studentNo;
29+
30+
@NotNull
31+
@Parameter(description = "전화번호 뒷자리")
32+
@Schema(example = "6789")
33+
private String contactLastDigit;
34+
35+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package dmu.dasom.api.domain.recruit.dto;
2+
3+
import dmu.dasom.api.domain.recruit.enums.ResultCheckType;
4+
import io.swagger.v3.oas.annotations.media.Schema;
5+
import jakarta.validation.constraints.NotNull;
6+
import jakarta.validation.constraints.Size;
7+
import lombok.Builder;
8+
import lombok.Getter;
9+
10+
@Getter
11+
@Builder
12+
@Schema(name = "ResultCheckResponseDto", description = "지원 결과 확인 응답 DTO")
13+
public class ResultCheckResponseDto {
14+
15+
@NotNull
16+
@Schema(description = "지원 결과 확인 타입", example = "DOCUMENT_PASS")
17+
ResultCheckType type;
18+
19+
@NotNull
20+
@Schema(description = "학번", example = "20210000")
21+
@Size(min = 8, max = 8)
22+
String studentNo;
23+
24+
@NotNull
25+
@Schema(description = "이름", example = "홍길동")
26+
@Size(max = 16)
27+
String name;
28+
29+
@NotNull
30+
@Schema(description = "결과", example = "true")
31+
Boolean isPassed;
32+
33+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package dmu.dasom.api.domain.recruit.enums;
2+
3+
public enum ResultCheckType {
4+
DOCUMENT_PASS,
5+
INTERVIEW_PASS
6+
}

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

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

3+
import dmu.dasom.api.domain.recruit.dto.ResultCheckRequestDto;
4+
import dmu.dasom.api.domain.recruit.dto.ResultCheckResponseDto;
35
import dmu.dasom.api.domain.recruit.dto.RecruitConfigResponseDto;
46
import dmu.dasom.api.domain.recruit.dto.RecruitScheduleModifyRequestDto;
57

@@ -11,4 +13,6 @@ public interface RecruitService {
1113

1214
void modifyRecruitSchedule(final RecruitScheduleModifyRequestDto requestDto);
1315

16+
ResultCheckResponseDto checkResult(final ResultCheckRequestDto request);
17+
1418
}

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

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
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;
36
import dmu.dasom.api.domain.common.exception.CustomException;
47
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;
510
import dmu.dasom.api.domain.recruit.dto.RecruitConfigResponseDto;
611
import dmu.dasom.api.domain.recruit.dto.RecruitScheduleModifyRequestDto;
712
import dmu.dasom.api.domain.recruit.entity.Recruit;
813
import dmu.dasom.api.domain.recruit.enums.ConfigKey;
14+
import dmu.dasom.api.domain.recruit.enums.ResultCheckType;
915
import dmu.dasom.api.domain.recruit.repository.RecruitRepository;
1016
import lombok.RequiredArgsConstructor;
1117
import org.springframework.stereotype.Service;
@@ -23,6 +29,7 @@
2329
public class RecruitServiceImpl implements RecruitService {
2430

2531
private final RecruitRepository recruitRepository;
32+
private final ApplicantService applicantService;
2633

2734
// 모집 일정 설정 조회
2835
@Override
@@ -49,6 +56,39 @@ public void modifyRecruitSchedule(final RecruitScheduleModifyRequestDto request)
4956
config.updateDateTime(dateTime);
5057
}
5158

59+
// 합격 결과 확인
60+
@Override
61+
public ResultCheckResponseDto checkResult(final ResultCheckRequestDto request) {
62+
final Recruit recruit = switch (request.getType()) {
63+
case DOCUMENT_PASS -> findByKey(ConfigKey.DOCUMENT_PASS_ANNOUNCEMENT);
64+
case INTERVIEW_PASS -> findByKey(ConfigKey.INTERVIEW_PASS_ANNOUNCEMENT);
65+
};
66+
final LocalDateTime parsedTime = parseDateTimeFormat(recruit.getValue());
67+
68+
// 설정 된 시간이 현재 시간보다 이전인 경우 예외 발생
69+
final LocalDateTime now = LocalDateTime.now();
70+
if (now.isBefore(parsedTime))
71+
throw new CustomException(ErrorCode.INVALID_INQUIRY_PERIOD);
72+
73+
final ApplicantDetailsResponseDto applicant = applicantService.getApplicantByStudentNo(request.getStudentNo());
74+
75+
// 연락처 뒷자리가 일치하지 않을 경우 예외 발생
76+
if (!applicant.getContact().split("-")[2].equals(request.getContactLastDigit()))
77+
throw new CustomException(ErrorCode.ARGUMENT_NOT_VALID);
78+
79+
// 합격 여부 반환
80+
return ResultCheckResponseDto.builder()
81+
.type(request.getType())
82+
.studentNo(applicant.getStudentNo())
83+
.name(applicant.getName())
84+
.isPassed(request.getType().equals(ResultCheckType.DOCUMENT_PASS) ?
85+
applicant.getStatus()
86+
.equals(ApplicantStatus.DOCUMENT_PASSED) :
87+
applicant.getStatus()
88+
.equals(ApplicantStatus.INTERVIEW_PASSED))
89+
.build();
90+
}
91+
5292
// DB에 저장된 모든 Recruit 객체를 찾아 반환
5393
private List<Recruit> findAll() {
5494
return recruitRepository.findAll();

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dmu.dasom.api.domain.applicant;
22

33
import dmu.dasom.api.domain.applicant.dto.ApplicantCreateRequestDto;
4+
import dmu.dasom.api.domain.applicant.dto.ApplicantDetailsResponseDto;
45
import dmu.dasom.api.domain.applicant.dto.ApplicantResponseDto;
56
import dmu.dasom.api.domain.applicant.entity.Applicant;
67
import dmu.dasom.api.domain.applicant.enums.ApplicantStatus;
@@ -176,4 +177,39 @@ void sendEmailsToApplicants_finalResult() throws MessagingException {
176177
verify(emailService).sendEmail("[email protected]", "합격자", mailType);
177178
verify(emailService).sendEmail("[email protected]", "불합격자", mailType);
178179
}
180+
181+
@Test
182+
@DisplayName("학번으로 지원자 조회 - 성공")
183+
void getApplicantByStudentNo_success() {
184+
// given
185+
String studentNo = "20210000";
186+
Applicant applicant = mock(Applicant.class);
187+
when(applicantRepository.findByStudentNo(studentNo)).thenReturn(Optional.of(applicant));
188+
when(applicant.toApplicantDetailsResponse()).thenReturn(mock(ApplicantDetailsResponseDto.class));
189+
190+
// when
191+
ApplicantDetailsResponseDto applicantByStudentNo = applicantService.getApplicantByStudentNo(studentNo);
192+
193+
// then
194+
assertNotNull(applicantByStudentNo);
195+
verify(applicantRepository).findByStudentNo(studentNo);
196+
}
197+
198+
@Test
199+
@DisplayName("학번으로 지원자 조회 - 실패 (결과 없음)")
200+
void getApplicantByStudentNo_fail() {
201+
// given
202+
String studentNo = "20210000";
203+
when(applicantRepository.findByStudentNo(studentNo)).thenReturn(Optional.empty());
204+
205+
// when
206+
CustomException exception = assertThrows(CustomException.class, () -> {
207+
applicantService.getApplicantByStudentNo(studentNo);
208+
});
209+
210+
// then
211+
verify(applicantRepository).findByStudentNo(studentNo);
212+
assertEquals(ErrorCode.ARGUMENT_NOT_VALID, exception.getErrorCode());
213+
}
214+
179215
}

0 commit comments

Comments
 (0)