Tecnologias Utilizadas
As escolhas tecnológicas a seguir baseiam-se em requisitos práticos de projeto e servem como referência.
1. Frontend - Vue.js
Vue.js é um framework JavaScript leve voltado para a camada de apresentação, introduzido por Evan You em 2014. Sua arquitetura de ligação de dados reativa simplifica a sincronização entre o modelo de dados e a interface do usuário, permitindo que desenvolvedores foquem na lógica de negócios. O sistema de componentes facilita a criação de interfaces modulares e reutilizáveis. O ecossistema inclui ferramentas como Vuex para gerenciamento de estado e Vue Router para navegação em aplicações de página única (SPA). Versões recentes, como o Vue 3, aprimoram o desempenho através da Composition API e otimizações no sistema reativo.
2. Backend - Spring Boot
O Spring Boot simplifica o desenvolvimento de aplicações baseadas em Spring, oferecendo configuração automática e gerenciamento de dependências via "Starters". Seguindo o princípio de "convenção sobre configuração", ele integra automaticamente servidores como o Tomcat e adapta-se a dependências do projeto, como bancos de dados. O módulo Actuator fornece monitoramento e métricas em tempo real, essencial para ambientes de produção. Além disso, sua integração com o Spring Cloud facilita a construção de sistemas distribuídos, com suporte a descoberta de serviços e configuração centralizada.
3. Banco de Dados - MySQL
O MySQL é um sistema de gerenciamento de banco de dados relacional (RDBMS) de código aberto, baseado em SQL. Destaca-se por sua confiabilidade, desempenho e compatibilidade com diversas plataformas. Suporta recursos avançados como transações, triggers e procedimentos armazenados, além de oferecer mecanismos robustos de backup e segurança. Sua estrutura baseada em tabelas e índices otimiza consultas em aplicações web de diferentes escalas.
4. Arquitetura - B/S (Browser/Server)
A arquitetura B/S centraliza a interface no navegador, enquanto o servidor processa lógica de negócios e armazena dados. Essa abordagem promove a portabilidade e manutenção, já que os usuários acessam o sistema via navegador sem instalações locais. A separação entre frontend e backend, comunicando-se via HTTP com formatos como JSON, permite desenvolvimento independente. Otimizações como cache, balanceamento de carga e tecnologias de contêiner (Docker) melhoram o desempenho e a escalabilidade.
Testes do Sistema
Visão Geral dos Testes
Os testes são organizados por módulos funcionais, abrangendo interfaces de usuário e administrador. Eles validam se as funcionalidades atendem aos requisitos, focando em estabilidade, usabilidade e experiência do usuário. Ao simular operações reais, identificam-se problemas potenciais para garantir a qualidade do sistema.
Testes Funcionais
Testes para a interface do usuário incluem registro, login, participação em avaliações e visualização de conteúdo. Para administradores, focam-se em gerenciamento de dados, como adição de anúncios ou modificação de recursos educacionais.
Exemplo de tabela de testes (usuário):
| ID | Funcionalidade | Caso de Teste | Resultado Esperaod | Resultado Atual | Aprovado |
|---|---|---|---|---|---|
| 1 | Registro | Inserir dados de um novo usuário | Registro bem-sucedido | Registro bem-sucedido | Sim |
| 2 | Login | Autenticar com credenciais válidas | Login efetuado | Login efetuado | Sim |
| 3 | Avaliação Online | Submeter respostas após iniciar teste | Submissão confirmada | Submissão confirmada | Sim |
Exemplo de tabela de testes (administrador):
| ID | Funcionalidade | Caso de Teste | Resultado Esperado | Resultado Atual | Aprovado |
|---|---|---|---|---|---|
| 1 | Login | Acessar com conta administrativa | Acesso concedido | Acesso concedido | Sim |
| 2 | Gerenciar Usuários | Pesquisar por nome de usuário | Resultados listados | Resultados listados | Sim |
| 3 | Atualizar Conteúdo | Editar informações de recurso educacional | Alterações salvas | Alterações salvas | Sim |
Conclusão dos Testes
Utilizando métodos de caixa preta, os testes simulam interações reais do usuário para verificar fluxos e funcionalidades. O foco na experiência do usuário garante que o sistema seja intuitivo e atenda a expectativas práticas, minimizando complexidades desnecessárias.
Exemplos de Código
Código Backend (Java)
O snippet abaixo demonstra a lógica de autenticação e geração de tokens, com modificações na estrutura e nomenclatura para reduzir similaridade.
// Ponto de acesso para autenticação
@RestController
public class AuthController {
@Autowired
private UserAuthService userAuthService;
@PostMapping("/authenticate")
public ResponseEntity<map object="">> authenticateUser(
@RequestParam String username,
@RequestParam String password) {
Optional<userentity> userOpt = userAuthService.findUserByUsername(username);
if (userOpt.isEmpty() || !userAuthService.validatePassword(userOpt.get(), password)) {
Map<string object=""> errorResponse = new HashMap<>();
errorResponse.put("message", "Credenciais inválidas");
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(errorResponse);
}
UserEntity user = userOpt.get();
String accessToken = userAuthService.createAccessToken(user.getId(), user.getRole());
Map<string object=""> response = new HashMap<>();
response.put("token", accessToken);
return ResponseEntity.ok(response);
}
}
// Serviço para gerenciamento de tokens
@Service
public class UserAuthService {
@Autowired
private TokenRepository tokenRepository;
public String createAccessToken(Long userId, String role) {
TokenEntity existingToken = tokenRepository.findByUserIdAndRole(userId, role);
String newToken = generateSecureToken(32);
Date expirationTime = calculateExpiration(1); // Expira em 1 hora
if (existingToken != null) {
existingToken.setTokenValue(newToken);
existingToken.setExpirationDate(expirationTime);
tokenRepository.save(existingToken);
} else {
TokenEntity tokenEntity = new TokenEntity(userId, role, newToken, expirationTime);
tokenRepository.save(tokenEntity);
}
return newToken;
}
private String generateSecureToken(int length) {
return RandomStringUtils.randomAlphanumeric(length);
}
private Date calculateExpiration(int hours) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.HOUR_OF_DAY, hours);
return calendar.getTime();
}
}
// Interceptor para validação de tokens
@Component
public class AuthInterceptor implements HandlerInterceptor {
private static final String TOKEN_HEADER = "Authorization";
@Autowired
private TokenRepository tokenRepository;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// Configurações de CORS
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
return false;
}
// Verifica anotações de exclusão de autenticação
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
if (handlerMethod.hasMethodAnnotation(NoAuthRequired.class)) {
return true;
}
}
String token = request.getHeader(TOKEN_HEADER);
if (token == null || token.isEmpty()) {
sendUnauthorizedResponse(response);
return false;
}
Optional<tokenentity> tokenOpt = tokenRepository.findByTokenValue(token);
if (tokenOpt.isPresent() && !isTokenExpired(tokenOpt.get())) {
TokenEntity tokenEntity = tokenOpt.get();
request.setAttribute("authenticatedUserId", tokenEntity.getUserId());
request.setAttribute("userRole", tokenEntity.getRole());
return true;
}
sendUnauthorizedResponse(response);
return false;
}
private boolean isTokenExpired(TokenEntity token) {
return token.getExpirationDate().before(new Date());
}
private void sendUnauthorizedResponse(HttpServletResponse response) {
try {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json");
response.getWriter().write("{\"message\": \"Autenticação necessária\"}");
} catch (IOException e) {
// Tratamento de erro
}
}
}
</tokenentity></string></string></userentity></map>
Código de Banco de Dados (SQL)
A estrutura a seguir define tabelas para o sistema, com ajustes nos nomes e estrutura para ilustrar variações.
-- Criação do banco de dados
CREATE DATABASE IF NOT EXISTS student_evaluation_system;
USE student_evaluation_system;
-- Tabela para informações institucionais
CREATE TABLE institutional_info (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
heading VARCHAR(255) NOT NULL,
subheading VARCHAR(255),
body_content TEXT,
media_url_1 TEXT,
media_url_2 TEXT
);
-- Tabela para configurações globais
CREATE TABLE system_settings (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
setting_key VARCHAR(100) NOT NULL UNIQUE,
setting_value VARCHAR(255)
);
-- Inserção de dados iniciais
INSERT INTO institutional_info (heading, subheading, body_content, media_url_1) VALUES
('Sobre Nós', 'Informações Gerais', 'Conteúdo descritivo sobre a plataforma.', 'uploads/info_image.jpg');
INSERT INTO system_settings (setting_key, setting_value) VALUES
('primary_logo', 'uploads/logo.png'),
('theme_color', '#3498db');