Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/main/java/dmu/dasom/api/ApiApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.scheduling.annotation.EnableAsync;

@EnableJpaAuditing
@SpringBootApplication
@EnableAsync
public class ApiApplication {

public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,7 @@ public void sendEmailsToApplicants(MailType mailType) {
}

for (Applicant applicant : applicants) {
try {
emailService.sendEmail(applicant.getEmail(), applicant.getName(), mailType);
} catch (MessagingException e) {
System.err.println("Failed to send email to: " + applicant.getEmail());
}
emailService.sendEmail(applicant.getEmail(), applicant.getName(), mailType);
}
}

Expand Down
40 changes: 40 additions & 0 deletions src/main/java/dmu/dasom/api/domain/google/entity/EmailLog.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package dmu.dasom.api.domain.google.entity;

import dmu.dasom.api.domain.google.enums.MailSendStatus;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.CreationTimestamp;

import java.time.LocalDateTime;

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class EmailLog {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false)
private String recipientEmail;

@Enumerated(EnumType.STRING)
@Column(nullable = false)
private MailSendStatus status;

private String errorMessage;

@CreationTimestamp
private LocalDateTime sentAt;

public static EmailLog of(String recipientEmail, MailSendStatus status, String errorMessage) {
return EmailLog.builder()
.recipientEmail(recipientEmail)
.status(status)
.errorMessage(errorMessage)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package dmu.dasom.api.domain.google.enums;

public enum MailSendStatus {
SUCCESS,
FAILURE
}
26 changes: 26 additions & 0 deletions src/main/java/dmu/dasom/api/domain/google/enums/MailTemplate.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package dmu.dasom.api.domain.google.enums;

import dmu.dasom.api.domain.common.exception.CustomException;
import dmu.dasom.api.domain.common.exception.ErrorCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

import java.util.Arrays;

@Getter
@RequiredArgsConstructor
public enum MailTemplate {
DOCUMENT_RESULT(MailType.DOCUMENT_RESULT, "동양미래대학교 컴퓨터소프트웨어공학과 전공 동아리 DASOM 서류 결과 안내", "document-result-email"),
FINAL_RESULT(MailType.FINAL_RESULT, "동양미래대학교 컴퓨터소프트웨어공학과 전공 동아리 DASOM 최종 면접 결과 안내", "final-result-email");

private final MailType mailType;
private final String subject;
private final String templateName;

public static MailTemplate getMailType(MailType mailType) {
return Arrays.stream(values())
.filter(template -> template.getMailType() == mailType)
.findFirst()
.orElseThrow(() -> new CustomException(ErrorCode.MAIL_TYPE_NOT_VALID));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package dmu.dasom.api.domain.google.repository;

import dmu.dasom.api.domain.google.entity.EmailLog;
import org.springframework.data.jpa.repository.JpaRepository;

public interface EmailLogRepository extends JpaRepository<EmailLog, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package dmu.dasom.api.domain.google.service;

import dmu.dasom.api.domain.google.entity.EmailLog;
import dmu.dasom.api.domain.google.enums.MailSendStatus;
import dmu.dasom.api.domain.google.repository.EmailLogRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class EmailLogService {

private final EmailLogRepository emailLogRepository;

@Async
public void logEmailSending(String recipientEmail, MailSendStatus status, String errorMessage) {
EmailLog emailLog = EmailLog.of(recipientEmail, status, errorMessage);
emailLogRepository.save(emailLog);
}
}
89 changes: 44 additions & 45 deletions src/main/java/dmu/dasom/api/domain/google/service/EmailService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,79 +2,78 @@

import dmu.dasom.api.domain.common.exception.CustomException;
import dmu.dasom.api.domain.common.exception.ErrorCode;
import dmu.dasom.api.domain.google.enums.MailSendStatus;
import dmu.dasom.api.domain.google.enums.MailTemplate;
import dmu.dasom.api.domain.google.enums.MailType;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

@Slf4j
@RequiredArgsConstructor
@Service
public class EmailService {

private final TemplateEngine templateEngine;
private final JavaMailSender javaMailSender;
private final EmailLogService emailLogService;

@Value("${spring.mail.username}")
private String from;

public void sendEmail(String to, String name, MailType mailType) throws MessagingException {
if (mailType == null){
throw new CustomException(ErrorCode.MAIL_TYPE_NOT_VALID);
}
@Async
public void sendEmail(String to, String name, MailType mailType) {
MailSendStatus mailSendStatus = MailSendStatus.SUCCESS;
String errorMessage = null;
try {
if (mailType == null) {
throw new CustomException(ErrorCode.MAIL_TYPE_NOT_VALID);
}

// 메일 제목 및 템플릿 설정
String subject;
String emailContent;
String buttonUrl = "https://dmu-dasom.or.kr/recruit/result";
String buttonText;
// 메일 템플릿 조회
MailTemplate mailTemplate = MailTemplate.getMailType(mailType);
String buttonUrl = "https://dmu-dasom.or.kr/recruit/result";

switch (mailType) {
case DOCUMENT_RESULT -> {
subject = "동양미래대학교 컴퓨터소프트웨어공학과 전공 동아리 DASOM 서류 결과 안내";
emailContent = "먼저 다솜 34기에 많은 관심을 두고 지원해 주셔서 감사드리며,<br>" +
"내부 서류 평가 결과 및 추후 일정에 관해 안내해드리고자 이메일을 발송하게 되었습니다.<br>" +
"서류 전형 결과는 아래 버튼 혹은 홈페이지를 통해 확인이 가능합니다.";
buttonText = "서류 결과 확인하기";
}
case FINAL_RESULT -> {
subject = "동양미래대학교 컴퓨터소프트웨어공학과 전공 동아리 DASOM 최종 면접 결과 안내";
emailContent = "먼저 다솜 34기에 많은 관심을 두고 지원해 주셔서 감사드리며,<br>" +
"최종 면접 결과 및 추후 일정에 관해 안내해드리고자 이메일을 발송하게 되었습니다.<br>" +
"최종 면접 결과는 아래 버튼 혹은 홈페이지를 통해 확인이 가능합니다.";
buttonText = "최종 결과 확인하기";
}
default -> throw new IllegalStateException("Unexpected value: " + mailType);
}
// HTML 템플릿에 전달할 데이터 설정
Context context = new Context();
context.setVariable("name", name); // 지원자 이름 전달
context.setVariable("buttonUrl", buttonUrl); // 버튼 링크 전달

// HTML 템플릿에 전달할 데이터 설정
Context context = new Context();
context.setVariable("name", name); // 지원자 이름 전달
context.setVariable("emailContent", emailContent); // 이메일 내용 전달
context.setVariable("buttonUrl", buttonUrl); // 버튼 링크 전달
context.setVariable("buttonText", buttonText);

// HTML 템플릿 처리
String htmlBody = templateEngine.process("email-template", context);
// HTML 템플릿 처리
String htmlBody = templateEngine.process(mailTemplate.getTemplateName(), context);

// 이메일 생성 및 전송
MimeMessage message = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
// 이메일 생성 및 전송
MimeMessage message = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");

helper.setTo(to);
helper.setSubject(subject);
helper.setText(htmlBody, true);
helper.setFrom((from != null && !from.isEmpty()) ? from : "[email protected]");
helper.setTo(to);
helper.setSubject(mailTemplate.getSubject());
helper.setText(htmlBody, true);
helper.setFrom((from != null && !from.isEmpty()) ? from : "[email protected]");

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

javaMailSender.send(message);
javaMailSender.send(message);
log.info("Email sent successfull {}", to);
} catch (MessagingException e) {
log.error("Failed to send email to {}: {}", to, e.getMessage());
mailSendStatus = MailSendStatus.FAILURE;
errorMessage = e.getMessage();
} catch (CustomException e) {
log.error("Email sending error for {}: {}", to, e.getMessage());
mailSendStatus = MailSendStatus.FAILURE;
errorMessage = e.getMessage();
}
emailLogService.logEmailSending(to, mailSendStatus, errorMessage);
}

}
122 changes: 122 additions & 0 deletions src/main/resources/templates/document-result-email.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="color-scheme" content="light dark">
<meta name="supported-color-schemes" content="light dark">
<title>이메일 안내</title>
</head>
<body style="
background-color: #17171B;
color: white !important;
font-family: 'Pretendard', sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;">
<div style="
width: 740px;
height: 680px;
background: #17171B;
padding: 30px;
position: relative;">
<div style="
display: flex;
justify-content: center;
position: relative;
margin-bottom: 20px;">
<div style="
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;">
<div style="
flex: 1;
border-top: 1px solid #00B493;
margin: 0 10px;">

</div>
<img src="https://dmu-dasom.or.kr/static/media/dasomLogo.c82d220d8093c3cb8d7fc0b148cafcd1.svg" alt="로고" style="
width: 21px;
height: 24px;
border-radius: 3px;">
<div style="
flex: 1;
border-top: 1px solid #00B493;
margin: 0 10px;">
</div>
</div>
</div>

<div style="
font-size: 48px;
font-weight: 900;
color: #00B493;
margin-bottom: 30px;">
DASOM
</div>
<div style="
font-size: 20px;
font-weight: 600;
margin-bottom: 5px;
color: white !important;"
th:text="${name} + '님 안녕하세요.'">
</div>
<div style="
font-size: 20px;
font-weight: 600;
margin-bottom: 5px;
color: white !important;">
컴퓨터공학부 전공동아리 다솜입니다.
</div>

<div style="
font-size: 16px;
font-weight: 400;
line-height: 2.5;
text-align: right;
margin-bottom: 40px;
color: white !important;">
먼저 다솜 35기에 많은 관심을 두고 지원해 주셔서 감사드리며,<br>
내부 서류 평가 결과 및 추후 일정에 관해 안내해드리고자 이메일을 발송하게 되었습니다.<br>
서류 전형 결과는 아래 버튼 혹은 홈페이지를 통해 확인이 가능합니다.
</div>

<div style="display: flex;
justify-content: flex-end;
margin-bottom: 40px;">
<a th:href="${buttonUrl}"
style="
background: #00B493;
color: white !important;
font-size: 16px;
font-weight: 400;
padding: 10px 20px;
border-radius: 5px;
display: flex;
align-items: center;
text-decoration: none;">
<span>서류 결과 확인하기</span>
<span style="
border: solid white;
border-width: 0 2px 2px 0;
display: inline-block;
padding: 5px;
transform: rotate(-45deg);
margin-left: 10px;">
</span>
</a>
</div>

<div style="font-size: 16px;
font-weight: 400;
line-height: 1.5;
color: white !important;">
또한, 문의 사항은 본 메일에 회신 또는 아래 번호로 편하게 연락 부탁드립니다.<br>
010-6361-3481
</div>
</div>
</body>
</html>
Loading
Loading