diff --git a/.github/workflows/dev_deploy.yml b/.github/workflows/dev_deploy.yml index 7df2e74..acdd6de 100644 --- a/.github/workflows/dev_deploy.yml +++ b/.github/workflows/dev_deploy.yml @@ -108,6 +108,8 @@ jobs: echo "KAKAO_CLIENT_ID=${{ secrets.KAKAO_CLIENT_ID }}" >> ~/deploy/.env echo "KAKAO_CLIENT_SECRET=${{ secrets.KAKAO_CLIENT_SECRET }}" >> ~/deploy/.env echo "JWT_SECRET_KEY=${{ secrets.JWT_SECRET_KEY }}" >> ~/deploy/.env + echo "MAIL_USERNAME=${{ secrets.MAIL_USERNAME }}" >> ~/deploy/.env + echo "MAIL_PASSWORD=${{ secrets.MAIL_PASSWORD }}" >> ~/deploy/.env sudo docker-compose -f ~/deploy/docker-compose.yml pull sudo docker-compose -f ~/deploy/docker-compose.yml up -d sudo docker image prune -f diff --git a/build.gradle b/build.gradle index b1967a7..a03fcd2 100644 --- a/build.gradle +++ b/build.gradle @@ -60,6 +60,13 @@ dependencies { //Swagger implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0' + + //email + implementation 'org.springframework.boot:spring-boot-starter-mail' + + //Thymeleaf + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' + implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect' } tasks.named('test') { diff --git a/docker-compose.yml b/docker-compose.yml index 0c25995..a84b7bc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,6 +15,8 @@ services: KAKAO_CLIENT_ID: ${KAKAO_CLIENT_ID} KAKAO_CLIENT_SECRET: ${KAKAO_CLIENT_SECRET} JWT_SECRET_KEY: ${JWT_SECRET_KEY} + MAIL_USERNAME : ${MAIL_USERNAME} + MAIL_PASSWORD : ${MAIL_PASSWORD} hibernate_ddl_auto: update REDIS_HOST: redis depends_on: diff --git a/src/main/java/com/example/fiurinee/FiurineeApplication.java b/src/main/java/com/example/fiurinee/FiurineeApplication.java index a9d33ba..68329e2 100644 --- a/src/main/java/com/example/fiurinee/FiurineeApplication.java +++ b/src/main/java/com/example/fiurinee/FiurineeApplication.java @@ -5,8 +5,10 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import com.example.fiurinee.domain.flower.service.FlowerService; +import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication +@EnableScheduling public class FiurineeApplication implements CommandLineRunner { @Autowired diff --git a/src/main/java/com/example/fiurinee/domain/anniversary/service/AnniversaryService.java b/src/main/java/com/example/fiurinee/domain/anniversary/service/AnniversaryService.java index cd97df4..1d54c6a 100644 --- a/src/main/java/com/example/fiurinee/domain/anniversary/service/AnniversaryService.java +++ b/src/main/java/com/example/fiurinee/domain/anniversary/service/AnniversaryService.java @@ -5,8 +5,10 @@ import com.example.fiurinee.domain.anniversary.entity.Anniversary; import com.example.fiurinee.domain.anniversary.entity.AnniversaryType; import com.example.fiurinee.domain.anniversary.repository.AnniversaryRepository; +import com.example.fiurinee.domain.mail.MailService; import com.example.fiurinee.domain.member.entity.Member; import com.example.fiurinee.domain.member.repository.MemberRepository; +import jakarta.mail.MessagingException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -30,6 +32,9 @@ public class AnniversaryService { @Autowired private MemberRepository memberRepository; + @Autowired + private MailService mailService; + public Anniversary addAnniversary(Long memberId, AnniversaryRequestDTO requestDTO) { Member member = memberRepository.findById(memberId).orElseThrow(() -> new IllegalArgumentException("Invalid member ID")); @@ -153,6 +158,7 @@ public List> calculateDDay(Anniversary anniversary) { } + public List getDDayZeroAnniversaries(List anniversaries) { List dDayZeroList = new ArrayList<>(); diff --git a/src/main/java/com/example/fiurinee/domain/mail/AnniversarySchedular.java b/src/main/java/com/example/fiurinee/domain/mail/AnniversarySchedular.java new file mode 100644 index 0000000..0433741 --- /dev/null +++ b/src/main/java/com/example/fiurinee/domain/mail/AnniversarySchedular.java @@ -0,0 +1,56 @@ +package com.example.fiurinee.domain.mail; + +import com.example.fiurinee.domain.anniversary.entity.Anniversary; +import com.example.fiurinee.domain.anniversary.service.AnniversaryService; +import com.example.fiurinee.domain.member.entity.Member; +import com.example.fiurinee.domain.member.service.MemberService; +import jakarta.mail.MessagingException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + + +import java.util.List; +import java.util.Map; + +@Service +public class AnniversarySchedular { + private final AnniversaryService anniversaryService; + private final MailService mailService; + private final MemberService memberService; + + public AnniversarySchedular(AnniversaryService anniversaryService, MailService mailService, MemberService memberService) { + this.anniversaryService = anniversaryService; + this.mailService = mailService; + this.memberService = memberService; + } + + @Scheduled(cron = "0 30 14 * * *", zone = "Asia/Seoul") + @Transactional + public void sendAnniversaryEmails() { + List members = memberService.findAll(); + for (Member member : members) { + if (!member.isAlarm()) { + continue; + } + List anniversaries = member.getAnniversaries(); + for (Anniversary anniversary : anniversaries) { + List> allDDays = anniversaryService.calculateDDay(anniversary); + for (Map dDay : allDDays) { + for (Map.Entry entry : dDay.entrySet()) { + try { + if (entry.getValue() == 0) { + mailService.sendAnniversaryEmail(anniversary.getMember(), anniversary); + } else if (entry.getValue() == 3) { + mailService.sendPreAnniversaryEmail(anniversary.getMember(), anniversary); + } + } catch (MessagingException e) { + e.printStackTrace(); + } + } + } + } + } + } +} diff --git a/src/main/java/com/example/fiurinee/domain/mail/MailService.java b/src/main/java/com/example/fiurinee/domain/mail/MailService.java new file mode 100644 index 0000000..096d1b2 --- /dev/null +++ b/src/main/java/com/example/fiurinee/domain/mail/MailService.java @@ -0,0 +1,59 @@ +package com.example.fiurinee.domain.mail; + +import com.example.fiurinee.domain.anniversary.entity.Anniversary; +import com.example.fiurinee.domain.member.entity.Member; +import jakarta.mail.MessagingException; +import jakarta.mail.internet.MimeMessage; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.springframework.stereotype.Service; +import org.thymeleaf.context.Context; +import org.thymeleaf.spring6.SpringTemplateEngine; + +import java.util.Map; + +@Service +public class MailService { + @Autowired + private JavaMailSender mailSender; + + @Autowired + private SpringTemplateEngine templateEngine; + + public void sendEmail(String to, String subject, String templateName, Map variables) throws MessagingException { + MimeMessage message = mailSender.createMimeMessage(); + MimeMessageHelper helper = new MimeMessageHelper(message, true); + Context context = new Context(); + context.setVariables(variables); + String html = templateEngine.process(templateName, context); + + helper.setFrom("Fiurinee "); + helper.setTo(to); + helper.setSubject(subject); + helper.setText(html, true); + + mailSender.send(message); + } + + public void sendAnniversaryEmail(Member member, Anniversary anniversary) throws MessagingException { + String to = member.getEmail(); + String subject = "[Fiurinee]안녕하세요, Fiurinee입니다. 기념일을 축하드려요!"; + Map variables = Map.of( + "name", member.getName(), + "anniversaryName", anniversary.getName() + ); + sendEmail(to, subject, "email", variables); + } + + public void sendPreAnniversaryEmail(Member member, Anniversary anniversary) throws MessagingException { + String to = member.getEmail(); + String subject = "[Fiurinee] 안녕하세요, Fiurinee입니다. 곧 다가올 기념일을 준비하세요!"; + Map variables = Map.of( + "name", member.getName(), + "anniversaryName", anniversary.getName() + ); + sendEmail(to, subject, "pre-email", variables); + } +} + diff --git a/src/main/java/com/example/fiurinee/domain/member/service/MemberService.java b/src/main/java/com/example/fiurinee/domain/member/service/MemberService.java index 596a666..8c44ede 100644 --- a/src/main/java/com/example/fiurinee/domain/member/service/MemberService.java +++ b/src/main/java/com/example/fiurinee/domain/member/service/MemberService.java @@ -9,6 +9,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; + @Service @Transactional(readOnly = true) @RequiredArgsConstructor @@ -37,5 +39,9 @@ public MemberResponseDTO getMemberDtoById(Long id) { return MemberResponseDTO.of(member); } + public List findAll() { + return memberRepository.findAll(); + } + } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index e2241e4..5f6a89b 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -41,9 +41,24 @@ spring: - profile_nickname - account_email + mail: + host: smtp.gmail.com + port: 587 + username: wlgusqor12@gmail.com + password: tbyiilhblgupacvg + properties: + mail: + smtp: + auth: true + starttls: + enable: true + logging.level: org.hibernate.SQL: debug org.hibernate.type: trace + org.springframework.mail: DEBUG + com.sun.mail.smtp: DEBUG + cloud: aws: @@ -59,3 +74,5 @@ cloud: jwt: secret-key: ${JWT_SECRET_KEY} + + diff --git a/src/main/resources/templates/email.html b/src/main/resources/templates/email.html new file mode 100644 index 0000000..47b187a --- /dev/null +++ b/src/main/resources/templates/email.html @@ -0,0 +1,35 @@ + + + + + D-Day Notification + + + +
+
+

[Fiurinee] 기념일 알림

+
+
+

안녕하세요.

+

소중한 사람들에게 선물할 꽃을 추천해주는 플랫폼 Fiurinee입니다.

+

+

+

기념일에 꽃을 추천 받아 보세요!

+
+
+

Fiurinee

+
+
+ + + + + + + diff --git a/src/main/resources/templates/pre-email.html b/src/main/resources/templates/pre-email.html new file mode 100644 index 0000000..f985eae --- /dev/null +++ b/src/main/resources/templates/pre-email.html @@ -0,0 +1,30 @@ + + + + + D-Day Notification + + + +
+
+

[Fiurinee] 기념일 알림

+
+
+

안녕하세요.

+

소중한 사람들에게 선물할 꽃을 추천해주는 플랫폼 Fiurinee입니다.

+

+

+

기념일에 꽃을 추천 받아 보세요!

+
+
+

Fiurinee

+
+
+ +