From d6e9b5d28156ce3c526f8d7df285ea52aa43bcd3 Mon Sep 17 00:00:00 2001 From: akirwana Date: Tue, 2 Sep 2025 10:37:30 -0600 Subject: [PATCH] Endpoints VV --- build.gradle | 2 + .../infowall/servicio/InfowallServicio.java | 10 +- .../gob/mx/vv/controlador/VvControlador.java | 76 ++++++ .../jumapacelaya/gob/mx/vv/dto/citaDTO.java | 37 +++ .../gob/mx/vv/dto/horarioDisponibleDTO.java | 10 + .../gob/mx/vv/dto/tramiteDTO.java | 32 +++ .../gob/mx/vv/repositorio/VvRepositorio.java | 5 + .../gob/mx/vv/servicio/VvServicio.java | 253 ++++++++++++++++++ src/main/resources/application.yml | 11 + 9 files changed, 429 insertions(+), 7 deletions(-) create mode 100644 src/main/java/jumapacelaya/gob/mx/vv/controlador/VvControlador.java create mode 100644 src/main/java/jumapacelaya/gob/mx/vv/dto/citaDTO.java create mode 100644 src/main/java/jumapacelaya/gob/mx/vv/dto/horarioDisponibleDTO.java create mode 100644 src/main/java/jumapacelaya/gob/mx/vv/dto/tramiteDTO.java create mode 100644 src/main/java/jumapacelaya/gob/mx/vv/repositorio/VvRepositorio.java create mode 100644 src/main/java/jumapacelaya/gob/mx/vv/servicio/VvServicio.java diff --git a/build.gradle b/build.gradle index e1f611f..f90f57a 100644 --- a/build.gradle +++ b/build.gradle @@ -36,6 +36,8 @@ dependencies { annotationProcessor("io.micronaut.data:micronaut-data-processor") implementation("io.micronaut.data:micronaut-data-jpa") implementation("io.micronaut.data:micronaut-data-hibernate-jpa") + implementation("io.micronaut.email:micronaut-email:2.2.1") + implementation("io.micronaut.email:micronaut-email-javamail:2.2.1") } diff --git a/src/main/java/jumapacelaya/gob/mx/infowall/servicio/InfowallServicio.java b/src/main/java/jumapacelaya/gob/mx/infowall/servicio/InfowallServicio.java index fea9cde..705f144 100644 --- a/src/main/java/jumapacelaya/gob/mx/infowall/servicio/InfowallServicio.java +++ b/src/main/java/jumapacelaya/gob/mx/infowall/servicio/InfowallServicio.java @@ -9,13 +9,10 @@ import java.util.List; import java.util.stream.Collectors; import javax.sql.DataSource; - -import io.micronaut.data.annotation.Query; -import io.micronaut.data.jdbc.annotation.JdbcRepository; -import io.micronaut.data.model.query.builder.sql.Dialect; -import io.micronaut.data.repository.CrudRepository; -import io.micronaut.transaction.annotation.Transactional; import jakarta.inject.Singleton; +import io.micronaut.transaction.annotation.Transactional; +import jumapacelaya.gob.mx.infowall.repositorio.InfowallRepositorio; + import jumapacelaya.gob.mx.infowall.dto.cobFacVtaDiaDTO; import jumapacelaya.gob.mx.infowall.dto.compCvDTO; import jumapacelaya.gob.mx.infowall.dto.otReconexDiaDTO; @@ -23,7 +20,6 @@ import jumapacelaya.gob.mx.infowall.dto.otSuspensionDTO; import jumapacelaya.gob.mx.infowall.dto.totalCobrosHoyDTO; import jumapacelaya.gob.mx.infowall.dto.totalPagosHoyDTO; import jumapacelaya.gob.mx.infowall.dto.totalPagosMesDTO; -import jumapacelaya.gob.mx.infowall.repositorio.InfowallRepositorio; @Singleton public class InfowallServicio { diff --git a/src/main/java/jumapacelaya/gob/mx/vv/controlador/VvControlador.java b/src/main/java/jumapacelaya/gob/mx/vv/controlador/VvControlador.java new file mode 100644 index 0000000..b37a30a --- /dev/null +++ b/src/main/java/jumapacelaya/gob/mx/vv/controlador/VvControlador.java @@ -0,0 +1,76 @@ +package jumapacelaya.gob.mx.vv.controlador; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import io.micronaut.http.HttpResponse; +import io.micronaut.http.MediaType; +import io.micronaut.http.annotation.Body; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Get; +import io.micronaut.http.annotation.PathVariable; +import io.micronaut.http.annotation.Post; +import jakarta.inject.Inject; +import jumapacelaya.gob.mx.vv.dto.citaDTO; +import jumapacelaya.gob.mx.vv.dto.horarioDisponibleDTO; +import jumapacelaya.gob.mx.vv.dto.tramiteDTO; +import jumapacelaya.gob.mx.vv.servicio.VvServicio; + +@Controller("/vv") +public class VvControlador { + + @Inject + VvServicio servicio; + + @Get("/citas/getdiasdisponibles/{punto}/{dias}") + public HttpResponse obtenerDiasDisponibles(@PathVariable Integer punto, @PathVariable Integer dias) { + try { + return HttpResponse.ok(servicio.obtenerDiasDisponibles(punto, dias)); + } catch (Exception e) { + throw new RuntimeException("Error al obtener días disponibles", e); + } + } + + @Get("/citas/gethorariosdisponibles/{punto}/{dia}") + public List obtenerHorariosDisponibles(Integer punto, String dia) { + return servicio.obtenerHorariosDisponibles(punto, dia); + } + + @Post(uri = "/citas/guardar", consumes = MediaType.APPLICATION_JSON) + public HttpResponse guardarCita(@Body Map body) { + try { + int puntoId = Integer.parseInt(body.get("ppuntoid").toString()); + int tipoTramite = Integer.parseInt(body.get("ptipotramite").toString()); + String dia = body.get("pdia").toString(); // formato: dd/mm/yyyy + String horario = body.get("phorario").toString(); // formato: hh:mm + String telefono = body.get("ptelefono").toString(); + String email = body.get("pemail").toString(); + + String resultado = servicio.guardarCita(puntoId, tipoTramite, dia, horario, telefono, email); + + return HttpResponse.ok(Map.of( + "status", "200", + "message", "Cita generada", + "data", resultado + )); + + } catch (Exception e) { + return HttpResponse.serverError(Map.of( + "status", "500", + "message", "Error al generar cita", + "error", e.getMessage() + )); + } + } + + @Post("/citas/tramites/guardar") + public HttpResponse guardarTramite(@Body tramiteDTO tramite) { + try { + String mensaje = servicio.guardarTramite(tramite); + return HttpResponse.ok().body(mensaje); + } catch (Exception e) { + return HttpResponse.serverError("Error al guardar trámite: " + e.getMessage()); + } + } +} diff --git a/src/main/java/jumapacelaya/gob/mx/vv/dto/citaDTO.java b/src/main/java/jumapacelaya/gob/mx/vv/dto/citaDTO.java new file mode 100644 index 0000000..9dfff63 --- /dev/null +++ b/src/main/java/jumapacelaya/gob/mx/vv/dto/citaDTO.java @@ -0,0 +1,37 @@ +package jumapacelaya.gob.mx.vv.dto; + +import jakarta.validation.constraints.NotBlank; + +public class citaDTO { + @NotBlank + private Integer ppuntoid; + @NotBlank + private String ptipotramite; + @NotBlank + private String pdia; + @NotBlank + private String phorario; + @NotBlank + private String ptelefono; + @NotBlank + private String pemail; + + // Getters y setters + public Integer getPpuntoid() { return ppuntoid; } + public void setPpuntoid(Integer ppuntoid) { this.ppuntoid = ppuntoid; } + + public String getPtipotramite() { return ptipotramite; } + public void setPtipotramite(String ptipotramite) { this.ptipotramite = ptipotramite; } + + public String getPdia() { return pdia; } + public void setPdia(String pdia) { this.pdia = pdia; } + + public String getPhorario() { return phorario; } + public void setPhorario(String phorario) { this.phorario = phorario; } + + public String getPtelefono() { return ptelefono; } + public void setPtelefono(String ptelefono) { this.ptelefono = ptelefono; } + + public String getPemail() { return pemail; } + public void setPemail(String pemail) { this.pemail = pemail; } +} diff --git a/src/main/java/jumapacelaya/gob/mx/vv/dto/horarioDisponibleDTO.java b/src/main/java/jumapacelaya/gob/mx/vv/dto/horarioDisponibleDTO.java new file mode 100644 index 0000000..87deb95 --- /dev/null +++ b/src/main/java/jumapacelaya/gob/mx/vv/dto/horarioDisponibleDTO.java @@ -0,0 +1,10 @@ +package jumapacelaya.gob.mx.vv.dto; + +public class horarioDisponibleDTO { + private String horariosdisponibles; + + public String getHorariosdisponibles() { return horariosdisponibles; } + public void setHorariosdisponibles(String horariosdisponibles) { + this.horariosdisponibles = horariosdisponibles; + } +} diff --git a/src/main/java/jumapacelaya/gob/mx/vv/dto/tramiteDTO.java b/src/main/java/jumapacelaya/gob/mx/vv/dto/tramiteDTO.java new file mode 100644 index 0000000..4e08de0 --- /dev/null +++ b/src/main/java/jumapacelaya/gob/mx/vv/dto/tramiteDTO.java @@ -0,0 +1,32 @@ +package jumapacelaya.gob.mx.vv.dto; + +import io.micronaut.core.annotation.Introspected; + +@Introspected +public class tramiteDTO { + + private int tipoTramite; + private int usuarioId; + private String telefono; + private Long predioId; + + public int getTipoTramite() { return tipoTramite; } + public void setTipoTramite(int tipoTramite) { + this.tipoTramite = tipoTramite; + } + + public int getUsuarioId() { return usuarioId; } + public void setUsuarioId(int usuarioId) { + this.usuarioId = usuarioId; + } + + public String getTelefono() { return telefono; } + public void setTelefono(String telefono) { + this.telefono = telefono; + } + + public Long getPredioId() { return predioId; } + public void setPredioId(Long predioId) { + this.predioId = predioId; + } +} diff --git a/src/main/java/jumapacelaya/gob/mx/vv/repositorio/VvRepositorio.java b/src/main/java/jumapacelaya/gob/mx/vv/repositorio/VvRepositorio.java new file mode 100644 index 0000000..d295857 --- /dev/null +++ b/src/main/java/jumapacelaya/gob/mx/vv/repositorio/VvRepositorio.java @@ -0,0 +1,5 @@ +package jumapacelaya.gob.mx.vv.repositorio; + +public class VvRepositorio { + +} diff --git a/src/main/java/jumapacelaya/gob/mx/vv/servicio/VvServicio.java b/src/main/java/jumapacelaya/gob/mx/vv/servicio/VvServicio.java new file mode 100644 index 0000000..a46c29e --- /dev/null +++ b/src/main/java/jumapacelaya/gob/mx/vv/servicio/VvServicio.java @@ -0,0 +1,253 @@ +package jumapacelaya.gob.mx.vv.servicio; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import javax.sql.DataSource; + +import jakarta.inject.Singleton; +import io.micronaut.transaction.annotation.ReadOnly; +import io.micronaut.transaction.annotation.Transactional; +import jumapacelaya.gob.mx.vv.dto.citaDTO; +import jumapacelaya.gob.mx.vv.dto.horarioDisponibleDTO; +import jumapacelaya.gob.mx.vv.dto.tramiteDTO; +import jumapacelaya.gob.mx.vv.repositorio.VvRepositorio; + +@Singleton +public class VvServicio { + + private final DataSource dataSource; + + public VvServicio(DataSource dataSource) { + this.dataSource = dataSource; + } + + @Transactional + public List> obtenerDiasDisponibles(Integer punto, Integer dias) { + List> resultado = new ArrayList<>(); + + String query = + "with dias as ( " + + " select sysdate as hoy, level as diasfaltantes, trunc(sysdate + level) as fechacita " + + " from dual connect by level <= ? " + + ") " + + "select count(c.citaid) as citas, " + + " to_char(d.fechacita, 'dd/mm/yyyy', 'NLS_DATE_LANGUAGE = SPANISH') as fechacita, " + + " to_char(d.fechacita, 'fmDY', 'NLS_DATE_LANGUAGE = SPANISH') || ' ' || " + + " to_char(d.fechacita, 'dd/mm/yyyy', 'NLS_DATE_LANGUAGE = SPANISH') as sfechacita " + + "from dias d " + + "left join appmovcom.citas c on (trunc(c.fechora) = d.fechacita and c.estado in ('S', 'P') and c.puntoid = ?) " + + "where d.fechacita not in (select fecha from diasfestivos) " + + " and to_char(d.fechacita, 'fmDY', 'NLS_DATE_LANGUAGE = SPANISH') not in ('SÁB', 'DOM') " + + "group by d.fechacita " + + "order by d.fechacita"; + + try (Connection connection = dataSource.getConnection(); + PreparedStatement stmt = connection.prepareStatement(query)) { + + stmt.setInt(1, dias); + stmt.setInt(2, punto); + + try (ResultSet rs = stmt.executeQuery()) { + ResultSetMetaData meta = rs.getMetaData(); + int cols = meta.getColumnCount(); + + while (rs.next()) { + Map fila = new HashMap<>(); + for (int i = 1; i <= cols; i++) { + fila.put(meta.getColumnLabel(i).toLowerCase(), rs.getObject(i)); + } + resultado.add(fila); + } + } + + } catch (SQLException e) { + throw new RuntimeException("Error al obtener días disponibles", e); + } + + return resultado; + } + + @ReadOnly + public List obtenerHorariosDisponibles(Integer punto, String dia) { + List lista = new ArrayList<>(); + + String horarios = "09:30,10:00,10:30,11:00,11:30,12:00,12:30,13:00,13:30,14:00,14:30"; + + String query = """ + with horarios as ( + select to_char(fechora, 'hh24:mi', 'NLS_DATE_LANGUAGE = SPANISH') hora + from appmovcom.citas + where trunc(fechora) = trunc(to_date(?, 'ddmmyyyy')) + and puntoid = ? + ) + select column_value horariosdisponibles + from table(cobranza.splitvlt(?, ',')) + where column_value not in (select * from horarios) + order by column_value + """; + + try (Connection conn = dataSource.getConnection(); + PreparedStatement ps = conn.prepareStatement(query)) { + + ps.setString(1, dia); + ps.setInt(2, punto); + ps.setString(3, horarios); + + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + horarioDisponibleDTO dto = new horarioDisponibleDTO(); + dto.setHorariosdisponibles(rs.getString("horariosdisponibles")); + lista.add(dto); + } + } catch (SQLException e) { + throw new RuntimeException("Error al obtener horarios disponibles", e); + } + + return lista; + } + + @Transactional + public String guardarCita(int puntoId, int tipoTramite, String dia, String horario, String telefono, String email) { + String fechaCita = dia + " " + horario; + String citaIdExistente = obtenerCitaExistente(puntoId, dia, horario); + + if (citaIdExistente != null) { + return "Ya existe una cita en ese horario. Revisa disponibilidad nuevamente."; + } + + String folioGenerado = generarFolio(); + + String insertQuery = "INSERT INTO appmovcom.citas (citaid, puntoid, fechora, tipotramid, email, telefono) " + + "VALUES (?, ?, TO_DATE(?, 'dd/mm/yyyy hh24:mi'), ?, ?, ?)"; + + try (Connection connection = dataSource.getConnection(); + PreparedStatement ps = connection.prepareStatement(insertQuery)) { + + ps.setString(1, folioGenerado); + ps.setInt(2, puntoId); + ps.setString(3, fechaCita); + ps.setInt(4, tipoTramite); + ps.setString(5, email); + ps.setString(6, telefono); + + ps.executeUpdate(); + + } catch (Exception e) { + throw new RuntimeException("Error al guardar cita: " + e.getMessage(), e); + } + + return "Se generó la cita con folio " + folioGenerado + " para la oficina " + nombrePunto(puntoId) + + " el día " + dia + " a las " + horario + " hrs."; + } + + private String obtenerCitaExistente(int puntoId, String dia, String horario) { + String query = "SELECT citaid FROM appmovcom.citas WHERE puntoid = ? AND trunc(fechora) = TO_DATE(?, 'dd/mm/yyyy') AND TO_CHAR(fechora, 'hh24:mi') = ?"; + try (Connection conn = dataSource.getConnection(); + PreparedStatement ps = conn.prepareStatement(query)) { + + ps.setInt(1, puntoId); + ps.setString(2, dia); + ps.setString(3, horario); + + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + return rs.getString("citaid"); + } + } + + } catch (Exception e) { + throw new RuntimeException("Error al verificar cita existente: " + e.getMessage(), e); + } + return null; + } + + private String generarFolio() { + String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + StringBuilder sb = new StringBuilder(); + Random random = new Random(); + while (sb.length() < 7) { + sb.append(chars.charAt(random.nextInt(chars.length()))); + } + return sb.toString(); + } + + private String nombrePunto(int puntoId) { + return switch (puntoId) { + case 1 -> "División del Norte"; + case 2 -> "Parque Morelos"; + case 3 -> "Cañitos"; + case 5 -> "Parque Celaya"; + default -> "Desconocida"; + }; + } + + @Transactional + public String guardarTramite(tramiteDTO tramite) { + String tramiteExistente = obtenerTramite(tramite.getUsuarioId(), tramite.getTipoTramite()); + + if (tramiteExistente != null) { + return "Ya hay un trámite registrado para este usuario y tipo. Revisa tu email para dar seguimiento."; + } + + String folio = generarFolio(); + + String insertQuery = """ + INSERT INTO appmovcom.tramites ( + tramiteid, tipotramid, usuarioid, email, telefono, estatus, predioid + ) VALUES ( + ?, ?, ?, (SELECT email FROM appmovcom.usuarios WHERE usuarioid = ?), ?, 'NUEVO', ? + ) + """; + + try (Connection conn = dataSource.getConnection(); + PreparedStatement ps = conn.prepareStatement(insertQuery)) { + + ps.setString(1, folio); + ps.setInt(2, tramite.getTipoTramite()); + ps.setInt(3, tramite.getUsuarioId()); + ps.setInt(4, tramite.getUsuarioId()); + ps.setString(5, tramite.getTelefono()); + ps.setLong(6, tramite.getPredioId()); + + ps.executeUpdate(); + + } catch (SQLException e) { + throw new RuntimeException("Error al guardar trámite: " + e.getMessage(), e); + } + + // Envío de correo (si lo deseas activar más adelante) + // enviaCorreo(folio, "G"); + + return "Se generó el Trámite con folio " + folio + ". Se te enviará un correo con la información necesaria."; + } + + private String obtenerTramite(int usuarioId, int tipoTramite) { + String query = "SELECT tramiteid FROM appmovcom.tramites WHERE usuarioid = ? AND tipotramid = ?"; + try (Connection conn = dataSource.getConnection(); + PreparedStatement ps = conn.prepareStatement(query)) { + + ps.setInt(1, usuarioId); + ps.setInt(2, tipoTramite); + + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + return rs.getString("tramiteid"); + } + } + + } catch (SQLException e) { + throw new RuntimeException("Error al verificar trámite existente: " + e.getMessage(), e); + } + return null; + } + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 2e5afb1..8e241bf 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,6 +1,17 @@ micronaut: application: name: API_ePay_Micronaut + email: + from: noreply + smtp: + auth: true + starttls.enable: true + host: smtp.mailtrap.io + port: 2525 + username: ${SMTP_USERNAME} + password: ${SMTP_PASSWORD} + ssl: false + tls: true datasources: default: