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
Original file line number Diff line number Diff line change
@@ -1,48 +1,37 @@
package br.com.springnoobs.reminderapi.mail.engine;

import br.com.springnoobs.reminderapi.reminder.entity.Reminder;
import br.com.springnoobs.reminderapi.user.entity.Contact;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import org.slf4j.Logger;
import org.springframework.core.io.ClassPathResource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;

@Component
public class MailEngine {

Logger logger = org.slf4j.LoggerFactory.getLogger(MailEngine.class);

private final JavaMailSender mailSender;

private static final DateTimeFormatter DATE_FORMATTER =
DateTimeFormatter.ofPattern("dd/MM/yyyy").withZone(ZoneId.of("America/Sao_Paulo"));

public MailEngine(JavaMailSender mailSender) {
this.mailSender = mailSender;
}

public void sendEmail(Contact contact, Reminder reminder) {
public void sendEmail(Map<String, String> variables) {
try {
MimeMessage message = mailSender.createMimeMessage();

MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setTo(contact.getEmail());
helper.setSubject("Lembrete" + " - " + reminder.getTitle());
helper.setTo(variables.get("email"));
helper.setSubject(variables.get("subject"));

String mailTemplate = getEmailTemplateContent();

Map<String, String> variables = buildEmailVariables(contact, reminder, "#");

mailTemplate = replaceVariables(mailTemplate, variables);

helper.setText(mailTemplate, true);
Expand All @@ -57,8 +46,11 @@ private String replaceVariables(String template, Map<String, String> variables)
String result = template;

for (Map.Entry<String, String> entry : variables.entrySet()) {
String placeholder = "{{" + entry.getKey() + "}}";
result = result.replace(placeholder, entry.getValue());
String emailVariable = "{{" + entry.getKey() + "}}";

if (result.contains(emailVariable)) {
result = result.replace(emailVariable, entry.getValue());
}
}

return result;
Expand All @@ -69,15 +61,4 @@ private String getEmailTemplateContent() throws IOException {

return new String(resource.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
}

private Map<String, String> buildEmailVariables(Contact contact, Reminder reminder, String disableUrl) {
Map<String, String> map = new HashMap<>();

map.put("name", contact.getUser().getFirstName());
map.put("title", reminder.getTitle());
map.put("remind_at", DATE_FORMATTER.format(reminder.getRemindAt()));
map.put("disable_notification_url", disableUrl);

return map;
}
}
Comment thread
lucasbdourado marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package br.com.springnoobs.reminderapi.mail.service;

import br.com.springnoobs.reminderapi.mail.engine.MailEngine;
import br.com.springnoobs.reminderapi.reminder.entity.Reminder;
import br.com.springnoobs.reminderapi.user.entity.Contact;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import org.springframework.stereotype.Service;

@Service
public class EmailService {

private final MailEngine mailEngine;

private static final DateTimeFormatter DATE_FORMATTER =
DateTimeFormatter.ofPattern("dd/MM/yyyy").withZone(ZoneId.of("America/Sao_Paulo"));

public EmailService(MailEngine mailEngine) {
this.mailEngine = mailEngine;
}

public void send(Reminder reminder) {
if (reminder.getUser() == null) {
throw new RuntimeException("User not found");
}

Contact contact = reminder.getUser().getContact();

Map<String, String> variables = buildEmailVariables(contact, reminder, "#");

mailEngine.sendEmail(variables);
}

private Map<String, String> buildEmailVariables(Contact contact, Reminder reminder, String disableUrl) {
Map<String, String> map = new HashMap<>();

map.put("name", contact.getUser().getFirstName());
map.put("email", contact.getEmail());
map.put("title", reminder.getTitle());
map.put(
"remind_at",
reminder.getRemindAt() != null
? DATE_FORMATTER.format(reminder.getRemindAt())
: DATE_FORMATTER.format(reminder.getDueDate()));
map.put("due_date", DATE_FORMATTER.format(reminder.getDueDate()));
map.put("disable_notification_url", disableUrl);
map.put("subject", "Lembrete" + " - " + reminder.getTitle());

return map;
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package br.com.springnoobs.reminderapi.reminder.dto.request;

import br.com.springnoobs.reminderapi.user.dto.request.CreateUserRequestDTO;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.time.Instant;

public record CreateReminderRequestDTO(
@NotBlank(message = "Title must not be null") String title,
@NotNull(message = "DueDate must not be null") Instant dueDate) {}
@NotNull(message = "DueDate must not be null") Instant dueDate,
@NotNull(message = "User must not be null") CreateUserRequestDTO user) {}
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
package br.com.springnoobs.reminderapi.reminder.entity;

import jakarta.persistence.*;
import br.com.springnoobs.reminderapi.user.entity.User;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.time.Instant;

@Entity
Expand All @@ -22,6 +30,10 @@ public class Reminder {

private boolean sent = false;

@ManyToOne(optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;

public Reminder() {}

public Reminder(
Expand Down Expand Up @@ -96,4 +108,12 @@ public Instant getDueDate() {
public void setDueDate(Instant dueDate) {
this.dueDate = dueDate;
}

public User getUser() {
return user;
}

public void setUser(User user) {
this.user = user;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package br.com.springnoobs.reminderapi.reminder.scheduler;

import br.com.springnoobs.reminderapi.mail.service.EmailService;
import br.com.springnoobs.reminderapi.reminder.entity.Reminder;
import br.com.springnoobs.reminderapi.reminder.exception.NotFoundException;
import br.com.springnoobs.reminderapi.reminder.repository.ReminderRepository;
Expand All @@ -15,22 +16,25 @@ public class ReminderSchedulerService {

private final ReminderRepository repository;
private final TaskScheduler taskScheduler;
private final EmailService emailService;
private final Map<Long, ScheduledFuture<?>> scheduledTasks = new ConcurrentHashMap<>();

public ReminderSchedulerService(ReminderRepository repository, TaskScheduler taskScheduler) {
public ReminderSchedulerService(ReminderRepository repository, TaskScheduler taskScheduler, EmailService emailService) {
this.repository = repository;
this.taskScheduler = taskScheduler;
this.emailService = emailService;
}

public void createSchedule(Reminder reminder) {
ScheduledFuture<?> future = taskScheduler.schedule(
() -> {
emailService.send(reminder);
reminder.setExecutedAt(Instant.now());
reminder.setSent(true);
repository.save(reminder);
deleteReminderSchedule(reminder.getId());
},
reminder.getRemindAt());
reminder.getDueDate());

scheduledTasks.put(reminder.getId(), future);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,29 @@
import br.com.springnoobs.reminderapi.reminder.repository.ReminderRepository;
import br.com.springnoobs.reminderapi.reminder.scheduler.ReminderSchedulerService;
import java.time.Instant;

import br.com.springnoobs.reminderapi.user.entity.User;
import br.com.springnoobs.reminderapi.user.service.UserService;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class ReminderService {

private final ReminderRepository repository;
private final UserService userService;
private final ReminderSchedulerService reminderSchedulerService;

public ReminderService(ReminderRepository repository, ReminderSchedulerService reminderSchedulerService) {
public ReminderService(ReminderRepository repository, UserService userService, ReminderSchedulerService reminderSchedulerService) {
this.repository = repository;
this.userService = userService;
this.reminderSchedulerService = reminderSchedulerService;
}

@Transactional
public ReminderResponseDTO create(CreateReminderRequestDTO dto) {
if (dto.dueDate().isBefore(Instant.now())) {
throw new PastDueDateException("DueDate should be a date in the future!");
Expand All @@ -34,6 +41,10 @@ public ReminderResponseDTO create(CreateReminderRequestDTO dto) {
Reminder reminder = new Reminder();
BeanUtils.copyProperties(dto, reminder);

User user = userService.createAndSaveUser(dto.user());

reminder.setUser(user);

Reminder savedReminder = repository.save(reminder);
reminderSchedulerService.createSchedule(savedReminder);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package br.com.springnoobs.reminderapi.user.entity;

import br.com.springnoobs.reminderapi.reminder.entity.Reminder;
import jakarta.persistence.*;

import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name = "users")
public class User {
Expand All @@ -17,13 +21,17 @@ public class User {
@OneToOne(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private Contact contact;

@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Reminder> reminders = new ArrayList<>();

public User() {}

public User(Long id, String firstName, String lastName, Contact contact) {
public User(Long id, String firstName, String lastName, Contact contact, List<Reminder> reminders) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.contact = contact;
this.reminders = reminders;
}

public Long getId() {
Expand Down Expand Up @@ -57,4 +65,12 @@ public Contact getContact() {
public void setContact(Contact contact) {
this.contact = contact;
}

public List<Reminder> getReminders() {
return reminders;
}

public void setReminders(List<Reminder> reminders) {
this.reminders = reminders;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,9 @@ public UserService(UserRepository userRepository, ContactRepository contactRepos

@Transactional
public UserResponseDTO create(CreateUserRequestDTO dto) {
if (contactRepository.existsByEmail(dto.contactRequestDTO().email())) {
throw new EmailAlreadyExistsException("Email already exists");
}
User user = new User();
user.setFirstName(dto.firstName());
user.setLastName(dto.lastName());
User user = createAndSaveUser(dto);

User savedUser = repository.save(user);

Contact contact = new Contact();
contact.setEmail(dto.contactRequestDTO().email());
contact.setPhoneNumber(dto.contactRequestDTO().phoneNumber());
contact.setUser(savedUser);

contactRepository.save(contact);
savedUser.setContact(contact);
return UserMapper.toResponse(savedUser);
return UserMapper.toResponse(user);
}

public UserResponseDTO findByEmail(String email) {
Expand All @@ -63,4 +49,26 @@ public void delete(Long id) {
User user = repository.findById(id).orElseThrow(() -> new UserNotFoundException("User not found" + id));
repository.delete(user);
}

public User createAndSaveUser(CreateUserRequestDTO request) {
if (contactRepository.existsByEmail(request.contactRequestDTO().email())) {
throw new EmailAlreadyExistsException("Email already exists");
}

User user = new User();
user.setFirstName(request.firstName());
user.setLastName(request.lastName());

User savedUser = repository.save(user);

Contact contact = new Contact();
contact.setEmail(request.contactRequestDTO().email());
contact.setPhoneNumber(request.contactRequestDTO().phoneNumber());
contact.setUser(savedUser);

contactRepository.save(contact);
savedUser.setContact(contact);

return savedUser;
}
}
1 change: 1 addition & 0 deletions src/main/resources/templates/email/email-template.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ <h2 style="color:#2c7be5; margin-top:0;">🔔 Notificação de Lembrete</h2>
<div style="background:#f1f3f5; padding:15px; border-radius:6px;">
<p style="margin:0;"><strong>Título:</strong> {{title}}</p>
<p style="margin:0;"><strong>Data do Lembrete:</strong> {{remind_at}}</p>
<p style="margin:0;"><strong>Data do Vencimento:</strong> {{due_date}}</p>
</div>

<p style="margin-top:20px;">
Expand Down
Loading