Entrodução
Este projeto detalha a concepção e implementação de um Sistema de Gerenciamento de Tarefas utilizando as tecnologias Spring Boot, Vue.js e Uni-app. O sistema visa otimizar a organização e o acompanhamento de tarefas em diversos contextos.
Tecnologias Utilizadas
Backend: Spring Boot
O Spring Boot simplifica o desenvolvimento de aplicações Java com seu ecossistema robusto. Ele inclui servidores embarcados como Tomcat, facilitando a implantação e execução sem configurações adicionais. A auto-configuração, uma de suas principais vantagens, ajusta automaticamente as definições da aplicação com base nas dependências detectadas. Além disso, o Spring Boot oferece uma vasta gama de funcionalidades prontas para uso, como Spring Data para persistência de dados e Spring Security para controle de acesso, permitindo a construção rápida e escalável de aplicações de alta qualidade.
Frontend: Vue.js
Vue.js é um framework JavaScript progressivo conhecido por sua abordagem declarativa e eficiente para a construção de interfaces de usuário. Baseado em um Virtual DOM, ele otimiza as atualizações na interface, permitindo que os desenvolvedores se concentrem na lógica de dados. Sua arquitetura baseada em componentes promove um código mais organizado e reutilizável. A reatividade do Vue.js garante que a interface do usuário seja atualizada automaticamente sempre que os dados subjacentes mudam, proporcionando uma experiência de desenvolvimento ágil e produtiva.
Persistência: MyBatis-Plus
MyBatis-Plus é uma extensão poderosa do framework MyBatis, projetada para simplificar o desenvolvimento de acesso a dados em Java. Ele suporta uma variedade de bancos de dados, como MySQL, Oracle e PostgreSQL, e oferece uma API rica com anotações para facilitar as operações ORM, reduzindo significativamente a necessidade de escrever SQL manualmente. O MyBatis-Plus inclui um gerador de código que automatiza a criação de classes de modelo (Entity), interfaces Mapper e arquivos de mapeamento XML, acelerando o fluxo de desenvolvimento. Funcionalidades como paginação, consultas dinâmicas e controle de concorrência também estão integradas, permitindo um manuseio de dados eficiente e robusto.
Testes do Sistema
A fase de testes é crucial para garantir a qualidade e a confiabilidade do sistema. Nosso objetivo é identificar e corrigir defeitos através de testes funcionais e de integração, assegurando que o sistema atenda às expectativas do usuário e aos requisitos definidos.
Objetivos dos Testes
Os testes de sistema visam validar a aderência do produto aos requisitos especificados e assegurar uma experiência de usuário fluida. Ao simular diversos cenários de uso e identificar potenciais falhas, garantimos que o sistema seja estável e confiável. A validação em múltiplos níveis ajuda a refinar a funcionalidade e a usabilidade, resultando em um produto final de maior qualidade.
Testes de Funcionalidade
Os testes de funcionalidade empregam técnicas de caixa-preta, como testes de valor limite e validação de campos obrigatórios, para verificar cada módulo do sistema. Casos de teste são elaborados para abranger cenários específicos, garantindo que as funcionalidades operem conforme o esperado.
Teste de Login
A funcionalidade de login é testada validando a correspondência entre as credenciais fornecidas e os dados armazenados no banco. O sistema deve fornecer feedback claro em caso de falha de autenticação e validar as permissões de acesso baseadas nos papéis dos usuários.
| Dados de Entrada | Resultado Esperado | Resultado Obtido | Análise |
|---|---|---|---|
| Usuário: admin, Senha: 123456, Captcha: Correto | Login bem-sucedido | Login efetaudo com sucesso | Correspondente ao espeardo |
| Usuário: admin, Senha: 111111, Captcha: Correto | Erro de senha | Mensagem de erro: Senha incorreta | Correspondente ao esperado |
| Usuário: admin, Senha: 123456, Captcha: Incorreto | Erro de Captcha | Mensagem de erro: Captcha inválido | Correspondente ao esperado |
| Usuário: (vazio), Senha: 123456, Captcha: Correto | Campo de usuário obrigatório | Mensagem de erro: Digite o nome de usuário | Correspondente ao esperado |
| Usuário: admin, Senha: (vazio), Captcha: Correto | Campo de senha obrigatório | Mensagem de erro: Digite a senha | Correspondente ao esperado |
Teste de Gerenciamento de Usuários
Esta funcionalidade abrange a adição, edição, exclusão e busca de usuários. Os testes verificam a validação de campos obrigatórios, a detecção de nomes de usuário duplicados, a confirmação de exclusão e a atualização correta das informações do usuário.
| Dados de Entrada | Resultado Esperado | Resultado Obtido | Análise |
|---|---|---|---|
| Informações completas do usuário | Adição bem-sucedida, usuário aparece na lista | Usuário adicionado com sucesso à lista | Correspondente ao esperado |
| Modificação de informações de usuário | Edição bem-sucedida, informações atualizadas | Informações do usuário foram alteradas corretamente | Correspondente ao esperado |
| Seleção para exclusão e confirmação | Sistema solicita confirmação, usuário é removido | Usuário não encontrado após confirmação de exclusão | Correspondente ao esperado |
| Adição sem nome de usuário | Mensagem de erro: Nome de usuário é obrigatório | Mensagem de erro: Nome de usuário é obrigatório | Correspondente ao esperado |
| Adição com nome de usuário existente | Falha na adição, mensagem de erro: Nome de usuário já em uso | Falha na adição, mensagem de erro: Nome de usuário já em uso | Correspondente ao esperado |
Conclusão dos Testes
Através de testes de caixa-preta e casos de uso simulados, validamos a correção dos fluxos e funcionalidades do sistema. A etapa de testes é fundamental para aprimorar a usabilidade e a robustez do sistema, garantindo que ele atenda aos objetivos de design e às necessidades dos usuários. Os testes confirmaram que o sistema, em termos de funcionalidade e performance, cumpre os requisitos estabelecidos.
Exemplo de Código
Controle de Autenticação (Backend)
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.common.utils.R; // Assumindo uma classe R para respostas padronizadas
import com.example.entity.UsersEntity;
import com.example.service.UserService;
import com.example.service.TokenService;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import javax.servlet.http.HttpServletRequest;
import java.util.Calendar;
import java.util.Date;
@RestController
@RequestMapping("/api")
public class AuthController {
private final UserService userService;
private final TokenService tokenService;
public AuthController(UserService userService, TokenService tokenService) {
this.userService = userService;
this.tokenService = tokenService;
}
@IgnoreAuth // Assumindo uma anotação para ignorar autenticação
@PostMapping("/login")
public R login(String username, String password, String captcha, HttpServletRequest request) {
UsersEntity user = userService.selectOne(new EntityWrapper<usersentity>().eq("username", username));
if (user == null || !user.getPassword().equals(password)) {
return R.error("Usuário ou senha incorretos");
}
String token = tokenService.generateAuthToken(user.getUserId(), username, "users", user.getRole());
return R.ok().put("token", token);
}
}
</usersentity>
Serviço de Geração de Token (Backend)
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.example.entity.TokenEntity;
import com.example.mapper.TokenMapper;
import com.example.service.TokenService;
import com.example.common.utils.CommonUtil; // Assumindo utilitário para strings aleatórias
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import org.springframework.stereotype.Service;
import java.util.Calendar;
import java.util.Date;
@Service
public class TokenServiceImpl extends ServiceImpl<tokenmapper tokenentity=""> implements TokenService {
@Override
public String generateAuthToken(Long userId, String username, String tableName, String role) {
TokenEntity existingToken = this.selectOne(new EntityWrapper<tokenentity>().eq("userId", userId).eq("role", role));
String newToken = CommonUtil.generateRandomString(32); // Gera uma string aleatória de 32 caracteres
Calendar expiryCalendar = Calendar.getInstance();
expiryCalendar.setTime(new Date());
expiryCalendar.add(Calendar.HOUR_OF_DAY, 1); // Token válido por 1 hora
if (existingToken != null) {
existingToken.setToken(newToken);
existingToken.setExpiryTime(expiryCalendar.getTime());
this.updateById(existingToken);
} else {
TokenEntity newTokenEntity = new TokenEntity(userId, username, tableName, role, newToken, expiryCalendar.getTime());
this.insert(newTokenEntity);
}
return newToken;
}
}
</tokenentity></tokenmapper>
Interceptors de Autorização (Backend)
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import com.alibaba.fastjson.JSONObject; // Assumindo uso de Fastjson para serialização
import com.example.common.utils.R;
import com.example.entity.TokenEntity;
import com.example.service.TokenService;
import com.example.common.annotation.IgnoreAuth; // Anotação para ignorar autenticação
import org.springframework.util.StringUtils;
import java.io.PrintWriter;
@Component
public class AuthorizationInterceptor implements HandlerInterceptor {
public static final String AUTH_TOKEN_HEADER = "Token";
@Autowired
private TokenService tokenService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// Configurações CORS
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with, request-source, Token, Origin, imgType, Content-Type, cache-control, postman-token, Cookie, Accept, authorization");
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
// Responde a requisições OPTIONS para CORS
if (request.getMethod().equalsIgnoreCase(RequestMethod.OPTIONS.name())) {
response.setStatus(HttpStatus.OK.value());
return false;
}
// Verifica se o handler é um HandlerMethod e se possui a anotação IgnoreAuth
IgnoreAuth ignoreAuthAnnotation = null;
if (handler instanceof HandlerMethod) {
ignoreAuthAnnotation = ((HandlerMethod) handler).getMethodAnnotation(IgnoreAuth.class);
} else if (handler instanceof ResourceHttpRequestHandler) {
// Ignora para recursos estáticos
return true;
}
// Se a anotação IgnoreAuth estiver presente, permite o acesso
if (ignoreAuthAnnotation != null) {
return true;
}
// Obtém o token do header da requisição
String authToken = request.getHeader(AUTH_TOKEN_HEADER);
TokenEntity tokenData = null;
if (StringUtils.hasText(authToken)) {
tokenData = tokenService.getTokenDetails(authToken); // Assumindo método para buscar detalhes do token
}
// Valida o token e os dados do usuário
if (tokenData != null) {
// Armazena informações do usuário na sessão
request.getSession().setAttribute("userId", tokenData.getUserId());
request.getSession().setAttribute("role", tokenData.getRole());
request.getSession().setAttribute("tableName", tokenData.getTableName());
request.getSession().setAttribute("username", tokenData.getUsername());
return true;
}
// Retorna erro de autenticação se o token for inválido ou ausente
PrintWriter writer = null;
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
try {
writer = response.getWriter();
writer.print(JSONObject.toJSONString(R.error(401, "Autenticação necessária")));
} finally {
if (writer != null) {
writer.close();
}
}
return false;
}
}
Estrutura do Banco de Dados (Exemplo: Tabela de Tokens)
-- Tabela para armazenar informações de tokens de autenticação
DROP TABLE IF EXISTS `auth_tokens`;
CREATE TABLE `auth_tokens` (
`token_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID do token',
`user_id` bigint(20) NOT NULL COMMENT 'ID do usuário associado',
`username` varchar(100) NOT NULL COMMENT 'Nome de usuário',
`table_name` varchar(100) DEFAULT NULL COMMENT 'Nome da tabela de origem do usuário',
`role` varchar(100) DEFAULT NULL COMMENT 'Papel/permissão do usuário',
`token_value` varchar(200) NOT NULL COMMENT 'Valor do token',
`creation_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Data e hora de criação',
`expiration_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'Data e hora de expiração',
PRIMARY KEY (`token_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='Tabela de tokens de autenticação';
-- Exemplo de registros na tabela auth_tokens
INSERT INTO `auth_tokens` VALUES
(9, 23, 'student01', 'students', 'Student', 'a1b2c3d4e5f67890abcdef1234567890', '2023-02-23 21:46:45', '2023-03-15 14:01:36'),
(10, 11, 'student02', 'students', 'Student', 'f0e9d8c7b6a54321fedcba0987654321', '2023-02-27 18:33:52', '2023-03-17 18:27:42'),
(12, 1, 'admin_user', 'users', 'Administrator', 'h1i2j3k4l5m6n7o8p9q0r1s2t3u4v5w6', '2023-02-27 19:37:01', '2023-03-17 18:23:02'),
(15, 29, 'club_pres', 'clubs', 'President', '0v1w2x3y4z5a6b7c8d9e0f1a2b3c4d5e', '2023-03-15 12:58:08', '2023-03-15 14:03:48');