Portal Educativo para Primaria: Java SSM + HTML5

El presente artículo describe la arquitectura y el desarrollo de un portal web pensado para que los alumnos de educación primaria amplíen sus conocimientos fuera del aula. El sistema combina un front-end ligero basado en HTML5 y Bootstrap con un back-end robusto implementado con Spring, Spring MVC y MyBatis (SSM). A continuación se detallan los componentes más relevantes.

Arquitectura general

  • Cliente: HTML5, CSS3, JavaScript ES6, jQuery 3.x, componentes LayUI.
  • Servidor: Java 11, Spring 5, Spring MVC, MyBatis 3, Maven 3.8, Tomcat 9.
  • Datos: MySQL 8.0 (modelo relacional), con scripts de migración para SQL Server.
  • Entorno de desarrollo: IntelliJ IDEA, Navicat, Git.

Diagrama de paquetes (back-end)


com.educare.primaria
├── controller
│   ├── RecursoControlador.java
│   ├── FavoritoControlador.java
│   └── UsuarioControlador.java
├── service
│   ├── RecursoServicio.java
│   └── impl
│       └── RecursoServicioImpl.java
├── mapper
│   └── RecursoMapper.xml
└── domain
    ├── Recurso.java
    └── Favorito.java

Modelo de datos simplificado


CREATE TABLE recurso (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    titulo VARCHAR(120) NOT NULL,
    descripcion TEXT,
    url_contenido VARCHAR(255),
    nivel ENUM('1','2','3','4','5','6') NOT NULL,
    materia ENUM('MAT','LEN','CIE','ART','ING') NOT NULL,
    fecha_alta DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE favorito (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    usuario_id BIGINT NOT NULL,
    recurso_id BIGINT NOT NULL,
    fecha_guardado DATETIME DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (usuario_id) REFERENCES usuario(id),
    FOREIGN KEY (recurso_id) REFERENCES recurso(id)
);

Código de ejemplo: controlador de faovritos


package com.educare.primaria.controller;

import com.educare.primaria.domain.Favorito;
import com.educare.primaria.service.FavoritoServicio;
import com.educare.primaria.util.Respuesta;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/api/favorito")
public class FavoritoControlador {

    @Autowired
    private FavoritoServicio favoritoServicio;

    @GetMapping("/pagina")
    public Respuesta pagina(@RequestParam Map<String, Object> params,
                            HttpServletRequest peticion) {
        Long usuarioId = (Long) peticion.getSession().getAttribute("uid");
        return Respuesta.ok()
                .conDatos(favoritoServicio.listarPaginado(params, usuarioId));
    }

    @PostMapping
    public Respuesta guardar(@RequestBody Favorito favorito,
                             HttpServletRequest peticion) {
        favorito.setUsuarioId((Long) peticion.getSession().getAttribute("uid"));
        favoritoServicio.insertar(favorito);
        return Respuesta.ok("Recurso guardado en favoritos");
    }

    @DeleteMapping("/{ids}")
    public Respuesta borrar(@PathVariable List<Long> ids,
                            HttpServletRequest peticion) {
        Long usuarioId = (Long) peticion.getSession().getAttribute("uid");
        favoritoServicio.eliminar(ids, usuarioId);
        return Respuesta.ok("Favoritos eliminados");
    }

    @GetMapping("/recordatorio/{campo}/{tipo}")
    public Respuesta recordatorio(@PathVariable String campo,
                                  @PathVariable String tipo,
                                  @RequestParam Map<String, Object> filtros,
                                  HttpServletRequest peticion) {
        filtros.put("columna", campo);
        filtros.put("tipo", tipo);
        Long usuarioId = (Long) peticion.getSession().getAttribute("uid");
        int total = favoritoServicio.contar(filtros, usuarioId);
        return Respuesta.ok().conDato("total", total);
    }
}

Fragmento de srevicio


@Service
public class FavoritoServicioImpl implements FavoritoServicio {

    @Autowired
    private FavoritoMapper mapper;

    @Override
    public Page<Favorito> listarPaginado(Map<String, Object> params, Long usuarioId) {
        QueryWrapper<Favorito> qw = new QueryWrapper<>();
        qw.eq("usuario_id", usuarioId);
        Page<Favorito> page = new Page<>(params);
        return mapper.selectPage(page, qw);
    }

    @Override
    public void insertar(Favorito favorito) {
        favorito.setId(SnowFlake.nextId());
        mapper.insert(favorito);
    }

    @Override
    public void eliminar(List<Long> ids, Long usuarioId) {
        mapper.deleteBatchIds(ids, usuarioId);
    }

    @Override
    public int contar(Map<String, Object> filtros, Long usuarioId) {
        QueryWrapper<Favorito> qw = new QueryWrapper<>();
        qw.eq("usuario_id", usuarioId);
        if (filtros.containsKey("inicio")) {
            qw.ge("fecha_guardado", filtros.get("inicio"));
        }
        if (filtros.containsKey("fin")) {
            qw.le("fecha_guardado", filtros.get("fin"));
        }
        return mapper.selectCount(qw);
    }
}

Pruebas de sistema

El plan de pruebas incluye:

  1. Pruebas unitarias: validan la lógica de negocio de cada servicio con JUnit 5 y Mockito.
  2. Pruebas de integración: verifican la correcta interacción entre controladores y base de datos mediante Testcontainers y MySQL en Docker.
  3. Pruebas de estrés: utilizan JMeter para simular 500 usuarios concurrentes accediendo a recursos de matemáticas de segundo grado.
  4. Pruebas de regresión: se ejecutan automado cada pull request con GitHub Actions para garantizar que los cambios no rompen funcionalidades previas.

Despliegue rápido con Docker Compose


version: "3.9"
services:
  db:
    image: mysql:8
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: educare
    ports:
      - "3306:3306"
  app:
    build: .
    ports:
      - "8080:8080"
    depends_on:
      - db

Con este conjunto de tecnologías y prácticas, el portal se mantiene ligero para el navegador del alumno y robusto en el servidor, permitiendo una experiencia fluida tanto en tablets como en equipos de escritorio.

Etiquetas: java SSM HTML5 Bootstrap MyBatis

Publicado el 8-7 06:46