Skip to content

Commit 6d0e134

Browse files
committed
DASOMBE-18 <피드백 반영. 추가 설명 표시. 인증코드 템플릿 반영>
1 parent 4c5cc6f commit 6d0e134

File tree

11 files changed

+168
-150
lines changed

11 files changed

+168
-150
lines changed

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ public enum ErrorCode {
3434
SLOT_FULL(400, "C025", "해당 슬롯이 가득 찼습니다."),
3535
RESERVATION_NOT_FOUND(400, "C026", "예약을 찾을 수 없습니다."),
3636
SLOT_NOT_ACTIVE(400, "C027", "해당 슬롯이 비활성화 되었습니다."),
37-
SLOT_UNAVAILABLE(400, "C028", "해당 슬롯을 예약할 수 없습니다."),
38-
FILE_ENCODE_FAIL(400, "C029", "파일 인코딩에 실패하였습니다."),
39-
RECRUITMENT_NOT_ACTIVE(400, "C030", "모집 기간이 아닙니다."),
40-
NOT_FOUND_PARTICIPANT(400, "C031", "참가자를 찾을 수 없습니다."),
41-
VERIFICATION_CODE_NOT_VALID(400, "C032", "인증 코드가 유효하지 않습니다.")
37+
FILE_ENCODE_FAIL(400, "C028", "파일 인코딩에 실패하였습니다."),
38+
RECRUITMENT_NOT_ACTIVE(400, "C029", "모집 기간이 아닙니다."),
39+
NOT_FOUND_PARTICIPANT(400, "C030", "참가자를 찾을 수 없습니다."),
40+
VERIFICATION_CODE_NOT_VALID(400, "C031", "인증 코드가 유효하지 않습니다."),
41+
SLOT_UNAVAILABLE(400, "C032", "해당 슬롯을 예약할 수 없습니다.")
4242
;
4343

4444
private final int status;

src/main/java/dmu/dasom/api/domain/email/service/EmailService.java

Lines changed: 0 additions & 96 deletions
This file was deleted.

src/main/java/dmu/dasom/api/domain/google/service/EmailService.java

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,8 @@ public void sendEmail(String to, String name, MailType mailType) {
4444

4545
// HTML 템플릿에 전달할 데이터 설정
4646
Context context = new Context();
47-
context.setVariable("name", name); // 지원자 이름 전달
48-
context.setVariable("buttonUrl", buttonUrl); // 버튼 링크 전달
49-
47+
context.setVariable("name", name);
48+
context.setVariable("buttonUrl", buttonUrl);
5049

5150
// HTML 템플릿 처리
5251
String htmlBody = templateEngine.process(mailTemplate.getTemplateName(), context);
@@ -60,11 +59,10 @@ public void sendEmail(String to, String name, MailType mailType) {
6059
helper.setText(htmlBody, true);
6160
helper.setFrom((from != null && !from.isEmpty()) ? from : "[email protected]");
6261

63-
// Content-Type을 명시적으로 설정
6462
message.setContent(htmlBody, "text/html; charset=utf-8");
6563

6664
javaMailSender.send(message);
67-
log.info("Email sent successfull {}", to);
65+
log.info("Email sent successfully {}", to);
6866
} catch (MessagingException e) {
6967
log.error("Failed to send email to {}: {}", to, e.getMessage());
7068
mailSendStatus = MailSendStatus.FAILURE;
@@ -76,4 +74,35 @@ public void sendEmail(String to, String name, MailType mailType) {
7674
}
7775
emailLogService.logEmailSending(to, mailSendStatus, errorMessage);
7876
}
77+
78+
/*
79+
* 면접 예약 변경을 위한 인증코드 발송
80+
* - VerificationCodeManager에서 생성된 코드를 이메일로 전송
81+
* - verify-num-email.html 템플릿을 이용해 코드와 버튼 링크 포함
82+
*/
83+
public void sendVerificationEmail(String to, String name, String code) throws MessagingException {
84+
String subject = "DASOM 면접 시간 변경을 위한 이메일 인증 코드 안내";
85+
86+
// 인증 코드만 템플릿으로 전달
87+
String emailContent = "인증 코드: <strong>" + code + "</strong>";
88+
89+
Context context = new Context();
90+
context.setVariable("name", name);
91+
context.setVariable("emailContent", emailContent);
92+
context.setVariable("buttonUrl", "https://dmu-dasom.or.kr");
93+
context.setVariable("buttonText", "인증 완료");
94+
95+
String htmlBody = templateEngine.process("verify-num-email", context);
96+
97+
MimeMessage message = javaMailSender.createMimeMessage();
98+
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
99+
100+
helper.setTo(to);
101+
helper.setSubject(subject);
102+
helper.setText(htmlBody, true);
103+
helper.setFrom((from != null && !from.isEmpty()) ? from : "[email protected]");
104+
105+
javaMailSender.send(message);
106+
}
107+
79108
}

src/main/java/dmu/dasom/api/domain/interview/controller/InterviewController.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
import dmu.dasom.api.domain.applicant.repository.ApplicantRepository;
55
import dmu.dasom.api.domain.common.exception.CustomException;
66
import dmu.dasom.api.domain.common.exception.ErrorCode;
7-
import dmu.dasom.api.domain.email.service.EmailService;
7+
import dmu.dasom.api.domain.google.service.EmailService;
8+
89
import dmu.dasom.api.domain.interview.dto.InterviewReservationModifyRequestDto;
910
import dmu.dasom.api.domain.interview.dto.VerificationCodeRequestDto;
1011
import dmu.dasom.api.domain.interview.service.InterviewService;
@@ -29,27 +30,46 @@ public class InterviewController {
2930
private final InterviewService interviewService;
3031
private final ApplicantRepository applicantRepository;
3132
private final VerificationCodeManager verificationCodeManager;
32-
private final EmailService emailService;
33+
private final EmailService emailService; // 이메일 발송 서비스 (Google 기반)
3334

35+
/*
36+
* 면접 예약 수정을 위한 인증 코드 발송
37+
* - 지원자의 학번을 입력받아 해당 지원자를 조회
38+
* - VerificationCodeManager를 통해 인증 코드 생성 및 Redis 저장
39+
* - EmailService를 이용해 지원자 이메일로 인증 코드 발송
40+
*/
3441
@Operation(summary = "면접 예약 수정을 위한 인증 코드 발송", description = "지원자의 학번을 받아 이메일로 인증 코드를 발송합니다.")
3542
@PostMapping("/send-verification")
3643
public ResponseEntity<Void> sendVerificationCode(@Valid @RequestBody VerificationCodeRequestDto request) throws MessagingException {
44+
// 학번으로 지원자 조회 (없으면 예외 발생)
3745
Applicant applicant = applicantRepository.findByStudentNo(request.getStudentNo())
3846
.orElseThrow(() -> new CustomException(ErrorCode.APPLICANT_NOT_FOUND));
3947

48+
// 인증 코드 생성 후 Redis에 저장
4049
String code = verificationCodeManager.generateAndStoreCode(applicant.getStudentNo());
50+
51+
// 이메일 발송 (받는 사람 이메일, 이름, 코드 전달)
4152
emailService.sendVerificationEmail(applicant.getEmail(), applicant.getName(), code);
4253

4354
return ResponseEntity.ok().build();
4455
}
4556

57+
/*
58+
* 면접 예약 수정
59+
* - 사용자가 받은 인증 코드를 검증한 후
60+
* - InterviewService를 통해 예약 날짜/시간 수정 처리
61+
*/
4662
@Operation(summary = "면접 예약 수정", description = "이메일로 발송된 인증 코드를 통해 인증 후, 면접 날짜 및 시간을 수정합니다.")
4763
@PutMapping("/reservation/modify")
4864
public ResponseEntity<Void> modifyInterviewReservation(@Valid @RequestBody InterviewReservationModifyRequestDto request) {
4965
interviewService.modifyInterviewReservation(request);
5066
return ResponseEntity.ok().build();
5167
}
5268

69+
/*
70+
* 모든 면접 지원자 조회
71+
* - InterviewService를 통해 모든 지원자 + 예약 정보 반환
72+
*/
5373
@Operation(summary = "모든 면접 지원자 목록 조회", description = "모든 면접 지원자의 상세 정보와 예약 정보를 조회합니다.")
5474
@GetMapping("/applicants")
5575
public ResponseEntity<List<InterviewReservationApplicantResponseDto>> getAllInterviewApplicants() {

src/main/java/dmu/dasom/api/domain/interview/repository/InterviewSlotRepository.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import dmu.dasom.api.domain.interview.enums.InterviewStatus;
55
import org.springframework.data.jpa.repository.JpaRepository;
66
import org.springframework.data.jpa.repository.Query;
7+
import org.springframework.data.repository.query.Param; // ★ import 추가
78
import org.springframework.stereotype.Repository;
89

910
import java.util.Collection;
@@ -12,13 +13,12 @@
1213
@Repository
1314
public interface InterviewSlotRepository extends JpaRepository<InterviewSlot, Long> {
1415
// 현재 인원이 최대 인원보다 작은 슬롯 조회
15-
// 현재 예약된 인원이 최대 지원자 수보다 적은 슬롯 조회
1616
@Query("SELECT s FROM InterviewSlot s WHERE s.currentCandidates < s.maxCandidates")
1717
Collection<InterviewSlot> findAllByCurrentCandidatesLessThanMaxCandidates();
1818

1919
// 상태에 따른 슬롯 조회
2020
@Query("SELECT s FROM InterviewSlot s WHERE s.interviewStatus = :status AND s.currentCandidates < s.maxCandidates")
21-
List<InterviewSlot> findAllByStatusAndCurrentCandidatesLessThanMaxCandidates(@org.springframework.data.repository.query.Param("status") InterviewStatus interviewStatus);
21+
List<InterviewSlot> findAllByStatusAndCurrentCandidatesLessThanMaxCandidates(@Param("status") InterviewStatus interviewStatus);
2222

2323
// 슬롯이 하나라도 존재하는지 확인
2424
@Query("SELECT COUNT(s) > 0 FROM InterviewSlot s")

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ public List<InterviewSlotResponseDto> getAllInterviewSlots() {
151151
.toList();
152152
}
153153

154-
@Override
154+
@Override
155155
public List<InterviewReservationApplicantResponseDto> getAllInterviewApplicants() {
156156
List<InterviewReservation> reservations = interviewReservationRepository.findAll();
157157

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,20 +67,31 @@ public ResponseEntity<List<RecruitConfigResponseDto>> getRecruitSchedule() {
6767
return ResponseEntity.ok(recruitService.getRecruitSchedule());
6868
}
6969

70+
/*
71+
* 모집 일정 수정
72+
* - 관리자가 모집 일정을 수정할 때 사용
73+
* - RecruitService.modifyRecruitSchedule()을 호출하여 DB 반영
74+
*/
7075
@Operation(summary = "모집 일정 수정")
7176
@PutMapping("/schedule")
7277
public ResponseEntity<Void> modifyRecruitSchedule(@RequestBody dmu.dasom.api.domain.recruit.dto.RecruitScheduleModifyRequestDto request) {
7378
recruitService.modifyRecruitSchedule(request);
7479
return ResponseEntity.ok().build();
7580
}
7681

82+
/*
83+
* 모집 일정 초기화 (테스트용)
84+
* - 테스트 시 초기화를 위해 전체 모집 일정을 초기 상태로 되돌림
85+
* - RecruitService.initRecruitSchedule()을 호출
86+
*/
7787
@Operation(summary = "TEMP: 모집 일정 초기화")
7888
@GetMapping("/init-schedule")
7989
public ResponseEntity<String> initSchedule() {
8090
recruitService.initRecruitSchedule();
8191
return ResponseEntity.ok("Recruit schedule initialized successfully.");
8292
}
8393

94+
8495
// 합격 결과 확인
8596
@Operation(summary = "합격 결과 확인")
8697
@ApiResponses(value = {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public interface RecruitService {
1313

1414
List<RecruitConfigResponseDto> getRecruitSchedule();
1515

16-
void modifyRecruitSchedule(RecruitScheduleModifyRequestDto request);
16+
void modifyRecruitSchedule(RecruitScheduleModifyRequestDto request);
1717

1818
void initRecruitSchedule();
1919

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

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ public class RecruitServiceImpl implements RecruitService {
2929
@Override
3030
public List<RecruitConfigResponseDto> getRecruitSchedule() {
3131
return findAll().stream()
32-
.map(config -> config.getKey() == ConfigKey.INTERVIEW_TIME_START || config.getKey() == ConfigKey.INTERVIEW_TIME_END
33-
? config.toTimeResponse() : config.toResponse())
34-
.toList();
32+
.map(config -> config.getKey() == ConfigKey.INTERVIEW_TIME_START || config.getKey() == ConfigKey.INTERVIEW_TIME_END
33+
? config.toTimeResponse() : config.toResponse())
34+
.toList();
3535
}
3636

3737
// 모집 일정 설정 수정
@@ -74,16 +74,25 @@ public LocalDateTime getResultAnnouncementSchedule(ResultCheckType type) {
7474
return parseDateTimeFormat(recruit.getValue());
7575
}
7676

77+
/*
78+
* 모집 일정 초기화
79+
* - DB에 Recruit 데이터가 존재하지 않을 경우 기본 값으로 초기화
80+
* - 각 ConfigKey에 대해 Recruit 엔티티를 생성하여 저장
81+
* - 기본 값은 "2025-01-01T00:00:00"으로 설정됨
82+
*/
7783
@Override
7884
@Transactional
7985
public void initRecruitSchedule() {
86+
// 이미 데이터가 존재하면 초기화하지 않음
8087
if (recruitRepository.count() > 0) {
81-
return; // Already initialized
88+
return;
8289
}
83-
for (dmu.dasom.api.domain.recruit.enums.ConfigKey key : dmu.dasom.api.domain.recruit.enums.ConfigKey.values()) {
84-
dmu.dasom.api.domain.recruit.entity.Recruit recruit = dmu.dasom.api.domain.recruit.entity.Recruit.builder()
90+
91+
// 모든 ConfigKey를 순회하며 기본 Recruit 데이터 생성
92+
for (ConfigKey key : ConfigKey.values()) {
93+
Recruit recruit = Recruit.builder()
8594
.key(key)
86-
.value("2025-01-01T00:00:00") // Default value
95+
.value("2025-01-01T00:00:00") // 초기 기본 값
8796
.build();
8897
recruitRepository.save(recruit);
8998
}
@@ -97,8 +106,7 @@ private List<Recruit> findAll() {
97106
// DB에서 key에 해당하는 Recruit 객체를 찾아 반환
98107
private Recruit findByKey(final ConfigKey key) {
99108
return recruitRepository.findByKey(key)
100-
.orElseThrow(() -> new CustomException(ErrorCode.ARGUMENT_NOT_VALID));
101-
109+
.orElseThrow(() -> new CustomException(ErrorCode.ARGUMENT_NOT_VALID));
102110
}
103111

104112
// 시간 형식 변환 및 검증
@@ -118,5 +126,4 @@ private LocalDateTime parseDateTimeFormat(String value) {
118126
throw new CustomException(ErrorCode.INVALID_DATETIME_FORMAT);
119127
}
120128
}
121-
122129
}

0 commit comments

Comments
 (0)