Browse Source

Se agregaron validaciones para que las encuestas se puedan responder solo una vez por medio de tokens en el link de la encuesta asi como tambien tiempo de vida del link

main
mramirezg 2 weeks ago
parent
commit
6e68caeb80
7 changed files with 228 additions and 140 deletions
  1. +6
    -10
      src/main/java/mx/gob/jumapacelaya/models/encuestas/Respuesta.java
  2. +136
    -0
      src/main/java/mx/gob/jumapacelaya/services/DatabaseService.java
  3. +0
    -1
      src/main/java/mx/gob/jumapacelaya/services/EmailService.java
  4. +0
    -84
      src/main/java/mx/gob/jumapacelaya/services/encuestas/EncuestasDBService.java
  5. +36
    -6
      src/main/java/mx/gob/jumapacelaya/ui/DetallesMantView.java
  6. +41
    -37
      src/main/java/mx/gob/jumapacelaya/ui/EncuestaView.java
  7. +9
    -2
      src/main/java/mx/gob/jumapacelaya/ui/PlanAnualView.java

+ 6
- 10
src/main/java/mx/gob/jumapacelaya/models/encuestas/Respuesta.java View File

@ -9,15 +9,11 @@ public class Respuesta {
private int preguntaId; private int preguntaId;
private Timestamp fechaResp; private Timestamp fechaResp;
private Boolean respuesta; private Boolean respuesta;
private int emepleadoId;
private int empleadoId;
public Respuesta(int respuestaId, int mantenimientoId, int preguntaId, Timestamp fechaResp, Boolean respuesta, int emepleadoId) {
this.respuestaId = respuestaId;
this.mantenimientoId = mantenimientoId;
public Respuesta(int preguntaId, Boolean respuesta) {
this.preguntaId = preguntaId; this.preguntaId = preguntaId;
this.fechaResp = fechaResp;
this.respuesta = respuesta; this.respuesta = respuesta;
this.emepleadoId = emepleadoId;
} }
public int getRespuestaId() { public int getRespuestaId() {
@ -60,11 +56,11 @@ public class Respuesta {
this.respuesta = respuesta; this.respuesta = respuesta;
} }
public int getEmepleadoId() {
return emepleadoId;
public int getEmpleadoId() {
return empleadoId;
} }
public void setEmepleadoId(int emepleadoId) {
this.emepleadoId = emepleadoId;
public void setEmpleadoId(int empleadoId) {
this.empleadoId = empleadoId;
} }
} }

+ 136
- 0
src/main/java/mx/gob/jumapacelaya/services/DatabaseService.java View File

@ -1,6 +1,8 @@
package mx.gob.jumapacelaya.services; package mx.gob.jumapacelaya.services;
import mx.gob.jumapacelaya.models.*; import mx.gob.jumapacelaya.models.*;
import mx.gob.jumapacelaya.models.encuestas.Pregunta;
import mx.gob.jumapacelaya.models.encuestas.Respuesta;
import oracle.jdbc.OracleConnection; import oracle.jdbc.OracleConnection;
import oracle.jdbc.OraclePreparedStatement; import oracle.jdbc.OraclePreparedStatement;
import oracle.sql.CLOB; import oracle.sql.CLOB;
@ -17,6 +19,7 @@ import java.sql.*;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID;
@Service @Service
public class DatabaseService { public class DatabaseService {
@ -904,4 +907,137 @@ public class DatabaseService {
e.printStackTrace(); e.printStackTrace();
} }
} }
/*************************************** ENCUESTAS ******************************************************************************************************/
public List<Pregunta> getPreguntas() {
List<Pregunta> preguntas = new ArrayList<>();
String query = """
select p.*
from PREGUNTAS p
join ENCUESTAS e on e.ENCUESTAID = p.ENCUESTAID
where e.ENCUESTAID = 1
order by p.ORDEN ASC
""";
try (Connection conn = getMysqlConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(query)) {
while (rs.next()) {
Pregunta pregunta = new Pregunta(
rs.getInt("preguntaid"),
rs.getInt("encuestaid"),
rs.getString("pregunta"),
rs.getInt("orden")
);
preguntas.add(pregunta);
}
} catch (SQLException e) {
e.printStackTrace();
}
return preguntas;
}
public void insertRespuestas(int manteniminetoId, List<Respuesta> respuestas, int empleadoid, String token) {
String insertQuery = """
insert into RESPUESTAS
(MANTENIMIENTOID,PREGUNTAID,FECHARESPUESTA,RESPUESTA,EMPLEADOID)
values
(?,?,?,?,?)
""";
String updateQuery = """
update MANTENIMIENTOS
set ENCUESTA = 'S'
where MANTENIMIENTOID = ?
""";
String markTokenUsedQuery = """
update ENCUESTATOKENS
set USADO = '1' where TOKEN = ?
""";
try (Connection conn = getMysqlConnection()) {
conn.setAutoCommit(false);
try (PreparedStatement insertStmt = conn.prepareStatement(insertQuery);
PreparedStatement updateStmt = conn.prepareStatement(updateQuery);
PreparedStatement tokenStmt = conn.prepareStatement(markTokenUsedQuery)) {
Timestamp ahora = new Timestamp(System.currentTimeMillis());
// 1. Insertar las respuestas en el Batch
for (Respuesta r : respuestas) {
insertStmt.setInt(1, manteniminetoId);
insertStmt.setInt(2, r.getPreguntaId());
insertStmt.setTimestamp(3, ahora);
insertStmt.setBoolean(4, r.getRespuesta());
insertStmt.setInt(5, empleadoid);
insertStmt.addBatch();
}
insertStmt.executeBatch();
// 2. Marcar mantenimiento como encuestado
updateStmt.setInt(1, manteniminetoId);
updateStmt.executeUpdate();
// 3. Invalidar el token para que no se use de nuevo
tokenStmt.setString(1, token);
tokenStmt.executeUpdate();
conn.commit();
} catch (SQLException e) {
conn.rollback();
throw e;
}
} catch (Exception e) {
throw new RuntimeException("Error al guardar encuesta y procesar el token", e);
}
}
public String crearTokenEncuesta(int mantenimientoid) {
String token = UUID.randomUUID().toString();
Timestamp expira = new Timestamp(System.currentTimeMillis() + (24 * 60 * 60 * 1000));
String query = "INSERT INTO ENCUESTATOKENS (TOKEN, MANTENIMIENTOID, FECHAEXPIRACION) VALUES (?, ?, ?)";
try (Connection conn = getMysqlConnection();
PreparedStatement stmt = conn.prepareStatement(query)) {
stmt.setString(1, token);
stmt.setInt(2, mantenimientoid);
stmt.setTimestamp(3, expira);
stmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
return token;
}
public Integer validarTokenYObtenerId(String token) {
String query = "SELECT MANTENIMIENTOID FROM ENCUESTATOKENS " +
"WHERE TOKEN = ? AND FECHAEXPIRACION > NOW() AND USADO = FALSE";
try (Connection conn = getMysqlConnection();
PreparedStatement stmt = conn.prepareStatement(query)) {
stmt.setString(1, token);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
return rs.getInt("MANTENIMIENTOID");
}
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
/***************************************************************************************************************************************/
} }

+ 0
- 1
src/main/java/mx/gob/jumapacelaya/services/EmailService.java View File

@ -36,7 +36,6 @@ public class EmailService {
helper.addInline("image_id", imgResource); helper.addInline("image_id", imgResource);
mailSender.send(mensaje); mailSender.send(mensaje);
System.out.println("Correo enviado con imagen exitosamente"); System.out.println("Correo enviado con imagen exitosamente");


+ 0
- 84
src/main/java/mx/gob/jumapacelaya/services/encuestas/EncuestasDBService.java View File

@ -1,84 +0,0 @@
package mx.gob.jumapacelaya.services.encuestas;
import mx.gob.jumapacelaya.models.encuestas.Pregunta;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
@Service
public class EncuestasDBService {
@Value("${db.url}")
private String dbUrl;
@Value("${db.user}")
private String dbUser;
@Value("${db.pass}")
private String dbPass;
private Connection getEncuestasConn() throws SQLException {
return DriverManager.getConnection(dbUrl, dbUser, dbPass);
}
public List<Pregunta> getPreguntas() {
List<Pregunta> preguntas = new ArrayList<>();
String query = """
select p.*
from PREGUNTAS p
join ENCUESTAS e on e.ENCUESTAID = p.ENCUESTAID
where e.ENCUESTAID = 1
order by p.ORDEN ASC
""";
try (Connection conn = getEncuestasConn();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(query)) {
while (rs.next()) {
Pregunta pregunta = new Pregunta(
rs.getInt("preguntaid"),
rs.getInt("encuestaid"),
rs.getString("pregunta"),
rs.getInt("orden")
);
preguntas.add(pregunta);
}
} catch (SQLException e) {
e.printStackTrace();
}
return preguntas;
}
public void insertRespuestas(int manteniminetoId, int preguntaId, Timestamp fechaResp,
boolean respuesta, int empleadoid) {
String query = """
insert into RESPUESTAS
(MANTENIMIENTOID,PREGUNTAID,FECHARESPUESTA,RESPUESTA,EMPLEADOID)
values
(?,?,?,?,?)
""";
try (Connection conn = getEncuestasConn();
PreparedStatement st = conn.prepareStatement(query)) {
st.setInt(1,manteniminetoId);
st.setInt(2, preguntaId);
if (fechaResp != null) {
st.setTimestamp(3, fechaResp);
} else
st.setNull(3, Types.DATE);
st.setBoolean(4, respuesta);
st.setInt(5, empleadoid);
st.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
System.err.println("Error al insertar respuesta: " + e.getMessage());
}
}
}

+ 36
- 6
src/main/java/mx/gob/jumapacelaya/ui/DetallesMantView.java View File

@ -9,7 +9,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import com.vaadin.flow.component.UI; import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.html.Anchor;
import com.vaadin.flow.component.html.*;
import com.vaadin.flow.server.StreamRegistration; import com.vaadin.flow.server.StreamRegistration;
import com.vaadin.flow.server.StreamResource; import com.vaadin.flow.server.StreamResource;
import mx.gob.jumapacelaya.services.EmailService; import mx.gob.jumapacelaya.services.EmailService;
@ -24,9 +24,6 @@ import com.vaadin.flow.component.dependency.CssImport;
import com.vaadin.flow.component.dialog.Dialog; import com.vaadin.flow.component.dialog.Dialog;
import com.vaadin.flow.component.grid.GridVariant; import com.vaadin.flow.component.grid.GridVariant;
import com.vaadin.flow.component.gridpro.GridPro; import com.vaadin.flow.component.gridpro.GridPro;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.html.Image;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.icon.Icon; import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon; import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.notification.Notification; import com.vaadin.flow.component.notification.Notification;
@ -88,6 +85,7 @@ public class DetallesMantView extends VerticalLayout implements BeforeEnterObser
private Image firmaUsuarioImg; private Image firmaUsuarioImg;
private Image firmaSmtImg; private Image firmaSmtImg;
private Image firmaGciaImg; private Image firmaGciaImg;
Notification avisoEncuestaNtf;
private VerticalLayout userSignLayout; private VerticalLayout userSignLayout;
private VerticalLayout smtSignLayout; private VerticalLayout smtSignLayout;
@ -304,7 +302,10 @@ public class DetallesMantView extends VerticalLayout implements BeforeEnterObser
}); });
btnEnviarEncuesta.addClickListener(e -> enviarCorreo());
btnEnviarEncuesta.addClickListener(e -> {
avisoEncuestaNtf.close();
enviarCorreo();
});
// Se dispara el dialogo de confirmacion // Se dispara el dialogo de confirmacion
@ -527,7 +528,8 @@ public class DetallesMantView extends VerticalLayout implements BeforeEnterObser
String destinatario = usuarioDestino.getEmail(); String destinatario = usuarioDestino.getEmail();
String asunto = "Encuesta de satisfacción -Mantenimiento #" + mantenimientoIdActual; String asunto = "Encuesta de satisfacción -Mantenimiento #" + mantenimientoIdActual;
String linkEncuesta = baseUrl + "/encuesta?mantenimientoId=" + mantenimientoIdActual;
String token = service.crearTokenEncuesta(mantenimientoIdActual);
String linkEncuesta = baseUrl + "/encuesta?token=" + token;
String cuerpo = "<html>" + String cuerpo = "<html>" +
"<body>" + "<body>" +
@ -554,12 +556,40 @@ public class DetallesMantView extends VerticalLayout implements BeforeEnterObser
? event.getLocation().getQueryParameters().getParameters().get("id").stream().findFirst().orElse(null) ? event.getLocation().getQueryParameters().getParameters().get("id").stream().findFirst().orElse(null)
: null; : null;
String encParam = event.getLocation().getQueryParameters().getParameters().getOrDefault("enc", List.of())
.stream().findFirst().orElse("N").toUpperCase();
if (idParam != null) { if (idParam != null) {
try { try {
int planId = Integer.parseInt(idParam); int planId = Integer.parseInt(idParam);
DetalleMantenimientoModel detalle = service.getDetalleMantenimientoPorPlanAnualId(planId); DetalleMantenimientoModel detalle = service.getDetalleMantenimientoPorPlanAnualId(planId);
if (encParam.equalsIgnoreCase("N")) {
Icon icono = new Icon(VaadinIcon.WARNING);
icono.setSize("40px");
NativeLabel aviso = new NativeLabel("Aviso:");
aviso.getStyle().set("font-weight", "bold");
NativeLabel label = new NativeLabel("¡No se ha respondido una encuesta para este mantenimiento!");
VerticalLayout textLyt = new VerticalLayout(aviso,label);
textLyt.setPadding(false);
textLyt.setSpacing(false);
HorizontalLayout layout = new HorizontalLayout(icono, textLyt);
layout.setJustifyContentMode(JustifyContentMode.CENTER);
layout.setAlignItems(Alignment.CENTER);
avisoEncuestaNtf = new Notification(layout);
avisoEncuestaNtf.setPosition(Notification.Position.TOP_CENTER);
avisoEncuestaNtf.addThemeVariants(NotificationVariant.LUMO_WARNING);
avisoEncuestaNtf.setDuration(5000);
avisoEncuestaNtf.open();
} else {
btnEnviarEncuesta.setEnabled(false);
btnEnviarEncuesta.getStyle().set("opacity", "0.5");
}
if (detalle != null) { if (detalle != null) {
this.planAnualIdActual = detalle.getPlanAnualId(); this.planAnualIdActual = detalle.getPlanAnualId();
this.mantenimientoIdActual = detalle.getId(); this.mantenimientoIdActual = detalle.getId();


+ 41
- 37
src/main/java/mx/gob/jumapacelaya/ui/EncuestaView.java View File

@ -18,7 +18,7 @@ import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.auth.AnonymousAllowed; import com.vaadin.flow.server.auth.AnonymousAllowed;
import mx.gob.jumapacelaya.models.encuestas.Pregunta; import mx.gob.jumapacelaya.models.encuestas.Pregunta;
import mx.gob.jumapacelaya.models.encuestas.Respuesta; import mx.gob.jumapacelaya.models.encuestas.Respuesta;
import mx.gob.jumapacelaya.services.encuestas.EncuestasDBService;
import mx.gob.jumapacelaya.services.DatabaseService;
import javax.swing.*; import javax.swing.*;
import java.sql.Timestamp; import java.sql.Timestamp;
@ -31,13 +31,14 @@ import java.util.Timer;
@AnonymousAllowed @AnonymousAllowed
public class EncuestaView extends VerticalLayout implements BeforeEnterObserver { public class EncuestaView extends VerticalLayout implements BeforeEnterObserver {
private EncuestasDBService encuestasDBService;
private DatabaseService encuestasDBService;
private int mantenimientoId = -1; private int mantenimientoId = -1;
private final VerticalLayout mainLyt = new VerticalLayout(); private final VerticalLayout mainLyt = new VerticalLayout();
private Span pregunta1Txt = new Span(); private Span pregunta1Txt = new Span();
private RadioButtonGroup<String> pregunta1Rb = new RadioButtonGroup<>(); private RadioButtonGroup<String> pregunta1Rb = new RadioButtonGroup<>();
private Button btnEnviar = new Button("Enviar"); private Button btnEnviar = new Button("Enviar");
private TextField txtNumEmpl = new TextField("No. Empleado:"); private TextField txtNumEmpl = new TextField("No. Empleado:");
private String token;
private static class RespuestaComponente { private static class RespuestaComponente {
@ -52,7 +53,7 @@ public class EncuestaView extends VerticalLayout implements BeforeEnterObserver
private final List<RespuestaComponente> respuestasUI = new ArrayList<>(); private final List<RespuestaComponente> respuestasUI = new ArrayList<>();
public EncuestaView(EncuestasDBService encuestasDBService) {
public EncuestaView(DatabaseService encuestasDBService) {
this.encuestasDBService = encuestasDBService; this.encuestasDBService = encuestasDBService;
setSpacing(true); setSpacing(true);
@ -80,23 +81,6 @@ public class EncuestaView extends VerticalLayout implements BeforeEnterObserver
this.add(mainLyt); this.add(mainLyt);
} }
@Override
public void beforeEnter(BeforeEnterEvent beforeEnterEvent) {
List<String> params = beforeEnterEvent.getLocation().getQueryParameters()
.getParameters().get("mantenimientoId");
if (params != null && !params.isEmpty()) {
try {
mantenimientoId = Integer.parseInt(params.get(0));
mostrarMensaje();
} catch (NumberFormatException e) {
add(new H2("Enlace inválido"));
}
} else {
add(new H2("No se recibió ningún numero de mantenimiento"));
}
}
private void mostrarMensaje() { private void mostrarMensaje() {
txtNumEmpl.setRequired(true); txtNumEmpl.setRequired(true);
@ -163,28 +147,24 @@ public class EncuestaView extends VerticalLayout implements BeforeEnterObserver
int empleadoId = Integer.parseInt(txtNumEmpl.getValue()); int empleadoId = Integer.parseInt(txtNumEmpl.getValue());
Timestamp ahora = new Timestamp(System.currentTimeMillis()); Timestamp ahora = new Timestamp(System.currentTimeMillis());
List<Respuesta> respuestas = new ArrayList<>();
for (RespuestaComponente rc : respuestasUI) { for (RespuestaComponente rc : respuestasUI) {
if (rc.radios.getValue() == null) continue;
Pregunta pregunta = rc.pregunta;
String respuestaTexto = rc.radios.getValue();
if (respuestaTexto == null) {
System.out.println("Pregunta no respondida: " + pregunta.getPregunta());
continue;
}
boolean respuestaBool = respuestaTexto.equals("Si");
encuestasDBService.insertRespuestas(
mantenimientoId,
pregunta.getPreguntaId(),
ahora,
respuestaBool,
empleadoId
);
respuestas.add(new Respuesta(
rc.pregunta.getPreguntaId(),
rc.radios.getValue().equals("Si")
));
} }
encuestasDBService.insertRespuestas(
mantenimientoId,
respuestas,
empleadoId,
this.token
);
NotiEncuesta(); NotiEncuesta();
mainLyt.setVisible(false); mainLyt.setVisible(false);
@ -211,4 +191,28 @@ public class EncuestaView extends VerticalLayout implements BeforeEnterObserver
ntf.add(layout); ntf.add(layout);
ntf.open(); ntf.open();
} }
@Override
public void beforeEnter(BeforeEnterEvent beforeEnterEvent) {
String token = beforeEnterEvent.getLocation().getQueryParameters()
.getParameters().getOrDefault("token", List.of("")).get(0);
if (!token.isEmpty()) {
Integer idRecuperado = encuestasDBService.validarTokenYObtenerId(token);
if (idRecuperado != null) {
this.mantenimientoId = idRecuperado;
this.token = token;
mostrarMensaje();
} else {
mainLyt.add(new H2("El enlace ha expírado o ya fue utilizado."));
mainLyt.setAlignItems(Alignment.CENTER);
}
} else {
mainLyt.add(new H2("Acceso no autorizado."));
mainLyt.setAlignItems(Alignment.CENTER);
}
}
} }

+ 9
- 2
src/main/java/mx/gob/jumapacelaya/ui/PlanAnualView.java View File

@ -268,13 +268,16 @@ public class PlanAnualView extends VerticalLayout {
LocalDate fechaSistema = LocalDate.now(); LocalDate fechaSistema = LocalDate.now();
String nomEquipo = planAnual.getNomEquipo(); String nomEquipo = planAnual.getNomEquipo();
String departamento = planAnual.getDepartamento(); String departamento = planAnual.getDepartamento();
String encuesta = planAnual.getEncuesta();
btn.getUI().ifPresent(ui -> ui.navigate( btn.getUI().ifPresent(ui -> ui.navigate(
"mantenimiento?id=" + idPlananual + "mantenimiento?id=" + idPlananual +
"&fecha=" + fechaSistema + "&fecha=" + fechaSistema +
"&tipo=1" + "&tipo=1" +
"&nomEquipo=" + nomEquipo + "&nomEquipo=" + nomEquipo +
"&departamento=" + departamento));
"&departamento=" + departamento +
"&enc=" + encuesta
));
}); });
} else if ("REALIZADO".equalsIgnoreCase(estado)) { } else if ("REALIZADO".equalsIgnoreCase(estado)) {
@ -284,8 +287,12 @@ public class PlanAnualView extends VerticalLayout {
btn.addClickListener(event -> { btn.addClickListener(event -> {
int idPlananual = planAnual.getNumero(); int idPlananual = planAnual.getNumero();
String encuesta = planAnual.getEncuesta();
btn.getUI().ifPresent(ui -> ui.navigate( btn.getUI().ifPresent(ui -> ui.navigate(
"detalles?id=" + idPlananual));
"detalles?id=" + idPlananual +
"&enc=" + encuesta
));
}); });
} else { } else {
btn = new Button("N/A"); btn = new Button("N/A");


Loading…
Cancel
Save