Plataforma de Venta de Peces Dorados con SpringBoot, Vue y UniApp: Diseño e Implementación Técnica Detallada

Tecnologías Utilizadas

Backend: SpringBoot

SpringBoot simplifica la creación de aplicaciones Spring independientes y listas para producción. Integra servidores web como Tomcat por defecto y ofrece configuración automática basada en las dependencias del proyecto. Esto reduce drásticamente la necesidad de configuración manual. Además, proporciona un ecosistema rico en funcionalidades como Spring Data para el acceso a datos, Spring Security para la autenticación y Spring Cloud para arquitecturas de microservicios, acelerando así el desarrollo.

Frontend Web: Vue.js

Vue.js emplea un modelo de programación declarativo y un sistema de reactividad basado en un DOM virtual. Este DOM virtual es una representación ligera en memoria de la interfaz de usuario real, lo que permite realizar actualizaciones de DOM de manera eficiente y predecible. Cuando el estado de los datos de la aplicación cambia, Vue calcula de forma óptima el número mínimo de operaciones necesarias para actualizar la vista, proporcionando un rendimiento alto y una experiencia de desarrollo ágil.

Capa de Persistencia: MyBatis-Plus

MyBatis-Plus es una potenciación del framework MyBatis para Java. Facilita el acceso a bases de datos relacionales mediante la eliminación de código repetitivo (boilerplate) y ofrece una sintaxis más concisa para operaciones CRUD comunes. Incluye un generador de código para producir automáticamente entidades, mappers y archivos de configuración. Funcionalidades como la paginación integrada, la construcción dinámica de consultas, el bloqueo optimista y el análisis de rendimiento son incorporadas directamente, simplificando el desarrollo de la capa de datos.

Pruebas del Sistema

El objetivo principal de las pruebas es identificar defectos en el sistema mediante una verificación exhaustiva. Se emplean técnicas de caja negra para validar la funcionalidad contra los requisitos del usuario. Esto incluye probar flujos de trabajo críticos, la validación de entradas y los mensajes de error esperados.

Casos de Prueba para la Autenticación

Entrada Resultado Esperado Resultado Obetnido Análisis
Usuario: admin, Contraseña: 123456, Captcha: Correcto Inicio de sesión exitoso Acceso al panel principal Coincide con la expectativa
Usuario: admin, Contraseña: incorrecta, Captcha: Correcto Error de contraseña Mensaje: "Contraseña incorrecta" Coincide con la expectativa
Usuario: admin, Contraseña: 123456, Captcha: Erróneo Error de captcha Mensaje: "Captcha incorrecto" Coincide con la expectativa
Usuario: (vacío), Contraseña: 123456, Captcha: Correcto Error: campo usuario requerido Mensaje: "El nombre de usuario es obligatorio" Coincide con la expectativa

El proceso de pruebas garantiza que la implementación se alinea con la lógica de negocio y proporciona una experiencia de usuario robusta. Se realizaron iteraciones de prueba para corregir los defectos hallados hasta que todos los módulos clave funcionaron según lo diseñado.

Fragments de Código Clave

Controlador de Autenticación (Login)


@RestController
@RequestMapping("/auth")
public class AuthController {

    @Autowired
    private UserAccountService userAccountService;
    @Autowired
    private TokenManagerService tokenManagerService;

    @PostMapping("/token")
    public ResponseEntity<ApiToken> authenticateUser(@RequestBody LoginRequest credentials) {
        Optional<UserAccount> userOpt = userAccountService.findByUsername(credentials.username());
        
        if (userOpt.isEmpty() || !passwordEncoder.matches(credentials.password(), userOpt.get().getPasswordHash())) {
            throw new UnauthorizedException("Credenciales inválidas.");
        }
        
        UserAccount user = userOpt.get();
        String jwt = tokenManagerService.createJwtForUser(user.getId(), user.getUsername(), user.getRole());
        return ResponseEntity.ok(new ApiToken(jwt, user.getRole()));
    }
}

Servicio de Gestión de Tokens (JWT)


@Service
public class TokenManagerService {
    
    @Value("${app.jwt.secret}")
    private String jwtSecret;
    @Value("${app.jwt.expiration-ms}")
    private long expirationTime;

    public String createJwtForUser(Long userId, String username, String role) {
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + expirationTime);

        return Jwts.builder()
                .setSubject(String.valueOf(userId))
                .claim("username", username)
                .claim("role", role)
                .setIssuedAt(now)
                .setExpiration(expiryDate)
                .signWith(SignatureAlgorithm.HS512, jwtSecret)
                .compact();
    }
    
    public Jws<Claims> validateAndParseToken(String token) {
        return Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token);
    }
}

Interceptor de Autorización


@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private TokenManagerService tokenService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        
        String authHeader = request.getHeader("Authorization");
        
        if (StringUtils.hasText(authHeader) && authHeader.startsWith("Bearer ")) {
            String jwtToken = authHeader.substring(7);
            try {
                Jws<Claims> claims = tokenService.validateAndParseToken(jwtToken);
                String userId = claims.getBody().getSubject();
                String role = claims.getBody().get("role", String.class);
                
                UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                    userId, null, Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role))
                );
                SecurityContextHolder.getContext().setAuthentication(authToken);
            } catch (JwtException e) {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Token inválido o expirado.");
                return;
            }
        }
        
        filterChain.doFilter(request, response);
    }
}

Esquema Simplificado de Base de Datos


CREATE TABLE `user_session` (
  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `user_id` BIGINT NOT NULL,
  `token` VARCHAR(512) NOT NULL UNIQUE,
  `issued_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `expires_at` TIMESTAMP NOT NULL,
  FOREIGN KEY (`user_id`) REFERENCES `system_user`(`id`)
) COMMENT='Almacena tokens de sesión válidos';

CREATE TABLE `goldfish_product` (
  `product_id` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `name` VARCHAR(200) NOT NULL,
  `variety` VARCHAR(100),
  `age_months` INT,
  `price` DECIMAL(10,2) NOT NULL,
  `stock_quantity` INT DEFAULT 0,
  `description` TEXT,
  `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) COMMENT='Catálogo de peces dorados en venta';

Etiquetas: SpringBoot vue.js uniapp MyBatis-Plus JWT

Publicado el 6-14 01:51