diff --git a/src/main/java/ar/utn/ba/ddsi/mailing/controllers/EmailController.java b/src/main/java/ar/utn/ba/ddsi/mailing/controllers/EmailController.java index fec8ee0..0096dec 100644 --- a/src/main/java/ar/utn/ba/ddsi/mailing/controllers/EmailController.java +++ b/src/main/java/ar/utn/ba/ddsi/mailing/controllers/EmailController.java @@ -23,4 +23,5 @@ public Email crearEmail(@RequestBody Email email) { public List obtenerEmails(@RequestParam(required = false) Boolean pendiente) { return emailService.obtenerEmails(pendiente); } -} \ No newline at end of file +} + diff --git a/src/main/java/ar/utn/ba/ddsi/mailing/models/adapters/IEmailAdapter.java b/src/main/java/ar/utn/ba/ddsi/mailing/models/adapters/IEmailAdapter.java new file mode 100644 index 0000000..19741ba --- /dev/null +++ b/src/main/java/ar/utn/ba/ddsi/mailing/models/adapters/IEmailAdapter.java @@ -0,0 +1,7 @@ +package ar.utn.ba.ddsi.mailing.models.adapters; + +import ar.utn.ba.ddsi.mailing.models.entities.Email; + +public interface IEmailAdapter { + public void enviar(Email email); +} diff --git a/src/main/java/ar/utn/ba/ddsi/mailing/models/adapters/impl/EmailAdapter.java b/src/main/java/ar/utn/ba/ddsi/mailing/models/adapters/impl/EmailAdapter.java new file mode 100644 index 0000000..f025ae0 --- /dev/null +++ b/src/main/java/ar/utn/ba/ddsi/mailing/models/adapters/impl/EmailAdapter.java @@ -0,0 +1,14 @@ +package ar.utn.ba.ddsi.mailing.models.adapters.impl; + +import ar.utn.ba.ddsi.mailing.models.adapters.IEmailAdapter; +import ar.utn.ba.ddsi.mailing.models.entities.Email; + +public class EmailAdapter implements IEmailAdapter { + + @Override + public void enviar(Email email) { + // Simulación de envío + System.out.printf("Email enviado a %s con asunto '%s'%n", + email.getDestinatario(), email.getAsunto()); + } +} diff --git a/src/main/java/ar/utn/ba/ddsi/mailing/models/dtos/EmailInputDTO.java b/src/main/java/ar/utn/ba/ddsi/mailing/models/dtos/EmailInputDTO.java new file mode 100644 index 0000000..43d0875 --- /dev/null +++ b/src/main/java/ar/utn/ba/ddsi/mailing/models/dtos/EmailInputDTO.java @@ -0,0 +1,13 @@ +package ar.utn.ba.ddsi.mailing.models.dtos; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class EmailInputDTO { + private String destinatario; + private String asunto; + private String contenido; +} + diff --git a/src/main/java/ar/utn/ba/ddsi/mailing/models/dtos/EmailOutputDTO.java b/src/main/java/ar/utn/ba/ddsi/mailing/models/dtos/EmailOutputDTO.java new file mode 100644 index 0000000..ea84e6c --- /dev/null +++ b/src/main/java/ar/utn/ba/ddsi/mailing/models/dtos/EmailOutputDTO.java @@ -0,0 +1,16 @@ +package ar.utn.ba.ddsi.mailing.models.dtos; + +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +public class EmailOutputDTO { + private Long id; + private String destinatario; + private String remitente; + private String asunto; + private String contenido; + private boolean enviado; + +} diff --git a/src/main/java/ar/utn/ba/ddsi/mailing/models/entities/Alerta.java b/src/main/java/ar/utn/ba/ddsi/mailing/models/entities/Alerta.java new file mode 100644 index 0000000..3f120db --- /dev/null +++ b/src/main/java/ar/utn/ba/ddsi/mailing/models/entities/Alerta.java @@ -0,0 +1,39 @@ +package ar.utn.ba.ddsi.mailing.models.entities; + +import lombok.Getter; + +import java.time.LocalDateTime; + +@Getter +public class Alerta { + private final Clima clima; + private final String mensaje; + private final LocalDateTime fechaGeneracion; + + public Alerta(Clima clima) { + this.clima = clima; + this.fechaGeneracion = LocalDateTime.now(); + this.mensaje = generarMensaje(clima); + } + + private String generarMensaje(Clima clima) { + return String.format( + "ALERTA: Condiciones climáticas extremas detectadas en %s\n\n" + + "Temperatura: %.1f°C\n" + + "Humedad: %d%%\n" + + "Condición: %s\n" + + "Velocidad del viento: %.1f km/h\n\n" + + "Se recomienda tomar precauciones.", + clima.getCiudad(), + clima.getTemperaturaCelsius(), + clima.getHumedad(), + clima.getCondicion(), + clima.getVelocidadVientoKmh() + ); + } + + public Email generarEmail(String remitente, String destinatario) { + String asunto = "Alerta de Clima - Condiciones Extremas"; + return new Email(destinatario, remitente, asunto, mensaje); + } +} diff --git a/src/main/java/ar/utn/ba/ddsi/mailing/models/entities/Ciudad.java b/src/main/java/ar/utn/ba/ddsi/mailing/models/entities/Ciudad.java new file mode 100644 index 0000000..3786985 --- /dev/null +++ b/src/main/java/ar/utn/ba/ddsi/mailing/models/entities/Ciudad.java @@ -0,0 +1,18 @@ +package ar.utn.ba.ddsi.mailing.models.entities; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class Ciudad { + private String nombre; + private String region; + private String pais; + + public Ciudad(String nombre, String region, String pais) { + this.nombre = nombre; + this.region = region; + this.pais = pais; + } +} diff --git a/src/main/java/ar/utn/ba/ddsi/mailing/models/entities/Clima.java b/src/main/java/ar/utn/ba/ddsi/mailing/models/entities/Clima.java index 4eaa65b..9f4f316 100644 --- a/src/main/java/ar/utn/ba/ddsi/mailing/models/entities/Clima.java +++ b/src/main/java/ar/utn/ba/ddsi/mailing/models/entities/Clima.java @@ -8,9 +8,7 @@ @Setter public class Clima { private Long id; - private String ciudad; - private String region; - private String pais; + private Ciudad ciudad; private Double temperaturaCelsius; private Double temperaturaFahrenheit; private String condicion; diff --git a/src/main/java/ar/utn/ba/ddsi/mailing/models/entities/CondicionAlerta.java b/src/main/java/ar/utn/ba/ddsi/mailing/models/entities/CondicionAlerta.java new file mode 100644 index 0000000..e149169 --- /dev/null +++ b/src/main/java/ar/utn/ba/ddsi/mailing/models/entities/CondicionAlerta.java @@ -0,0 +1,16 @@ +package ar.utn.ba.ddsi.mailing.models.entities; + +public class CondicionAlerta { + private double temperaturaAlerta; + private int humedadAlerta; + + public CondicionAlerta(double temperaturaAlerta, int humedadAlerta) { + this.temperaturaAlerta = temperaturaAlerta; + this.humedadAlerta = humedadAlerta; + } + + public boolean cumple(Clima clima) { + return clima.getTemperaturaCelsius() > temperaturaAlerta && + clima.getHumedad() > humedadAlerta; + } +} diff --git a/src/main/java/ar/utn/ba/ddsi/mailing/models/entities/Email.java b/src/main/java/ar/utn/ba/ddsi/mailing/models/entities/Email.java index eb940be..5a40575 100644 --- a/src/main/java/ar/utn/ba/ddsi/mailing/models/entities/Email.java +++ b/src/main/java/ar/utn/ba/ddsi/mailing/models/entities/Email.java @@ -1,5 +1,6 @@ package ar.utn.ba.ddsi.mailing.models.entities; +import ar.utn.ba.ddsi.mailing.models.adapters.IEmailAdapter; import lombok.Getter; import lombok.Setter; @@ -13,6 +14,8 @@ public class Email { private String contenido; private boolean enviado; + private IEmailAdapter emailAdapter; + public Email(String destinatario, String remitente, String asunto, String contenido) { this.destinatario = destinatario; this.remitente = remitente; @@ -22,6 +25,12 @@ public Email(String destinatario, String remitente, String asunto, String conten } public void enviar() { - //TODO: Implementación pendiente. Podríamos usar adapters + + if (emailAdapter == null) { + throw new IllegalStateException("No se configuró el adaptador del email"); + } + emailAdapter.enviar(this); + this.enviado = true; } -} \ No newline at end of file +} + diff --git a/src/main/java/ar/utn/ba/ddsi/mailing/services/impl/AlertasService.java b/src/main/java/ar/utn/ba/ddsi/mailing/services/impl/AlertasService.java index f5d52b6..0587ef7 100644 --- a/src/main/java/ar/utn/ba/ddsi/mailing/services/impl/AlertasService.java +++ b/src/main/java/ar/utn/ba/ddsi/mailing/services/impl/AlertasService.java @@ -1,6 +1,8 @@ package ar.utn.ba.ddsi.mailing.services.impl; import ar.utn.ba.ddsi.mailing.models.entities.Clima; +import ar.utn.ba.ddsi.mailing.models.entities.Alerta; +import ar.utn.ba.ddsi.mailing.models.entities.CondicionAlerta; import ar.utn.ba.ddsi.mailing.models.entities.Email; import ar.utn.ba.ddsi.mailing.models.repositories.IClimaRepository; import ar.utn.ba.ddsi.mailing.services.IAlertasService; @@ -15,23 +17,29 @@ @Service public class AlertasService implements IAlertasService { private static final Logger logger = LoggerFactory.getLogger(AlertasService.class); - private static final double TEMPERATURA_ALERTA = 35.0; - private static final int HUMEDAD_ALERTA = 60; private final IClimaRepository climaRepository; private final EmailService emailService; private final String remitente; private final List destinatarios; + private final CondicionAlerta condicionAlerta; + + // double TEMPERATURA_ALERTA = 35.0; + // private static final int HUMEDAD_ALERTA = 60; public AlertasService( - IClimaRepository climaRepository, - EmailService emailService, - @Value("${email.alertas.remitente}") String remitente, - @Value("${email.alertas.destinatarios}") String destinatarios) { + IClimaRepository climaRepository, + EmailService emailService, + @Value("${email.alertas.remitente}") String remitente, + @Value("${email.alertas.destinatarios}") String destinatarios, + @Value("${alerta.temperatura.minima}") double tempMin, + @Value("${alerta.humedad.minima}") int humedadMin) + { this.climaRepository = climaRepository; this.emailService = emailService; this.remitente = remitente; this.destinatarios = Arrays.asList(destinatarios.split(",")); + this.condicionAlerta = new CondicionAlerta(tempMin, humedadMin); } @Override @@ -43,15 +51,16 @@ public Mono generarAlertasYAvisar() { }) .flatMap(climas -> { climas.stream() - .filter(this::cumpleCondicionesAlerta) - .forEach(this::generarYEnviarEmail); - + .filter(condicionAlerta::cumple) + .map(Alerta::new) + .forEach(this::enviarAlertaPorEmail); + // Marcar todos como procesados climas.forEach(clima -> { clima.setProcesado(true); climaRepository.save(clima); }); - + return Mono.empty(); }) .onErrorResume(e -> { @@ -61,34 +70,13 @@ public Mono generarAlertasYAvisar() { .then(); } - private boolean cumpleCondicionesAlerta(Clima clima) { - //TODO: podríamos refactorizar el diseño para que no sea un simple método, pues puede ser más complejo - return clima.getTemperaturaCelsius() > TEMPERATURA_ALERTA && - clima.getHumedad() > HUMEDAD_ALERTA; - } - - private void generarYEnviarEmail(Clima clima) { - String asunto = "Alerta de Clima - Condiciones Extremas"; - String mensaje = String.format( - "ALERTA: Condiciones climáticas extremas detectadas en %s\n\n" + - "Temperatura: %.1f°C\n" + - "Humedad: %d%%\n" + - "Condición: %s\n" + - "Velocidad del viento: %.1f km/h\n\n" + - "Se recomienda tomar precauciones.", - clima.getCiudad(), - clima.getTemperaturaCelsius(), - clima.getHumedad(), - clima.getCondicion(), - clima.getVelocidadVientoKmh() - ); - + private void enviarAlertaPorEmail(Alerta alerta) { for (String destinatario : destinatarios) { - Email email = new Email(destinatario, remitente, asunto, mensaje); + Email email = alerta.generarEmail(remitente, destinatario); emailService.crearEmail(email); } - - logger.info("Email de alerta generado para {} - Enviado a {} destinatarios", - clima.getCiudad(), destinatarios.size()); + + logger.info("Alerta generada para {} - Emails creados para {} destinatarios", + alerta.getClima().getCiudad(), destinatarios.size()); } -} \ No newline at end of file +} \ No newline at end of file diff --git a/src/main/java/ar/utn/ba/ddsi/mailing/services/impl/ClimaService.java b/src/main/java/ar/utn/ba/ddsi/mailing/services/impl/ClimaService.java index d2296b4..33245ae 100644 --- a/src/main/java/ar/utn/ba/ddsi/mailing/services/impl/ClimaService.java +++ b/src/main/java/ar/utn/ba/ddsi/mailing/services/impl/ClimaService.java @@ -1,8 +1,9 @@ package ar.utn.ba.ddsi.mailing.services.impl; +import ar.utn.ba.ddsi.mailing.models.entities.Ciudad; import ar.utn.ba.ddsi.mailing.models.entities.Clima; -import ar.utn.ba.ddsi.mailing.models.repositories.IClimaRepository; import ar.utn.ba.ddsi.mailing.models.dto.external.weatherapi.WeatherResponse; +import ar.utn.ba.ddsi.mailing.models.repositories.IClimaRepository; import ar.utn.ba.ddsi.mailing.services.IClimaService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,33 +16,34 @@ @Service public class ClimaService implements IClimaService { private static final Logger logger = LoggerFactory.getLogger(ClimaService.class); - private static final String[] CIUDADES_ARGENTINA = { - "Buenos Aires", "Cordoba", "Rosario", "Mendoza", "Tucuman", - "La Plata", "Mar del Plata", "Salta", "Santa Fe", "San Juan" - }; private final IClimaRepository climaRepository; private final WebClient webClient; private final String apiKey; + private final String[] ciudadesArgentinas; - public ClimaService( - IClimaRepository climaRepository, - @Value("${weather.api.key}") String apiKey, - @Value("${weather.api.base-url}") String baseUrl) { - this.climaRepository = climaRepository; - this.apiKey = apiKey; - this.webClient = WebClient.builder() - .baseUrl(baseUrl) - .build(); + public ClimaService( + IClimaRepository climaRepository, + @Value("${weather.api.key}") String apiKey, + @Value("${weather.api.base-url}") String baseUrl, + @Value("${ciudades.argentina}") String[] ciudadesArgentinas + ) { + this.climaRepository = climaRepository; + this.apiKey = apiKey; + this.ciudadesArgentinas = ciudadesArgentinas; + + this.webClient = WebClient.builder() + .baseUrl(baseUrl) + .build(); } - @Override + @Override public Mono actualizarClimaCiudades() { - return Flux.fromArray(CIUDADES_ARGENTINA) + return Flux.fromArray(ciudadesArgentinas) .flatMap(this::obtenerClimaDeAPI) .flatMap(clima -> { climaRepository.save(clima); - logger.info("Clima actualizado para: {}", clima.getCiudad()); + logger.info("Clima actualizado para: {}", clima.getCiudad().getNombre()); return Mono.empty(); }) .onErrorResume(e -> { @@ -62,16 +64,20 @@ private Mono obtenerClimaDeAPI(String ciudad) { .retrieve() .bodyToMono(WeatherResponse.class) .map(response -> { - Clima clima = new Clima(); - clima.setCiudad(ciudad); - clima.setRegion(response.getLocation().getRegion()); - clima.setPais(response.getLocation().getCountry()); - clima.setTemperaturaCelsius(response.getCurrent().getTemp_c()); - clima.setTemperaturaFahrenheit(response.getCurrent().getTemp_f()); - clima.setCondicion(response.getCurrent().getCondition().getText()); - clima.setVelocidadVientoKmh(response.getCurrent().getWind_kph()); - clima.setHumedad(response.getCurrent().getHumidity()); - return clima; + Clima clima = new Clima(); + Ciudad ciudadObj = new Ciudad( + response.getLocation().getName(), + response.getLocation().getRegion(), + response.getLocation().getCountry() + ); + clima.setCiudad(ciudadObj); + clima.setTemperaturaCelsius(response.getCurrent().getTemp_c()); + clima.setTemperaturaFahrenheit(response.getCurrent().getTemp_f()); + clima.setCondicion(response.getCurrent().getCondition().getText()); + clima.setVelocidadVientoKmh(response.getCurrent().getWind_kph()); + clima.setHumedad(response.getCurrent().getHumidity()); + + return clima; }); } } \ No newline at end of file diff --git a/src/main/java/ar/utn/ba/ddsi/mailing/services/impl/EmailService.java b/src/main/java/ar/utn/ba/ddsi/mailing/services/impl/EmailService.java index e1ffb85..1ab0625 100644 --- a/src/main/java/ar/utn/ba/ddsi/mailing/services/impl/EmailService.java +++ b/src/main/java/ar/utn/ba/ddsi/mailing/services/impl/EmailService.java @@ -34,9 +34,13 @@ public List obtenerEmails(Boolean pendiente) { public void procesarPendientes() { List pendientes = emailRepository.findByEnviado(false); for (Email email : pendientes) { - email.enviar(); - email.setEnviado(true); - emailRepository.save(email); + try { + email.enviar(); + email.setEnviado(true); + emailRepository.save(email); + } catch (Exception e) { + logger.error("Error al enviar el email ID {}: {}", email.getId(), e.getMessage()); + } } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 6bcf441..99bdd7b 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -4,9 +4,12 @@ spring.application.name=climalert cron.expression=0 * * * * * # WeatherAPI Configuration -weather.api.key=API-KEY-EXAMPLE +weather.api.key=373aa442691542b5866230029250206 weather.api.base-url=http://api.weatherapi.com/v1 # Email Configuration email.alertas.remitente=alertas@clima.com email.alertas.destinatarios=admin@clima.com,emergencias@clima.com,meteorologia@clima.com + +ciudades.argentina=Buenos Aires,Cordoba,Rosario,Mendoza,Tucuman,La Plata,Mar del Plata,Salta,Santa Fe,San Juan +