1+ package sevenstar .marineleisure .global .mail ;
2+
3+ import java .time .LocalDate ;
4+ import java .util .ArrayList ;
5+ import java .util .EnumMap ;
6+ import java .util .HashMap ;
7+ import java .util .HashSet ;
8+ import java .util .List ;
9+ import java .util .Map ;
10+ import java .util .Set ;
11+
12+ import org .springframework .mail .javamail .JavaMailSender ;
13+ import org .springframework .mail .javamail .MimeMessageHelper ;
14+ import org .springframework .stereotype .Service ;
15+
16+ import jakarta .mail .internet .MimeMessage ;
17+ import lombok .RequiredArgsConstructor ;
18+ import lombok .extern .slf4j .Slf4j ;
19+ import sevenstar .marineleisure .favorite .repository .FavoriteRepository ;
20+ import sevenstar .marineleisure .global .enums .ActivityCategory ;
21+ import sevenstar .marineleisure .global .enums .TotalIndex ;
22+ import sevenstar .marineleisure .spot .dto .EmailContent ;
23+ import sevenstar .marineleisure .spot .dto .detail .provider .ActivityProvider ;
24+
25+ @ Service
26+ @ RequiredArgsConstructor
27+ @ Slf4j
28+ public class MailService {
29+ private static final String MESSAGE_SUBJECT = "[MarineLeisure] 즐겨찾기한 스팟이 최상의 컨디션이에요!" ;
30+
31+ private final JavaMailSender javaMailSender ;
32+ private final FavoriteRepository favoriteRepository ;
33+ private final List <ActivityProvider > providers ;
34+
35+ public void sendMail (String to , String subject , String htmlContent ) {
36+ try {
37+ MimeMessage mimeMessage = javaMailSender .createMimeMessage ();
38+ MimeMessageHelper helper = new MimeMessageHelper (mimeMessage , "UTF-8" );
39+ helper .
setFrom (
"[email protected] " );
40+ helper .setTo (to );
41+ helper .setSubject (subject );
42+ helper .setText (htmlContent , true ); // true = HTML
43+
44+ javaMailSender .send (mimeMessage );
45+ } catch (Exception e ) {
46+ log .error ("메일 전송 실패" , e );
47+ }
48+ }
49+
50+
51+ public void sendMailToHaveFavoriteBestSpot (LocalDate date ) {
52+ TotalIndex totalIndex = TotalIndex .VERY_GOOD ;
53+ List <EmailContent > emailContents = new ArrayList <>();
54+ for (ActivityProvider provider : providers ) {
55+ emailContents .addAll (provider .findEmailContent (totalIndex , date ));
56+ }
57+ Map <String , Map <ActivityCategory , Set <String >>> result = new HashMap <>();
58+ for (EmailContent emailContent : emailContents ) {
59+ List <String > emails = favoriteRepository .findEmailByFavoriteBestSpot (emailContent .spotId ());
60+ for (String email : emails ) {
61+ if (result .containsKey (email )) {
62+ result .get (email ).get (emailContent .category ()).add (emailContent .spotName ());
63+ } else {
64+ Map <ActivityCategory , Set <String >> map = new EnumMap <>(ActivityCategory .class );
65+ for (ActivityCategory value : ActivityCategory .values ()) {
66+ map .put (value , new HashSet <>());
67+ }
68+ map .get (emailContent .category ()).add (emailContent .spotName ());
69+ result .put (email , map );
70+ }
71+ }
72+ }
73+ for (Map .Entry <String , Map <ActivityCategory , Set <String >>> entry : result .entrySet ()) {
74+ sendMail (entry .getKey (), MESSAGE_SUBJECT , transformEmailContent (entry .getValue ()));
75+ }
76+ }
77+
78+ // private String transformEmailContent(Map<ActivityCategory, Set<String>> map) {
79+ // StringBuilder sb = new StringBuilder();
80+ // sb.append("<div style='font-family: Arial, sans-serif; font-size: 14px;'>");
81+ // sb.append("<p>안녕하세요, <strong>MarineLeisure</strong>입니다 🌊</p>");
82+ // sb.append("<p>고객님이 즐겨찾기한 장소 중, 오늘 같은 날 <strong>최상의 컨디션</strong>을 보이는 스팟들을 추천드립니다.</p>");
83+ //
84+ // sb.append("<ul>");
85+ // for (ActivityCategory category : ActivityCategory.values()) {
86+ // Set<String> spots = map.getOrDefault(category, Set.of());
87+ // String spotList = spots.isEmpty() ? "없어요 😢" : String.join(", ", spots);
88+ // sb.append("<li><strong>")
89+ // .append(category.getKoreanName())
90+ // .append("</strong>에 좋은 스팟: ")
91+ // .append(spotList)
92+ // .append("</li>");
93+ // }
94+ // sb.append("</ul>");
95+ //
96+ // sb.append("<p>👉 <a href=\"https://marineleisure.com\" target=\"_blank\">MarineLeisure 앱에서 자세히 보기</a></p>");
97+ // sb.append("<p>안전하고 즐거운 하루 보내세요 😊<br>MarineLeisure 드림</p>");
98+ // sb.append("</div>");
99+ //
100+ // return sb.toString();
101+ // }
102+
103+ private String transformEmailContent (Map <ActivityCategory , Set <String >> map ) {
104+ StringBuilder sb = new StringBuilder ();
105+
106+ sb .append ("<div style='font-family: \" Apple SD Gothic Neo\" , sans-serif; background-color: #f4f4f4; padding: 20px;'>" )
107+ .append ("<div style='max-width: 600px; margin: auto; background-color: #ffffff; border-radius: 10px; padding: 30px; box-shadow: 0 0 10px rgba(0,0,0,0.05);'>" )
108+
109+ .append ("<h2 style='color: #0077b6; text-align: center;'>🌊 MarineLeisure 추천 스팟 알림</h2>" )
110+ .append ("<p style='font-size: 15px; color: #333;'>" )
111+ .append ("고객님이 즐겨찾기한 해양 활동 스팟 중, 오늘 같은 날 <strong style='color: #0077b6;'>최고의 컨디션</strong>을 보이는 장소를 추천드릴게요!" )
112+ .append ("</p>" );
113+
114+ for (ActivityCategory category : ActivityCategory .values ()) {
115+ Set <String > spots = map .getOrDefault (category , Set .of ());
116+ if (!spots .isEmpty ()) {
117+ sb .append ("<div style='margin-top: 20px;'>" )
118+ .append ("<h3 style='color: #023e8a; font-size: 16px;'>" )
119+ .append ("✔️ " ).append (category .getKoreanName ()).append (" 추천 스팟" )
120+ .append ("</h3>" )
121+ .append ("<ul style='padding-left: 20px;'>" );
122+ for (String spot : spots ) {
123+ sb .append ("<li>" ).append (spot ).append ("</li>" );
124+ }
125+ sb .append ("</ul></div>" );
126+ }
127+ }
128+
129+ sb .append ("<div style='text-align: center; margin-top: 30px;'>" )
130+ .append ("<a href='https://marineleisure.vercel.app' target='_blank' style='background-color: #00b4d8; color: white; padding: 12px 24px; text-decoration: none; border-radius: 5px; font-weight: bold;'>MarineLeisure 앱에서 확인하기</a>" )
131+ .append ("</div>" )
132+
133+ .append ("<p style='margin-top: 30px; font-size: 14px; color: #555;'>" )
134+ .append ("안전하고 즐거운 하루 보내세요!<br><strong>MarineLeisure 드림</strong>" )
135+ .append ("</p>" )
136+
137+ .append ("</div></div>" );
138+
139+ return sb .toString ();
140+ }
141+
142+ }
0 commit comments