Plataforma de Voluntariado Estudiantil con SpringBoot, Vue y uniApp: Implementación Completa

Tecnologías Utilizadas

Framework Backend: SpringBoot

SpringBoot integra servidores como Tomcat, Jetty y Undertow, permitiendo su uso sin configuraciones adicionales. Una de sus características principales es la configuración automática, que adapta la aplicación según las dependencias del proyecto. Esto simplifica significativamente el proceso de configuración, eliminando la necesidad de ajustar manualmente cada dependencia.

El framework ofrece numerosas funcionalidades listas para usar, como Spring Data para el acceso a datos, Spring Security para la autenticación y Spring Cloud para arquitecturas distribuidas. Estas herramientas aceleran el desarrollo y facilitan la integración con otras tecnologías. Su popularidad se debe a estas características, que permiten construir aplicaciones de alta calidad de manera eficiente y con menos esfuerzo.

Framework Front end: Vue

Vue.js utiliza tecnología de DOM Virtual, una estructura de datos en memoria que optimiza las operaciones con el DOM. Implementa enlace de datos reactivo, DOM Virtual y arquitectura de componentes, proporcionando un modelo de desarrolllo flexible, eficiente y fácil de mantener.

Cuando los datos cambian, la interfaz de usuario se actualiza automáticamente, permitiendo a los desarrolladores concentrarse en la lógica de datos en lugar de en las actualizaciones manuales de la interfaz. Esta es la esencia de la simplicidad, flexibilidad y eficiencia que Vue.js ofrece.

Capa de Persistencia: MyBatisPlus

MyBatis-Plus es una herramienta de extensión basada en MyBatis diseñada para simplificar el desarrollo. Es un framework Java de código abierto compatible con múltiples bases de datos como MySQL, Oracle, SQL Server y PostgreSQL.

Proporciona una amplia API y anotaciones que facilitan las operaciones ORM, reduciendo considerablemente la necesidad de escribir SQL manualmente. Además, incluye un generador de código que puede crear automáticamente entidades, interfaces Mapper y archivos de mapeo XML, optimizando el flujo de desarrollo.

MyBatis-Plus también soporta funcionalidades prácticas como paginación, consultas dinámicas, bloqueo optimista y análisis de rendimiento, permitiendo operaciones de datos eficientes. Con esta herramienta, los desarrolladores pueden crear capas de acceso a datos de alta calidad rápidamente.

Pruebas del Sistema

El objetivo principal de las pruebas es identificar problemas desde múltiples perspectivas. A través de las pruebas funcionales, se detectan y corrigen defectos, asegurando que el sistema cumpla con los requisitos del cliente. Durante el proceso de prueba, se valida que el sistema satisface las necesidades del usuario y se corrigen oportunamente los problemas encontrados.

Objetivo de las Pruebas del Sistema

En el ciclo de desarrollo de aplicaciones web, las pruebas del sistema son un proceso esencial que requiere paciencia. Su importancia radica en ser el último filtro de calidad y fiabilidad, así como la última inspección de todo el proceso de desarrollo.

El objetivo es evitar problemas durante el uso del sistema y mejorar la experiencia del usuario. Es necesario cnosiderar múltiples ángulos y enfoques para anticipar posibles problemas, simulando diferentes escenarios para identificar y resolver deficiencias. Durante las pruebas, se evalúa la calidad del sistema, la integridad de sus funciones y la fluidez de su lógica. Un proceso de pruebas adecuado mejora significativamente la calidad y la usabilidad del sistema. Las pruebas buscan verificar si el sistema cumple con las especificaciones del documento de requisitos e identificar cualquier inconsistencia o conflicto. Durante el proceso, es crucial adoptar la perspectiva del usuario para evitar escenarios poco realistas que consuman tiempo y puedan generar resultados inesperados.

Pruebas Funcionales del Sistema

Se realizan pruebas en los módulos funcionales del sistema mediante técnicas de caja negra, incluyendo clics, entrada de valores límite y validación de campos obligatorios y opcionales. Se elaboran casos de prueba basados en escenarios específicos y se documentan los resultados.

Casos de Prueba de Inicio de Sesión

Datos de Entrada Resultado Esperado Resultado Real Análisis
Usuario: admin Contraseña: 123456 Código: correcto Iniciar sesión Inicio de sesión exitoso Coincide con lo esperado
Usuario: admin Contraseña: 111111 Código: correcto Contraseña incorrecta Mensaje de error de contraseña Coincide con lo esperado
Usuario: admin Contraseña: 123456 Código: incorrecto Código incorrecto Mensaje de error de código Coincide con lo esperado
Usuario: (vacío) Contraseña: 123456 Código: correcto Usuario obligatorio Mensaje de requerir usuario Coincide con lo esperado
Usuario: admin Contraseña: (vacío) Código: correcto Contraseña incorrecta Mensaje de error de contraseña Coincide con lo esperado

Casos de Prueba de Gestión de Usuarios

La gestión de usuarios incluye funciones de adición, edición, eliminación y búsqueda. Al agregar un usuario, se valida que los campos obligatorios no estén vacíos; al agregar información de un usuario existente, se verifica si el sistema muestra una advertencia de duplicado; al eliminar un usuario, el sistema confirma la acción; al modificar información, los cambios se reflejan correctamente en la interfaz.

Datos de Entrada Resultado Esperado Resultado Real Análisis
Ingresar información básica del usuario Agregar exitosamente, mostrar en lista El usuario aparece en la lista Coincide con lo esperado
Modificar información del usuario Editar exitosamente, cambios aplicados La información del usuario se actualiza Coincide con lo esperado
Seleccionar y eliminar usuario Sistema pregunta confirmación, usuario eliminado Sistema pregunta confirmación, usuario no encontrado después Coincide con lo esperado
No ingresar usuario al agregar Mensaje de usuario obligatorio Mensaje de usuario obligatorio Coincide con lo esperado
Ingresar usuario existente Fallo al agregar, mensaje de duplicado Fallo al agregar, mensaje de duplicado Coincide con lo esperado

Conclusión de las Pruebas del Sistema

El sistema principalmente utiliza pruebas de caja negra, simulando el uso del sistema para verificar cada función y asegurar la corrección de los flujos. Las pruebas son indispensables para perfeccionar el sistema y mejorar su usabilidad.

Las pruebas buscan validar si los módulos funcionales cumplen con el diseño original y si su lógica es correcta. Este sistema no requiere procesamiento lógico complejo para facilitar su uso. El objetivo final de las pruebas se centra en la experiencia del usuario. Todos los escenarios de prueba deben satisfacer las necesidades del usuario sin desviarse de los objetivos iniciales. Al resolver problemas, es fundamental adoptar la perspectiva del usuario. Tras completar el proceso de prueba, los resultados indican que el sistema cumple con los requisitos funcionales y de diseño.

Código de Referencia

@IgnoreAuth
@PostMapping(value = "/iniciarSesion")
public R iniciarSesion(String nombreUsuario, String contrasena, String captcha, HttpServletRequest peticion) {
   UsuarioEntity usuario = servicioUsuario.seleccionarUno(new EntityWrapper<UsuarioEntity>().eq("nombreUsuario", nombreUsuario));
   if(usuario==null || !usuario.getContrasena().equals(contrasena)) {
      return R.error("Cuenta o contraseña incorrectos");
   }
   String token = servicioToken.generarToken(usuario.getId(), nombreUsuario, "usuarios", usuario.getRol());
   return R.ok().put("token", token);
}

	@Override
	public String generarToken(Long idUsuario, String nombreUsuario, String nombreTabla, String rol) {
		TokenEntity entidadToken = this.seleccionarUno(new EntityWrapper<TokenEntity>().eq("idUsuario", idUsuario).eq("rol", rol));
		String token = UtilidadesComunes.getCadenaAleatoria(32);
		Calendar calendario = Calendar.getInstance();   
    	calendario.setTime(new Date());   
    	calendario.add(Calendar.HORA_DEL_DIA, 1);
		if(entidadToken!=null) {
			entidadToken.setToken(token);
			entidadToken.setTiempoExpiracion(calendario.getTime());
			this.actualizarPorId(entidadToken);
		} else {
			this.insertar(new TokenEntity(idUsuario, nombreUsuario, nombreTabla, rol, token, calendario.getTime()));
		}
		return token;
	}

/**
 * Verificación de Permisos (Token)
 */
@Component
public class InterceptorAutorizacion implements HandlerInterceptor {

    public static final String CLAVE_TOKEN_LOGIN = "Token";

    @Autowired
    private ServicioToken servicioToken;
    
	@Override
    public boolean preHandle(HttpServletRequest peticion, HttpServletResponse respuesta, Object manejador) throws Exception {

		// Soporte para solicitudes跨域
        respuesta.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        respuesta.setHeader("Access-Control-Max-Age", "3600");
        respuesta.setHeader("Access-Control-Allow-Credentials", "true");
        respuesta.setHeader("Access-Control-Allow-Headers", "x-requested-with,request-source,Token, Origin,imgType, Content-Type, cache-control,postman-token,Cookie, Accept,authorization");
        respuesta.setHeader("Access-Control-Allow-Origin", peticion.getHeader("Origin"));
	// Las solicitudes跨域 envían primero una solicitud OPTIONS, respondemos con estado normal
	if (peticion.getMethod().equals(RequestMethod.OPTIONS.name())) {
        	respuesta.setStatus(HttpStatus.OK.value());
            return false;
        }
        
        AnotacionIgnorarAuth anotacion;
        if (manejador instanceof HandlerMethod) {
            anotacion = ((HandlerMethod) manejador).getMethodAnnotation(AnotacionIgnorarAuth.class);
        } else {
            return true;
        }

        // Obtener token del encabezado
        String token = peticion.getHeader(CLAVE_TOKEN_LOGIN);
        
        /**
         * Métodos que no requieren verificación de permisos
         */
        if(anotacion!=null) {
        	return true;
        }
        
        TokenEntity entidadToken = null;
        if(StringUtils.isNotBlank(token)) {
        	entidadToken = servicioToken.obtenerEntidadToken(token);
        }
        
        if(entidadToken != null) {
        	peticion.getSession().setAttribute("idUsuario", entidadToken.getIdUsuario());
        	peticion.getSession().setAttribute("rol", entidadToken.getRol());
        	peticion.getSession().setAttribute("nombreTabla", entidadToken.getNombreTabla());
        	peticion.getSession().setAttribute("nombreUsuario", entidadToken.getNombreUsuario());
        	return true;
        }
		
		PrintWriter escritor = null;
		respuesta.setCharacterEncoding("UTF-8");
		respuesta.setContentType("application/json; charset=utf-8");
		try {
		    escritor = respuesta.getWriter();
		    escritor.print(JSONObject.toJSONString(R.error(401, "Por favor inicie sesión primero")));
		} finally {
		    if(escritor != null){
		        escritor.close();
		    }
		}
		return false;
    }
}

Estructura de Base de Datos de Referencia

-- ----------------------------
-- Estructura de la tabla token
-- ----------------------------
DROP TABLE IF EXISTS `token`;
CREATE TABLE `token` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'Clave primaria',
  `idUsuario` bigint(20) NOT NULL COMMENT 'ID de usuario',
  `nombreUsuario` varchar(100) NOT NULL COMMENT 'Nombre de usuario',
  `nombreTabla` varchar(100) DEFAULT NULL COMMENT 'Nombre de tabla',
  `rol` varchar(100) DEFAULT NULL COMMENT 'Rol',
  `token` varchar(200) NOT NULL COMMENT 'Contraseña',
  `tiempoAdicion` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Tiempo de adición',
  `tiempoExpiracion` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'Tiempo de expiración',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='Tabla de tokens';

-- ----------------------------
-- Registros de la tabla token
-- ----------------------------
INSERT INTO `token` VALUES ('9', '23', 'cd01', 'estudiante', 'Estudiante', 'al6svx5qkei1wljry5o1npswhdpqcpcg', '2023-02-23 21:46:45', '2023-03-15 14:01:36');
INSERT INTO `token` VALUES ('10', '11', 'xh01', 'estudiante', 'Estudiante', 'fahmrd9bkhqy04sq0fzrl4h9m86cu6kx', '2023-02-27 18:33:52', '2023-03-17 18:27:42');
INSERT INTO `token` VALUES ('11', '17', 'ch01', 'estudiante', 'Estudiante', 'u5km44scxvzuv5yumdah2lhva0gp4393', '2023-02-27 18:46:19', '2023-02-27 19:48:58');
INSERT INTO `token` VALUES ('12', '1', 'admin', 'usuarios', 'Administrador', 'h1pqzsb9bldh93m92j9m2sljy9bt1wdh', '2023-02-27 19:37:01', '2023-03-17 18:23:02');
INSERT INTO `token` VALUES ('13', '21', 'xiaohao', 'director', 'Director', 'zdm7j8h1wnfe27pkxyiuzvxxy27ykl2a', '2023-02-27 19:38:07', '2023-03-17 18:25:20');
INSERT INTO `token` VALUES ('14', '27', 'djy01', 'estudiante', 'Estudiante', 'g3teq4335pe21nwuwj2sqkrpqoabqomm', '2023-03-15 12:56:17', '2023-03-15 14:00:16');
INSERT INTO `token` VALUES ('15', '29', 'dajiyue', 'director', 'Director', '0vb1x9xn7riewlp5ddma5ro7lp4u8m9j', '2023-03-15 12:58:08', '2023-03-15 14:03:48');


Etiquetas: SpringBoot vue.js uniapp MyBatisPlus java

Publicado el 6-8 20:47