-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #205 from HongYeseul/feat/notification
[feat/notification] 댓글 작성시 이메일 알림 기능 추가
- Loading branch information
Showing
9 changed files
with
273 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
src/main/java/chzzk/grassdiary/domain/notification/CommentCreatedEvent.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package chzzk.grassdiary.domain.notification; | ||
|
||
import lombok.Getter; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.context.ApplicationEvent; | ||
|
||
import java.time.LocalDateTime; | ||
|
||
@Getter | ||
@Slf4j | ||
public class CommentCreatedEvent extends ApplicationEvent { | ||
private final Long diaryId; | ||
private final String authorName; | ||
private final String diaryAuthorEmail; | ||
private final String commentContent; | ||
private final LocalDateTime diaryCreatedAt; | ||
private final String commentCreatedBy; | ||
private final LocalDateTime commentCreatedAt; | ||
|
||
public CommentCreatedEvent(Object source, | ||
Long diaryId, | ||
String authorName, | ||
String diaryAuthorEmail, | ||
String commentContent, | ||
LocalDateTime diaryCreatedAt, | ||
String commentCreatedBy, | ||
LocalDateTime commentCreatedAt | ||
) { | ||
super(source); | ||
this.diaryId = diaryId; | ||
this.authorName = authorName; | ||
this.diaryAuthorEmail = diaryAuthorEmail; | ||
this.commentContent = commentContent; | ||
this.diaryCreatedAt = diaryCreatedAt; | ||
this.commentCreatedBy = commentCreatedBy; | ||
this.commentCreatedAt = commentCreatedAt; | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
src/main/java/chzzk/grassdiary/domain/notification/EmailNotificationListener.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package chzzk.grassdiary.domain.notification; | ||
|
||
import jakarta.mail.MessagingException; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.transaction.event.TransactionPhase; | ||
import org.springframework.transaction.event.TransactionalEventListener; | ||
|
||
@Component | ||
@RequiredArgsConstructor | ||
public class EmailNotificationListener { | ||
|
||
private final EmailService emailService; | ||
|
||
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) | ||
public void handleCommentCreatedEvent(CommentCreatedEvent event) throws MessagingException { | ||
emailService.sendCommentNotification(event); | ||
} | ||
} |
54 changes: 54 additions & 0 deletions
54
src/main/java/chzzk/grassdiary/domain/notification/EmailService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package chzzk.grassdiary.domain.notification; | ||
|
||
import jakarta.mail.MessagingException; | ||
import jakarta.mail.internet.MimeMessage; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.thymeleaf.spring6.SpringTemplateEngine; | ||
import org.springframework.core.io.ClassPathResource; | ||
import org.springframework.mail.javamail.JavaMailSender; | ||
import org.springframework.mail.javamail.MimeMessageHelper; | ||
import org.springframework.stereotype.Service; | ||
import org.thymeleaf.context.Context; | ||
|
||
@Slf4j | ||
@Service | ||
@RequiredArgsConstructor | ||
public class EmailService { | ||
|
||
private final JavaMailSender mailSender; | ||
|
||
private final SpringTemplateEngine templateEngine; | ||
|
||
public void sendCommentNotification(CommentCreatedEvent event) { | ||
try { | ||
log.info("Sending comment notification for comment {}", event); | ||
MimeMessage message = mailSender.createMimeMessage(); | ||
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8"); | ||
|
||
Context context = new Context(); | ||
context.setVariable("recipientName", event.getAuthorName()); | ||
context.setVariable("diaryDate", event.getDiaryCreatedAt()); | ||
context.setVariable("commenterName", event.getCommentCreatedBy()); | ||
context.setVariable("commentDate", event.getCommentCreatedAt()); | ||
context.setVariable("commentContent", event.getCommentContent()); | ||
context.setVariable("diaryUrl", "https://grassdiary.site/diary/" + event.getDiaryId()); | ||
// context.setVariable("unsubscribeUrl", "http://your-domain.com/unsubscribe"); | ||
|
||
String htmlContent = templateEngine.process("comment-notification", context); | ||
|
||
helper.setTo(event.getDiaryAuthorEmail()); | ||
helper.setSubject("[잔디 일기] 새로운 댓글 알림"); | ||
helper.setText(htmlContent, true); | ||
|
||
ClassPathResource imageResource = new ClassPathResource("static/images/grass-diary-logo.png"); | ||
helper.addInline("headerImage", imageResource); | ||
|
||
mailSender.send(message); | ||
log.info("{}님께 [댓글 알림] 이메일을 보냈습니다.", event.getAuthorName()); | ||
|
||
} catch (MessagingException e) { | ||
throw new RuntimeException("댓글 이메일 보내기가 실패했습니다.", e); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
<!DOCTYPE html> | ||
<html xmlns:th="http://www.thymeleaf.org"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<title>새로운 댓글 알림</title> | ||
<style> | ||
.email-container { | ||
max-width: 600px; | ||
margin: 0 auto; | ||
font-family: 'Arial', sans-serif; | ||
line-height: 1.6; | ||
color: #333333; | ||
} | ||
.button-container { | ||
text-align: center; | ||
margin-top: 20px; | ||
} | ||
.header { | ||
background-color: #f8f9fa; | ||
padding: 20px; | ||
text-align: center; | ||
border-radius: 5px 5px 0 0; | ||
} | ||
.header-image { | ||
width: 200px; | ||
height: auto; | ||
margin-bottom: 15px; | ||
} | ||
.content { | ||
padding: 20px; | ||
background-color: #ffffff; | ||
border: 1px solid #e9ecef; | ||
} | ||
.diary-date { | ||
font-size: 18px; | ||
color: #495057; | ||
margin-bottom: 15px; | ||
padding: 10px; | ||
background-color: #f8f9fa; | ||
border-radius: 5px; | ||
} | ||
.comment-box { | ||
background-color: #f8f9fa; | ||
padding: 15px; | ||
border-radius: 5px; | ||
margin: 10px 0; | ||
} | ||
.commenter-info { | ||
font-weight: bold; | ||
color: #495057; | ||
margin-bottom: 10px; | ||
} | ||
.comment-text { | ||
color: #666666; | ||
} | ||
.button { | ||
display: inline-block; | ||
padding: 10px 20px; | ||
background-color: #029764; | ||
color: #ffffff; | ||
text-decoration: none; | ||
border-radius: 5px; | ||
margin-top: 20px; | ||
} | ||
.footer { | ||
text-align: center; | ||
padding: 20px; | ||
font-size: 12px; | ||
color: #6c757d; | ||
} | ||
</style> | ||
</head> | ||
<body> | ||
<div class="email-container"> | ||
<div class="header"> | ||
<img class="header-image" th:src="'cid:headerImage'" alt="헤더 이미지"/> | ||
</div> | ||
|
||
<div class="content"> | ||
<p th:text="${recipientName} + '님, 일기에 새로운 댓글이 달렸어요!'">홍길동님, 일기에 새로운 댓글이 달렸어요!</p> | ||
|
||
<div class="diary-date"> | ||
<strong>일기 작성일:</strong> | ||
<span th:text="${#temporals.format(diaryDate, 'yyyy년 MM월 dd일')}">2024년 03월 21일</span> | ||
</div> | ||
|
||
<div class="comment-box"> | ||
<div class="commenter-info"> | ||
<span th:text="${commenterName}">댓글 작성자</span>님의 댓글 | ||
<span th:text="${#temporals.format(commentDate, 'yyyy-MM-dd HH:mm')}">2024-03-21 14:30</span> | ||
</div> | ||
<div class="comment-text"> | ||
<p th:text="${commentContent}">댓글 내용이 여기에 표시됩니다.</p> | ||
</div> | ||
</div> | ||
|
||
<div class="button-container"> | ||
<a th:href="${diaryUrl}" class="button">새로운 댓글 보러가기</a> | ||
</div> | ||
</div> | ||
|
||
<div class="footer"> | ||
<p>본 메일은 자동으로 발송되는 알림 메일입니다.</p> | ||
<!-- <p>더 이상 알림을 받고 싶지 않으시다면 <a th:href="${unsubscribeUrl}">여기</a>를 클릭하세요.</p>--> | ||
</div> | ||
</div> | ||
</body> | ||
</html> |