Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
32 changes: 32 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,32 @@
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;
}
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,25 @@
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.builder()
.recipientEmail(recipientEmail)
.status(status)
.errorMessage(errorMessage)
.build();
emailLogRepository.save(emailLog);
}
}
85 changes: 40 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,74 @@

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) {
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);
emailLogService.logEmailSending(to, MailSendStatus.SUCCESS, null);
} catch (MessagingException e) {
log.error("Failed to send email to {}: {}", to, e.getMessage());
emailLogService.logEmailSending(to, MailSendStatus.FAILURE, e.getMessage());
} catch (CustomException e) {
log.error("Email sending error for {}: {}", to, e.getMessage());
emailLogService.logEmailSending(to, MailSendStatus.FAILURE, e.getMessage());
}
}

}
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>
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,10 @@
line-height: 2.5;
text-align: right;
margin-bottom: 40px;
color: white !important;"
th:utext="${emailContent}">
color: white !important;">
먼저 다솜 35기에 많은 관심을 두고 지원해 주셔서 감사드리며,<br>
최종 면접 결과 및 추후 일정에 관해 안내해드리고자 이메일을 발송하게 되었습니다.<br>
최종 면접 결과는 아래 버튼 혹은 홈페이지를 통해 확인이 가능합니다.
</div>

<div style="display: flex;
Expand All @@ -96,8 +98,7 @@
display: flex;
align-items: center;
text-decoration: none;">
<span th:text="${buttonText}">
</span>
<span>최종 결과 확인하기</span>
<span style="
border: solid white;
border-width: 0 2px 2px 0;
Expand Down
Loading
Loading