Skip to content

Commit ae46c47

Browse files
committed
DASOMBE-18 지원자 학번,이메일 확인후 면접 예약 및 날자 변경 기능(테스트 못해봄.)
1 parent eec1b56 commit ae46c47

File tree

8 files changed

+118
-4
lines changed

8 files changed

+118
-4
lines changed

src/main/java/dmu/dasom/api/domain/applicant/repository/ApplicantRepository.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@ public interface ApplicantRepository extends JpaRepository<Applicant, Long> {
2525
Optional<Applicant> findByStudentNoAndContactEndsWith(@Param("studentNo") String studentNo,
2626
@Param("contactLastDigits") String contactLastDigits);
2727

28+
Optional<Applicant> findByStudentNoAndEmail(String studentNo, String email);
2829
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@ 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", "파일 인코딩에 실패하였습니다."),
38-
RECRUITMENT_NOT_ACTIVE(400, "C029", "모집 기간이 아닙니다."),
39-
NOT_FOUND_PARTICIPANT(400, "C030", "참가자를 찾을 수 없습니다.")
37+
SLOT_UNAVAILABLE(400, "C028", "해당 슬롯을 예약할 수 없습니다."),
38+
FILE_ENCODE_FAIL(400, "C029", "파일 인코딩에 실패하였습니다."),
39+
RECRUITMENT_NOT_ACTIVE(400, "C030", "모집 기간이 아닙니다."),
40+
NOT_FOUND_PARTICIPANT(400, "C031", "참가자를 찾을 수 없습니다.")
4041
;
4142

4243
private final int status;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package dmu.dasom.api.domain.interview.controller;
2+
3+
import dmu.dasom.api.domain.interview.dto.InterviewReservationModifyRequestDto;
4+
import dmu.dasom.api.domain.interview.service.InterviewService;
5+
import io.swagger.v3.oas.annotations.Operation;
6+
import io.swagger.v3.oas.annotations.tags.Tag;
7+
import lombok.RequiredArgsConstructor;
8+
import org.springframework.http.ResponseEntity;
9+
import org.springframework.web.bind.annotation.*;
10+
11+
@Tag(name = "Interview", description = "면접 관련 API")
12+
@RestController
13+
@RequiredArgsConstructor
14+
@RequestMapping("/api/interview")
15+
public class InterviewController {
16+
17+
private final InterviewService interviewService;
18+
19+
@Operation(summary = "면접 예약 수정", description = "학번과 이메일 인증 후 면접 날짜 및 시간을 수정합니다.")
20+
@PutMapping("/reservation/modify")
21+
public ResponseEntity<Void> modifyInterviewReservation(@RequestBody InterviewReservationModifyRequestDto request) {
22+
interviewService.modifyInterviewReservation(request);
23+
return ResponseEntity.ok().build();
24+
}
25+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package dmu.dasom.api.domain.interview.dto;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import jakarta.validation.constraints.Email;
5+
import jakarta.validation.constraints.NotNull;
6+
import jakarta.validation.constraints.Pattern;
7+
import jakarta.validation.constraints.Size;
8+
import lombok.*;
9+
10+
@Getter
11+
@Setter
12+
@NoArgsConstructor
13+
@AllArgsConstructor
14+
@Builder
15+
@Schema(name = "InterviewReservationModifyRequestDto", description = "면접 예약 수정 요청 DTO")
16+
public class InterviewReservationModifyRequestDto {
17+
18+
@NotNull(message = "학번은 필수 값입니다.")
19+
@Pattern(regexp = "^[0-9]{8}$", message = "학번은 8자리 숫자로 구성되어야 합니다.")
20+
@Size(min = 8, max = 8)
21+
@Schema(description = "지원자 학번", example = "20250001")
22+
private String studentNo;
23+
24+
@NotNull(message = "이메일은 필수 값입니다.")
25+
@Email(message = "유효한 이메일 주소를 입력해주세요.")
26+
@Size(max = 64)
27+
@Schema(description = "지원자 이메일", example = "[email protected]")
28+
private String email;
29+
30+
@NotNull(message = "새로운 슬롯 ID는 필수 값입니다.")
31+
@Schema(description = "새롭게 예약할 면접 슬롯의 ID", example = "2")
32+
private Long newSlotId;
33+
}

src/main/java/dmu/dasom/api/domain/interview/entity/InterviewReservation.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,8 @@ public class InterviewReservation {
3636
@CreatedDate
3737
@Column(name = "created_at", nullable = false, updatable = false)
3838
private LocalDateTime createdAt; // 생성
39+
40+
public void setSlot(InterviewSlot slot) {
41+
this.slot = slot;
42+
}
3943
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
package dmu.dasom.api.domain.interview.repository;
22

33
import dmu.dasom.api.domain.interview.entity.InterviewReservation;
4+
import dmu.dasom.api.domain.applicant.entity.Applicant;
45
import org.springframework.data.jpa.repository.JpaRepository;
56
import org.springframework.stereotype.Repository;
67

8+
import java.util.Optional;
9+
710
@Repository
811
public interface InterviewReservationRepository extends JpaRepository<InterviewReservation, Long> {
912
boolean existsByReservationCode(String reservationCode);
13+
Optional<InterviewReservation> findByReservationCode(String reservationCode);
14+
Optional<InterviewReservation> findByApplicant(Applicant applicant);
1015
}

src/main/java/dmu/dasom/api/domain/interview/service/InterviewService.java

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

33
import dmu.dasom.api.domain.interview.dto.InterviewReservationApplicantResponseDto;
44
import dmu.dasom.api.domain.interview.dto.InterviewReservationRequestDto;
5+
import dmu.dasom.api.domain.interview.dto.InterviewReservationModifyRequestDto;
56
import dmu.dasom.api.domain.interview.dto.InterviewSlotResponseDto;
67

78
import java.time.LocalDate;
@@ -27,4 +28,7 @@ public interface InterviewService {
2728

2829
List<InterviewReservationApplicantResponseDto> getAllInterviewApplicants();
2930

31+
// 면접 예약 수정
32+
void modifyInterviewReservation(InterviewReservationModifyRequestDto request);
33+
3034
}

src/main/java/dmu/dasom/api/domain/interview/service/InterviewServiceImpl.java

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import dmu.dasom.api.domain.common.exception.ErrorCode;
77
import dmu.dasom.api.domain.interview.dto.InterviewReservationApplicantResponseDto;
88
import dmu.dasom.api.domain.interview.dto.InterviewReservationRequestDto;
9+
import dmu.dasom.api.domain.interview.dto.InterviewReservationModifyRequestDto;
910
import dmu.dasom.api.domain.interview.dto.InterviewSlotResponseDto;
1011
import dmu.dasom.api.domain.interview.entity.InterviewReservation;
1112
import dmu.dasom.api.domain.interview.entity.InterviewSlot;
@@ -148,7 +149,7 @@ public List<InterviewSlotResponseDto> getAllInterviewSlots() {
148149
.toList();
149150
}
150151

151-
@Override
152+
@Override
152153
public List<InterviewReservationApplicantResponseDto> getAllInterviewApplicants() {
153154
List<InterviewReservation> reservations = interviewReservationRepository.findAll();
154155

@@ -172,4 +173,44 @@ public List<InterviewReservationApplicantResponseDto> getAllInterviewApplicants(
172173
.collect(Collectors.toList());
173174
}
174175

176+
@Override
177+
@Transactional
178+
public void modifyInterviewReservation(InterviewReservationModifyRequestDto request) {
179+
// 1. 지원자 학번과 이메일로 지원자 조회 및 검증
180+
Applicant applicant = applicantRepository.findByStudentNoAndEmail(request.getStudentNo(), request.getEmail())
181+
.orElseThrow(() -> new CustomException(ErrorCode.APPLICANT_NOT_FOUND));
182+
183+
// 2. 해당 지원자의 기존 면접 예약 조회
184+
InterviewReservation existingReservation = interviewReservationRepository.findByApplicant(applicant)
185+
.orElseThrow(() -> new CustomException(ErrorCode.RESERVATION_NOT_FOUND));
186+
187+
// 3. 새로운 면접 슬롯 조회 및 검증
188+
InterviewSlot newSlot = interviewSlotRepository.findById(request.getNewSlotId())
189+
.orElseThrow(() -> new CustomException(ErrorCode.SLOT_NOT_FOUND));
190+
191+
// 4. 새로운 슬롯이 현재 예약된 슬롯과 동일한지 확인 (불필요한 업데이트 방지)
192+
if (existingReservation.getSlot().getId().equals(newSlot.getId())) {
193+
return; // 동일한 슬롯으로 변경 요청 시 아무것도 하지 않음
194+
}
195+
196+
// 5. 새로운 슬롯의 가용성 확인
197+
if (newSlot.getCurrentCandidates() >= newSlot.getMaxCandidates() || newSlot.getInterviewStatus() != InterviewStatus.ACTIVE) {
198+
throw new CustomException(ErrorCode.SLOT_UNAVAILABLE);
199+
}
200+
201+
// 6. 기존 슬롯의 예약 인원 감소
202+
InterviewSlot oldSlot = existingReservation.getSlot();
203+
oldSlot.decrementCurrentCandidates();
204+
interviewSlotRepository.save(oldSlot); // 변경된 oldSlot 저장
205+
206+
// 7. 예약 정보 업데이트 (새로운 슬롯으로 변경)
207+
existingReservation.setSlot(newSlot); // InterviewReservation 엔티티에 setSlot 메서드가 없으므로 추가해야 함.
208+
209+
// 8. 새로운 슬롯의 예약 인원 증가
210+
newSlot.incrementCurrentCandidates();
211+
interviewSlotRepository.save(newSlot); // 변경된 newSlot 저장
212+
213+
// 9. 업데이트된 예약 정보 저장
214+
interviewReservationRepository.save(existingReservation);
215+
}
175216
}

0 commit comments

Comments
 (0)