diff --git a/.factorypath b/.factorypath
index b62a4f0..40ad18f 100644
--- a/.factorypath
+++ b/.factorypath
@@ -1,51 +1,3 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/.gitignore b/.gitignore
index d1c44b4..7a900d3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,6 +21,7 @@ buildNumber.properties
*.iml
*.ipr
out/
+*.factorypath
# VS Code
.vscode/
diff --git a/COMOUSARAAPI.md b/COMOUSARAAPI.md
new file mode 100644
index 0000000..0f8ba9c
--- /dev/null
+++ b/COMOUSARAAPI.md
@@ -0,0 +1,64 @@
+# Guia de Uso da API - Agenda Digital para Estudantes
+
+Esta API foi desenvolvida em **Java (Spring Boot)** com **MongoDB** para auxiliar estudantes na organização de tarefas e compromissos acadêmicos.
+
+## Autenticação
+
+A maioria dos endpoints requer autenticação via **Token JWT**. Para obtê-lo:
+
+1. Realize o login em `POST /api/estudantes/login`.
+2. Extraia o campo `token` do corpo da resposta.
+3. Inclua o token no cabeçalho de todas as requisições subsequentes:
+
+```
+Authorization: Bearer SEU_TOKEN_AQUI
+```
+
+---
+
+## Endpoints
+
+### Estudantes
+
+| Método | Rota | Descrição |
+|--------|------|-----------|
+| `POST` | `/api/estudantes/cadastro` | Cria um novo estudante |
+| `POST` | `/api/estudantes/login` | Autentica e retorna o token JWT |
+
+### Tarefas
+
+| Método | Rota | Descrição |
+|--------|------|-----------|
+| `POST` | `/api/tarefas` | Cria uma nova tarefa |
+| `GET` | `/api/tarefas/estudante/{id}` | Lista todas as tarefas de um estudante |
+| `GET` | `/api/tarefas/estudante/{id}/pendentes` | Lista apenas as tarefas não concluídas |
+| `GET` | `/api/tarefas/{id}` | Retorna os detalhes de uma tarefa específica |
+| `PUT` | `/api/tarefas/{id}` | Atualiza os dados de uma tarefa existente |
+| `PATCH` | `/api/tarefas/{id}/concluir` | Marca uma tarefa como concluída |
+| `DELETE` | `/api/tarefas/{id}` | Remove uma tarefa |
+
+---
+
+## Formato de Resposta
+
+Todas as respostas da API seguem a estrutura abaixo:
+
+```json
+{
+ "data": { ... },
+ "message": "Sucesso",
+ "timestamp": "2026-03-01T19:00:00"
+}
+```
+
+---
+
+## Execução
+
+```bash
+# Iniciar a aplicação
+mvn spring-boot:run
+
+# Executar os testes
+mvn test
+```
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 7f076b8..95125bd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,6 +19,9 @@
17
+ 17
+ 17
+ 1.18.30
@@ -37,6 +40,39 @@
spring-boot-starter-validation
+
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+
+ io.jsonwebtoken
+ jjwt-api
+ 0.12.3
+
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ 0.12.3
+ runtime
+
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ 0.12.3
+ runtime
+
+
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
+ 2.3.0
+
+
io.github.cdimascio
dotenv-java
@@ -54,10 +90,40 @@
spring-boot-starter-test
test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+ org.springframework.security
+ spring-security-test
+ test
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 17
+ 17
+
+ -parameters
+
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+
+
+
+
org.springframework.boot
spring-boot-maven-plugin
diff --git a/src/main/java/com/agendaestudantil/config/EnvConfig.java b/src/main/java/com/agendaestudantil/config/EnvConfig.java
deleted file mode 100644
index e5482e4..0000000
--- a/src/main/java/com/agendaestudantil/config/EnvConfig.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.agendaestudantil.config;
-
-import io.github.cdimascio.dotenv.Dotenv;
-import io.github.cdimascio.dotenv.DotenvEntry;
-import org.springframework.context.annotation.Configuration;
-
-import jakarta.annotation.PostConstruct;
-
-@Configuration
-public class EnvConfig {
-
- @PostConstruct
- public void init() {
- Dotenv dotenv = Dotenv.configure()
- .ignoreIfMissing()
- .load();
-
- if (dotenv != null) {
- for (io.github.cdimascio.dotenv.DotenvEntry entry : dotenv.entries()) {
- System.setProperty(entry.getKey(), entry.getValue());
- }
- }
- }
-}
diff --git a/src/main/java/com/agendaestudantil/configuracao/ConfiguracaoMongo.java b/src/main/java/com/agendaestudantil/configuracao/ConfiguracaoMongo.java
new file mode 100644
index 0000000..3f5f981
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/configuracao/ConfiguracaoMongo.java
@@ -0,0 +1,9 @@
+package com.agendaestudantil.configuracao;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.mongodb.config.EnableMongoAuditing;
+
+@Configuration
+@EnableMongoAuditing
+public class ConfiguracaoMongo {
+}
\ No newline at end of file
diff --git a/src/main/java/com/agendaestudantil/configuracao/ConfiguracaoSeguranca.java b/src/main/java/com/agendaestudantil/configuracao/ConfiguracaoSeguranca.java
new file mode 100644
index 0000000..bca7f13
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/configuracao/ConfiguracaoSeguranca.java
@@ -0,0 +1,71 @@
+package com.agendaestudantil.configuracao;
+
+import com.agendaestudantil.filtro.FiltroJwt;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.CorsConfigurationSource;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+
+import java.util.List;
+
+@Configuration
+@EnableWebSecurity
+public class ConfiguracaoSeguranca {
+
+ private final FiltroJwt filtroJwt;
+ private final String corsAllowedOrigins;
+
+ public ConfiguracaoSeguranca(FiltroJwt filtroJwt, @Value("${cors.allowed.origins}") String corsAllowedOrigins) {
+ this.filtroJwt = filtroJwt;
+ this.corsAllowedOrigins = corsAllowedOrigins;
+ }
+
+ @Bean
+ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+ http.csrf(csrf -> csrf.disable())
+ .cors(cors -> cors.configurationSource(corsConfigurationSource()))
+ .authorizeHttpRequests(auth -> auth
+ .requestMatchers("/", "/index.html", "/favicon.ico", "/static/**", "/api/estudantes/cadastro",
+ "/api/estudantes/login", "/swagger-ui/**", "/v3/api-docs/**")
+ .permitAll()
+ .anyRequest().authenticated())
+ .sessionManagement(session -> session
+ .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
+ .addFilterBefore(filtroJwt, UsernamePasswordAuthenticationFilter.class);
+
+ return http.build();
+ }
+
+ @Bean
+ public CorsConfigurationSource corsConfigurationSource() {
+ CorsConfiguration configuration = new CorsConfiguration();
+ configuration.setAllowedOrigins(List.of(corsAllowedOrigins.split(",")));
+ configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"));
+ configuration.setAllowedHeaders(List.of("*"));
+ configuration.setAllowCredentials(true);
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+ source.registerCorsConfiguration("/**", configuration);
+ return source;
+ }
+
+ @Bean
+ public PasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
+
+ @Bean
+ public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
+ return config.getAuthenticationManager();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/agendaestudantil/configuracao/ConfiguracaoSwagger.java b/src/main/java/com/agendaestudantil/configuracao/ConfiguracaoSwagger.java
new file mode 100644
index 0000000..cc6806f
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/configuracao/ConfiguracaoSwagger.java
@@ -0,0 +1,29 @@
+package com.agendaestudantil.configuracao;
+
+import io.swagger.v3.oas.models.Components;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Info;
+import io.swagger.v3.oas.models.security.SecurityRequirement;
+import io.swagger.v3.oas.models.security.SecurityScheme;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class ConfiguracaoSwagger {
+
+ @Bean
+ public OpenAPI customOpenAPI() {
+ final String securitySchemeName = "bearerAuth";
+ return new OpenAPI()
+ .info(new Info().title("API Agenda Estudantil")
+ .version("1.0")
+ .description("Documentação da API da Agenda Estudantil"))
+ .addSecurityItem(new SecurityRequirement().addList(securitySchemeName))
+ .components(new Components().addSecuritySchemes(securitySchemeName,
+ new SecurityScheme()
+ .name(securitySchemeName)
+ .type(SecurityScheme.Type.HTTP)
+ .scheme("bearer")
+ .bearerFormat("JWT")));
+ }
+}
diff --git a/src/main/java/com/agendaestudantil/controlador/EstudanteControlador.java b/src/main/java/com/agendaestudantil/controlador/EstudanteControlador.java
new file mode 100644
index 0000000..0435285
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/controlador/EstudanteControlador.java
@@ -0,0 +1,35 @@
+package com.agendaestudantil.controlador;
+
+import com.agendaestudantil.dto.RespostaApi;
+import com.agendaestudantil.dto.RequisicaoCadastroDTO;
+import com.agendaestudantil.dto.RespostaEstudanteDTO;
+import com.agendaestudantil.dto.RequisicaoLoginDTO;
+import com.agendaestudantil.dto.RespostaLoginDTO;
+import com.agendaestudantil.servico.EstudanteServico;
+import jakarta.validation.Valid;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/api/estudantes")
+public class EstudanteControlador {
+
+ private final EstudanteServico estudanteServico;
+
+ public EstudanteControlador(EstudanteServico estudanteServico) {
+ this.estudanteServico = estudanteServico;
+ }
+
+ @PostMapping("/cadastro")
+ public ResponseEntity> cadastrar(@Valid @RequestBody RequisicaoCadastroDTO dto) {
+ RespostaEstudanteDTO resposta = estudanteServico.cadastrar(dto);
+ return ResponseEntity.status(HttpStatus.CREATED).body(RespostaApi.sucesso(resposta));
+ }
+
+ @PostMapping("/login")
+ public ResponseEntity> login(@Valid @RequestBody RequisicaoLoginDTO dto) {
+ RespostaLoginDTO resposta = estudanteServico.login(dto);
+ return ResponseEntity.ok(RespostaApi.sucesso(resposta));
+ }
+}
diff --git a/src/main/java/com/agendaestudantil/controlador/TarefaControlador.java b/src/main/java/com/agendaestudantil/controlador/TarefaControlador.java
new file mode 100644
index 0000000..e078a12
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/controlador/TarefaControlador.java
@@ -0,0 +1,86 @@
+package com.agendaestudantil.controlador;
+
+import com.agendaestudantil.dto.RespostaApi;
+import com.agendaestudantil.dto.RequisicaoTarefaDTO;
+import com.agendaestudantil.dto.RespostaTarefaDTO;
+import com.agendaestudantil.servico.TarefaServico;
+import jakarta.validation.Valid;
+import org.springframework.format.annotation.DateTimeFormat;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import java.time.LocalDate;
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/tarefas")
+public class TarefaControlador {
+
+ private final TarefaServico tarefaServico;
+
+ public TarefaControlador(TarefaServico tarefaServico) {
+ this.tarefaServico = tarefaServico;
+ }
+
+ @PostMapping
+ public ResponseEntity> criarTarefa(@Valid @RequestBody RequisicaoTarefaDTO dto) {
+ RespostaTarefaDTO tarefa = tarefaServico.criarTarefa(dto);
+ return ResponseEntity.status(HttpStatus.CREATED).body(RespostaApi.sucesso(tarefa));
+ }
+
+ @GetMapping("/estudante/{estudanteId}")
+ public ResponseEntity>> listarTarefasPorEstudante(
+ @PathVariable String estudanteId) {
+ List tarefas = tarefaServico.listarTarefasPorEstudante(estudanteId);
+ return ResponseEntity.ok(RespostaApi.sucesso(tarefas));
+ }
+
+ @GetMapping("/estudante/{estudanteId}/pendentes")
+ public ResponseEntity>> listarTarefasPendentes(
+ @PathVariable String estudanteId) {
+ List tarefas = tarefaServico.listarTarefasPendentes(estudanteId);
+ return ResponseEntity.ok(RespostaApi.sucesso(tarefas));
+ }
+
+ @GetMapping("/estudante/{estudanteId}/data")
+ public ResponseEntity>> listarTarefasPorData(
+ @PathVariable String estudanteId,
+ @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate data) {
+ List tarefas = tarefaServico.listarTarefasPorData(estudanteId, data);
+ return ResponseEntity.ok(RespostaApi.sucesso(tarefas));
+ }
+
+ @GetMapping("/estudante/{estudanteId}/periodo")
+ public ResponseEntity>> listarTarefasPorPeriodo(
+ @PathVariable String estudanteId,
+ @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate inicio,
+ @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate fim) {
+ List tarefas = tarefaServico.listarTarefasPorPeriodo(estudanteId, inicio, fim);
+ return ResponseEntity.ok(RespostaApi.sucesso(tarefas));
+ }
+
+ @GetMapping("/{id}")
+ public ResponseEntity> buscarTarefaPorId(@PathVariable String id) {
+ RespostaTarefaDTO dto = tarefaServico.buscarTarefaPorId(id);
+ return ResponseEntity.ok(RespostaApi.sucesso(dto));
+ }
+
+ @PutMapping("/{id}")
+ public ResponseEntity> atualizarTarefa(@PathVariable String id,
+ @Valid @RequestBody RequisicaoTarefaDTO dto) {
+ RespostaTarefaDTO tarefa = tarefaServico.atualizarTarefa(id, dto);
+ return ResponseEntity.ok(RespostaApi.sucesso(tarefa));
+ }
+
+ @DeleteMapping("/{id}")
+ public ResponseEntity> excluirTarefa(@PathVariable String id) {
+ tarefaServico.excluirTarefa(id);
+ return ResponseEntity.status(HttpStatus.NO_CONTENT).body(RespostaApi.sucesso(null));
+ }
+
+ @PatchMapping("/{id}/concluir")
+ public ResponseEntity> marcarConcluida(@PathVariable String id) {
+ RespostaTarefaDTO tarefa = tarefaServico.marcarConcluida(id);
+ return ResponseEntity.ok(RespostaApi.sucesso(tarefa));
+ }
+}
diff --git a/src/main/java/com/agendaestudantil/controller/EstudanteController.java b/src/main/java/com/agendaestudantil/controller/EstudanteController.java
deleted file mode 100644
index ec30fda..0000000
--- a/src/main/java/com/agendaestudantil/controller/EstudanteController.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.agendaestudantil.controller;
-
-import com.agendaestudantil.dto.CadastroRequestDTO;
-import com.agendaestudantil.dto.EstudanteResponseDTO;
-import com.agendaestudantil.dto.LoginRequestDTO;
-import com.agendaestudantil.service.EstudanteService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.*;
-
-@RestController
-@RequestMapping("/api/estudantes")
-@CrossOrigin(origins = "*")
-public class EstudanteController {
-
- @Autowired
- private EstudanteService estudanteService;
-
- @PostMapping("/cadastro")
- public ResponseEntity cadastrar(@RequestBody CadastroRequestDTO dto) {
- EstudanteResponseDTO resposta = estudanteService.cadastrar(dto);
- return ResponseEntity.status(HttpStatus.CREATED).body(resposta);
- }
-
- @PostMapping("/login")
- public ResponseEntity login(@RequestBody LoginRequestDTO dto) {
- EstudanteResponseDTO resposta = estudanteService.login(dto);
- return ResponseEntity.ok(resposta);
- }
-}
diff --git a/src/main/java/com/agendaestudantil/controller/TarefaController.java b/src/main/java/com/agendaestudantil/controller/TarefaController.java
deleted file mode 100644
index d9f4931..0000000
--- a/src/main/java/com/agendaestudantil/controller/TarefaController.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package com.agendaestudantil.controller;
-
-import com.agendaestudantil.dto.TarefaRequestDTO;
-import com.agendaestudantil.entity.Tarefa;
-import com.agendaestudantil.service.TarefaService;
-import jakarta.validation.Valid;
-import lombok.RequiredArgsConstructor;
-import org.springframework.format.annotation.DateTimeFormat;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.*;
-import java.time.LocalDate;
-import java.util.List;
-
-@RestController
-@RequestMapping("/api/tarefas")
-@RequiredArgsConstructor
-public class TarefaController {
-
- private final TarefaService tarefaService;
-
- @PostMapping
- public ResponseEntity criarTarefa(@Valid @RequestBody TarefaRequestDTO dto) {
- Tarefa tarefa = tarefaService.criarTarefa(dto);
- return ResponseEntity.status(HttpStatus.CREATED).body(tarefa);
- }
-
- @GetMapping("/estudante/{estudanteId}")
- public ResponseEntity> listarTarefasPorEstudante(@PathVariable String estudanteId) {
- List tarefas = tarefaService.listarTarefasPorEstudante(estudanteId);
- return ResponseEntity.ok(tarefas);
- }
-
- @GetMapping("/estudante/{estudanteId}/pendentes")
- public ResponseEntity> listarTarefasPendentes(@PathVariable String estudanteId) {
- List tarefas = tarefaService.listarTarefasPendentes(estudanteId);
- return ResponseEntity.ok(tarefas);
- }
-
- @GetMapping("/estudante/{estudanteId}/data")
- public ResponseEntity> listarTarefasPorData(
- @PathVariable String estudanteId,
- @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate data) {
- List tarefas = tarefaService.listarTarefasPorData(estudanteId, data);
- return ResponseEntity.ok(tarefas);
- }
-
- @GetMapping("/estudante/{estudanteId}/periodo")
- public ResponseEntity> listarTarefasPorPeriodo(
- @PathVariable String estudanteId,
- @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate inicio,
- @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate fim) {
- List tarefas = tarefaService.listarTarefasPorPeriodo(estudanteId, inicio, fim);
- return ResponseEntity.ok(tarefas);
- }
-
- @GetMapping("/{id}")
- public ResponseEntity buscarTarefaPorId(@PathVariable String id) {
- return tarefaService.buscarTarefaPorId(id)
- .map(ResponseEntity::ok)
- .orElse(ResponseEntity.notFound().build());
- }
-
- @PutMapping("/{id}")
- public ResponseEntity atualizarTarefa(@PathVariable String id, @Valid @RequestBody TarefaRequestDTO dto) {
- Tarefa tarefa = tarefaService.atualizarTarefa(id, dto);
- return ResponseEntity.ok(tarefa);
- }
-
- @DeleteMapping("/{id}")
- public ResponseEntity excluirTarefa(@PathVariable String id) {
- tarefaService.excluirTarefa(id);
- return ResponseEntity.noContent().build();
- }
-
- @PatchMapping("/{id}/concluir")
- public ResponseEntity marcarConcluida(@PathVariable String id) {
- Tarefa tarefa = tarefaService.marcarConcluida(id);
- return ResponseEntity.ok(tarefa);
- }
-}
diff --git a/src/main/java/com/agendaestudantil/dto/CadastroRequestDTO.java b/src/main/java/com/agendaestudantil/dto/CadastroRequestDTO.java
deleted file mode 100644
index 0b2728e..0000000
--- a/src/main/java/com/agendaestudantil/dto/CadastroRequestDTO.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.agendaestudantil.dto;
-
-import lombok.Data;
-import lombok.NoArgsConstructor;
-import lombok.AllArgsConstructor;
-
-@Data
-@NoArgsConstructor
-@AllArgsConstructor
-public class CadastroRequestDTO {
- private String nome;
- private String email;
- private String senha;
- private String curso;
- private Integer periodo;
-}
diff --git a/src/main/java/com/agendaestudantil/dto/EstudanteResponseDTO.java b/src/main/java/com/agendaestudantil/dto/EstudanteResponseDTO.java
deleted file mode 100644
index 4fc9552..0000000
--- a/src/main/java/com/agendaestudantil/dto/EstudanteResponseDTO.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.agendaestudantil.dto;
-
-import lombok.Data;
-import lombok.NoArgsConstructor;
-import lombok.AllArgsConstructor;
-
-@Data
-@NoArgsConstructor
-@AllArgsConstructor
-public class EstudanteResponseDTO {
- private String id;
- private String nome;
- private String email;
- private String curso;
- private Integer periodo;
-}
diff --git a/src/main/java/com/agendaestudantil/dto/LoginRequestDTO.java b/src/main/java/com/agendaestudantil/dto/LoginRequestDTO.java
deleted file mode 100644
index beb1d90..0000000
--- a/src/main/java/com/agendaestudantil/dto/LoginRequestDTO.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.agendaestudantil.dto;
-
-import lombok.Data;
-import lombok.NoArgsConstructor;
-import lombok.AllArgsConstructor;
-
-@Data
-@NoArgsConstructor
-@AllArgsConstructor
-public class LoginRequestDTO {
- private String email;
- private String senha;
-}
diff --git a/src/main/java/com/agendaestudantil/dto/RequisicaoCadastroDTO.java b/src/main/java/com/agendaestudantil/dto/RequisicaoCadastroDTO.java
new file mode 100644
index 0000000..7c1dee2
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/dto/RequisicaoCadastroDTO.java
@@ -0,0 +1,16 @@
+package com.agendaestudantil.dto;
+
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+
+public record RequisicaoCadastroDTO(
+ @NotBlank String nome,
+ @Email @NotBlank String email,
+ @NotBlank @Size(min = 6) String senha,
+ @NotBlank String curso,
+ @NotNull @Min(1) Integer periodo
+) {
+}
diff --git a/src/main/java/com/agendaestudantil/dto/RequisicaoLoginDTO.java b/src/main/java/com/agendaestudantil/dto/RequisicaoLoginDTO.java
new file mode 100644
index 0000000..fc73032
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/dto/RequisicaoLoginDTO.java
@@ -0,0 +1,10 @@
+package com.agendaestudantil.dto;
+
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.NotBlank;
+
+public record RequisicaoLoginDTO(
+ @Email @NotBlank String email,
+ @NotBlank String senha
+) {
+}
diff --git a/src/main/java/com/agendaestudantil/dto/RequisicaoTarefaDTO.java b/src/main/java/com/agendaestudantil/dto/RequisicaoTarefaDTO.java
new file mode 100644
index 0000000..4295812
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/dto/RequisicaoTarefaDTO.java
@@ -0,0 +1,19 @@
+package com.agendaestudantil.dto;
+
+import com.agendaestudantil.entidade.Tarefa;
+import jakarta.validation.constraints.Future;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+
+import java.time.LocalDate;
+
+public record RequisicaoTarefaDTO(
+ @NotBlank String titulo,
+ String descricao,
+ @NotNull Tarefa.Prioridade prioridade,
+ Tarefa.StatusTarefa status,
+ @NotNull @Future LocalDate dataEntrega,
+ String disciplinaId,
+ @NotBlank String estudanteId
+) {
+}
diff --git a/src/main/java/com/agendaestudantil/dto/RespostaApi.java b/src/main/java/com/agendaestudantil/dto/RespostaApi.java
new file mode 100644
index 0000000..69ef1a2
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/dto/RespostaApi.java
@@ -0,0 +1,9 @@
+package com.agendaestudantil.dto;
+
+import java.time.LocalDateTime;
+
+public record RespostaApi(T data, String message, LocalDateTime timestamp) {
+ public static RespostaApi sucesso(T data) {
+ return new RespostaApi<>(data, "Sucesso", LocalDateTime.now());
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/agendaestudantil/dto/RespostaDisciplinaDTO.java b/src/main/java/com/agendaestudantil/dto/RespostaDisciplinaDTO.java
new file mode 100644
index 0000000..360ebf8
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/dto/RespostaDisciplinaDTO.java
@@ -0,0 +1,10 @@
+package com.agendaestudantil.dto;
+
+public record RespostaDisciplinaDTO(
+ String id,
+ String estudanteId,
+ String nome,
+ String professor,
+ String sala,
+ String cor
+) {}
diff --git a/src/main/java/com/agendaestudantil/dto/RespostaEstudanteDTO.java b/src/main/java/com/agendaestudantil/dto/RespostaEstudanteDTO.java
new file mode 100644
index 0000000..264d88a
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/dto/RespostaEstudanteDTO.java
@@ -0,0 +1,10 @@
+package com.agendaestudantil.dto;
+
+public record RespostaEstudanteDTO(
+ String id,
+ String nome,
+ String email,
+ String curso,
+ Integer periodo
+) {
+}
diff --git a/src/main/java/com/agendaestudantil/dto/RespostaEventoDTO.java b/src/main/java/com/agendaestudantil/dto/RespostaEventoDTO.java
new file mode 100644
index 0000000..82b764d
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/dto/RespostaEventoDTO.java
@@ -0,0 +1,14 @@
+package com.agendaestudantil.dto;
+
+import java.time.LocalDateTime;
+
+public record RespostaEventoDTO(
+ String id,
+ String estudanteId,
+ String titulo,
+ String descricao,
+ String tipo,
+ String local,
+ String disciplinaId,
+ LocalDateTime dataHora
+) {}
diff --git a/src/main/java/com/agendaestudantil/dto/RespostaLoginDTO.java b/src/main/java/com/agendaestudantil/dto/RespostaLoginDTO.java
new file mode 100644
index 0000000..3cc2eb2
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/dto/RespostaLoginDTO.java
@@ -0,0 +1,4 @@
+package com.agendaestudantil.dto;
+
+public record RespostaLoginDTO(String token, RespostaEstudanteDTO estudante) {
+}
diff --git a/src/main/java/com/agendaestudantil/dto/RespostaTarefaDTO.java b/src/main/java/com/agendaestudantil/dto/RespostaTarefaDTO.java
new file mode 100644
index 0000000..ae18890
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/dto/RespostaTarefaDTO.java
@@ -0,0 +1,14 @@
+package com.agendaestudantil.dto;
+
+import java.time.LocalDate;
+
+public record RespostaTarefaDTO(
+ String id,
+ String titulo,
+ String descricao,
+ String prioridade,
+ String status,
+ LocalDate dataEntrega,
+ String disciplinaId,
+ String estudanteId
+) {}
diff --git a/src/main/java/com/agendaestudantil/dto/TarefaRequestDTO.java b/src/main/java/com/agendaestudantil/dto/TarefaRequestDTO.java
deleted file mode 100644
index bafe02c..0000000
--- a/src/main/java/com/agendaestudantil/dto/TarefaRequestDTO.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.agendaestudantil.dto;
-
-import com.agendaestudantil.entity.Tarefa;
-import jakarta.validation.constraints.NotBlank;
-import jakarta.validation.constraints.NotNull;
-import lombok.Data;
-import java.time.LocalDate;
-
-@Data
-public class TarefaRequestDTO {
-
- @NotBlank(message = "Título é obrigatório")
- private String titulo;
-
- private String descricao;
-
- private Tarefa.Prioridade prioridade;
-
- private Tarefa.StatusTarefa status;
-
- @NotNull(message = "Data de entrega é obrigatória")
- private LocalDate dataEntrega;
-
- private String disciplinaId;
-
- @NotBlank(message = "ID do estudante é obrigatório")
- private String estudanteId;
-}
diff --git a/src/main/java/com/agendaestudantil/entidade/Disciplina.java b/src/main/java/com/agendaestudantil/entidade/Disciplina.java
new file mode 100644
index 0000000..e3b41db
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/entidade/Disciplina.java
@@ -0,0 +1,97 @@
+package com.agendaestudantil.entidade;
+
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.index.CompoundIndex;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+import java.util.Objects;
+
+@Document(collection = "disciplinas")
+@CompoundIndex(name = "estudante_nome_idx", def = "{estudanteId: 1, nome: 1}")
+public class Disciplina extends EntidadeAuditoria {
+
+ @Id
+ private String id;
+ private String estudanteId;
+ private String nome;
+ private String professor;
+ private String sala;
+ private String cor;
+
+ public Disciplina() {
+ }
+
+ public Disciplina(String id, String estudanteId, String nome, String professor, String sala, String cor) {
+ this.id = id;
+ this.estudanteId = estudanteId;
+ this.nome = nome;
+ this.professor = professor;
+ this.sala = sala;
+ this.cor = cor;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getEstudanteId() {
+ return estudanteId;
+ }
+
+ public void setEstudanteId(String estudanteId) {
+ this.estudanteId = estudanteId;
+ }
+
+ public String getNome() {
+ return nome;
+ }
+
+ public void setNome(String nome) {
+ this.nome = nome;
+ }
+
+ public String getProfessor() {
+ return professor;
+ }
+
+ public void setProfessor(String professor) {
+ this.professor = professor;
+ }
+
+ public String getSala() {
+ return sala;
+ }
+
+ public void setSala(String sala) {
+ this.sala = sala;
+ }
+
+ public String getCor() {
+ return cor;
+ }
+
+ public void setCor(String cor) {
+ this.cor = cor;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ Disciplina that = (Disciplina) o;
+ return Objects.equals(id, that.id) &&
+ Objects.equals(estudanteId, that.estudanteId) &&
+ Objects.equals(nome, that.nome);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, estudanteId, nome);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/agendaestudantil/entidade/EntidadeAuditoria.java b/src/main/java/com/agendaestudantil/entidade/EntidadeAuditoria.java
new file mode 100644
index 0000000..63b8048
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/entidade/EntidadeAuditoria.java
@@ -0,0 +1,30 @@
+package com.agendaestudantil.entidade;
+
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.LastModifiedDate;
+import java.time.LocalDateTime;
+
+public abstract class EntidadeAuditoria {
+
+ @CreatedDate
+ private LocalDateTime dataCriacao;
+
+ @LastModifiedDate
+ private LocalDateTime dataAtualizacao;
+
+ public LocalDateTime getDataCriacao() {
+ return dataCriacao;
+ }
+
+ public void setDataCriacao(LocalDateTime dataCriacao) {
+ this.dataCriacao = dataCriacao;
+ }
+
+ public LocalDateTime getDataAtualizacao() {
+ return dataAtualizacao;
+ }
+
+ public void setDataAtualizacao(LocalDateTime dataAtualizacao) {
+ this.dataAtualizacao = dataAtualizacao;
+ }
+}
diff --git a/src/main/java/com/agendaestudantil/entidade/Estudante.java b/src/main/java/com/agendaestudantil/entidade/Estudante.java
new file mode 100644
index 0000000..7295ce8
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/entidade/Estudante.java
@@ -0,0 +1,101 @@
+package com.agendaestudantil.entidade;
+
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.index.Indexed;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+import java.util.Objects;
+
+@Document(collection = "estudantes")
+public class Estudante extends EntidadeAuditoria {
+
+ @Id
+ private String id;
+
+ @Indexed(unique = true)
+ private String email;
+
+ private String nome;
+
+ private String senha;
+
+ private String curso;
+
+ private Integer periodo;
+
+ public Estudante() {
+ }
+
+ public Estudante(String id, String email, String nome, String senha, String curso, Integer periodo) {
+ this.id = id;
+ this.email = email;
+ this.nome = nome;
+ this.senha = senha;
+ this.curso = curso;
+ this.periodo = periodo;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getNome() {
+ return nome;
+ }
+
+ public void setNome(String nome) {
+ this.nome = nome;
+ }
+
+ public String getSenha() {
+ return senha;
+ }
+
+ public void setSenha(String senha) {
+ this.senha = senha;
+ }
+
+ public String getCurso() {
+ return curso;
+ }
+
+ public void setCurso(String curso) {
+ this.curso = curso;
+ }
+
+ public Integer getPeriodo() {
+ return periodo;
+ }
+
+ public void setPeriodo(Integer periodo) {
+ this.periodo = periodo;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ Estudante estudante = (Estudante) o;
+ return Objects.equals(id, estudante.id) &&
+ Objects.equals(email, estudante.email);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, email);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/agendaestudantil/entidade/Evento.java b/src/main/java/com/agendaestudantil/entidade/Evento.java
new file mode 100644
index 0000000..3c35b99
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/entidade/Evento.java
@@ -0,0 +1,123 @@
+package com.agendaestudantil.entidade;
+
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.index.CompoundIndex;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+import java.time.LocalDateTime;
+import java.util.Objects;
+
+@Document(collection = "eventos")
+@CompoundIndex(name = "estudante_data_hora_idx", def = "{estudanteId: 1, dataHora: 1}")
+public class Evento extends EntidadeAuditoria {
+
+ @Id
+ private String id;
+ private String estudanteId;
+ private String titulo;
+ private String descricao;
+ private TipoEvento tipo;
+ private String local;
+ private String disciplinaId;
+ private LocalDateTime dataHora;
+
+ public Evento() {
+ }
+
+ public Evento(String id, String estudanteId, String titulo, String descricao, TipoEvento tipo, String local,
+ String disciplinaId, LocalDateTime dataHora) {
+ this.id = id;
+ this.estudanteId = estudanteId;
+ this.titulo = titulo;
+ this.descricao = descricao;
+ this.tipo = tipo;
+ this.local = local;
+ this.disciplinaId = disciplinaId;
+ this.dataHora = dataHora;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getEstudanteId() {
+ return estudanteId;
+ }
+
+ public void setEstudanteId(String estudanteId) {
+ this.estudanteId = estudanteId;
+ }
+
+ public String getTitulo() {
+ return titulo;
+ }
+
+ public void setTitulo(String titulo) {
+ this.titulo = titulo;
+ }
+
+ public String getDescricao() {
+ return descricao;
+ }
+
+ public void setDescricao(String descricao) {
+ this.descricao = descricao;
+ }
+
+ public TipoEvento getTipo() {
+ return tipo;
+ }
+
+ public void setTipo(TipoEvento tipo) {
+ this.tipo = tipo;
+ }
+
+ public String getLocal() {
+ return local;
+ }
+
+ public void setLocal(String local) {
+ this.local = local;
+ }
+
+ public String getDisciplinaId() {
+ return disciplinaId;
+ }
+
+ public void setDisciplinaId(String disciplinaId) {
+ this.disciplinaId = disciplinaId;
+ }
+
+ public LocalDateTime getDataHora() {
+ return dataHora;
+ }
+
+ public void setDataHora(LocalDateTime dataHora) {
+ this.dataHora = dataHora;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ Evento evento = (Evento) o;
+ return Objects.equals(id, evento.id) &&
+ Objects.equals(estudanteId, evento.estudanteId) &&
+ Objects.equals(titulo, evento.titulo);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, estudanteId, titulo);
+ }
+
+ public enum TipoEvento {
+ AULA, PROVA, TRABALHO, ESTUDO, EXAME, OUTRO
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/agendaestudantil/entidade/Tarefa.java b/src/main/java/com/agendaestudantil/entidade/Tarefa.java
new file mode 100644
index 0000000..47f37d3
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/entidade/Tarefa.java
@@ -0,0 +1,131 @@
+package com.agendaestudantil.entidade;
+
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.index.CompoundIndex;
+import org.springframework.data.mongodb.core.index.CompoundIndexes;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+import java.time.LocalDate;
+import java.util.Objects;
+
+@Document(collection = "tarefas")
+@CompoundIndexes({
+ @CompoundIndex(name = "estudante_data_entrega_idx", def = "{estudanteId: 1, dataEntrega: 1}"),
+ @CompoundIndex(name = "estudante_status_idx", def = "{estudanteId: 1, status: 1}")
+})
+public class Tarefa extends EntidadeAuditoria {
+
+ @Id
+ private String id;
+ private String titulo;
+ private String descricao;
+ private Prioridade prioridade;
+ private StatusTarefa status;
+ private LocalDate dataEntrega;
+ private String disciplinaId;
+ private String estudanteId;
+
+ public Tarefa() {
+ }
+
+ public Tarefa(String id, String titulo, String descricao, Prioridade prioridade, StatusTarefa status,
+ LocalDate dataEntrega, String disciplinaId, String estudanteId) {
+ this.id = id;
+ this.titulo = titulo;
+ this.descricao = descricao;
+ this.prioridade = prioridade;
+ this.status = status;
+ this.dataEntrega = dataEntrega;
+ this.disciplinaId = disciplinaId;
+ this.estudanteId = estudanteId;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getTitulo() {
+ return titulo;
+ }
+
+ public void setTitulo(String titulo) {
+ this.titulo = titulo;
+ }
+
+ public String getDescricao() {
+ return descricao;
+ }
+
+ public void setDescricao(String descricao) {
+ this.descricao = descricao;
+ }
+
+ public Prioridade getPrioridade() {
+ return prioridade;
+ }
+
+ public void setPrioridade(Prioridade prioridade) {
+ this.prioridade = prioridade;
+ }
+
+ public StatusTarefa getStatus() {
+ return status;
+ }
+
+ public void setStatus(StatusTarefa status) {
+ this.status = status;
+ }
+
+ public LocalDate getDataEntrega() {
+ return dataEntrega;
+ }
+
+ public void setDataEntrega(LocalDate dataEntrega) {
+ this.dataEntrega = dataEntrega;
+ }
+
+ public String getDisciplinaId() {
+ return disciplinaId;
+ }
+
+ public void setDisciplinaId(String disciplinaId) {
+ this.disciplinaId = disciplinaId;
+ }
+
+ public String getEstudanteId() {
+ return estudanteId;
+ }
+
+ public void setEstudanteId(String estudanteId) {
+ this.estudanteId = estudanteId;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ Tarefa tarefa = (Tarefa) o;
+ return Objects.equals(id, tarefa.id) &&
+ Objects.equals(estudanteId, tarefa.estudanteId) &&
+ Objects.equals(titulo, tarefa.titulo);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, estudanteId, titulo);
+ }
+
+ public enum Prioridade {
+ BAIXA, MEDIA, ALTA, URGENTE
+ }
+
+ public enum StatusTarefa {
+ PENDENTE, EM_ANDAMENTO, CONCLUIDA, ATRASADA
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/agendaestudantil/entity/Disciplina.java b/src/main/java/com/agendaestudantil/entity/Disciplina.java
deleted file mode 100644
index 6aecb29..0000000
--- a/src/main/java/com/agendaestudantil/entity/Disciplina.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.agendaestudantil.entity;
-
-import lombok.Data;
-import lombok.NoArgsConstructor;
-import lombok.AllArgsConstructor;
-import org.springframework.data.annotation.Id;
-import org.springframework.data.mongodb.core.mapping.Document;
-import java.time.LocalDateTime;
-
-@Document(collection = "disciplinas")
-@Data
-@NoArgsConstructor
-@AllArgsConstructor
-public class Disciplina {
-
- @Id
- private String id;
-
- private String nome;
-
- private String professor;
-
- private String sala;
-
- private String cor;
-
- private String estudanteId;
-
- private LocalDateTime dataCriacao;
-}
diff --git a/src/main/java/com/agendaestudantil/entity/Estudante.java b/src/main/java/com/agendaestudantil/entity/Estudante.java
deleted file mode 100644
index 489185d..0000000
--- a/src/main/java/com/agendaestudantil/entity/Estudante.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.agendaestudantil.entity;
-
-import lombok.Data;
-import lombok.NoArgsConstructor;
-import lombok.AllArgsConstructor;
-import org.springframework.data.annotation.Id;
-import org.springframework.data.mongodb.core.mapping.Document;
-import java.time.LocalDateTime;
-
-@Document(collection = "estudantes")
-@Data
-@NoArgsConstructor
-@AllArgsConstructor
-public class Estudante {
-
- @Id
- private String id;
-
- private String nome;
-
- private String email;
-
- private String senha;
-
- private String curso;
-
- private Integer periodo;
-
- private LocalDateTime dataCriacao;
-
- private LocalDateTime dataAtualizacao;
-}
diff --git a/src/main/java/com/agendaestudantil/entity/Evento.java b/src/main/java/com/agendaestudantil/entity/Evento.java
deleted file mode 100644
index e319745..0000000
--- a/src/main/java/com/agendaestudantil/entity/Evento.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package com.agendaestudantil.entity;
-
-import lombok.Data;
-import lombok.NoArgsConstructor;
-import lombok.AllArgsConstructor;
-import org.springframework.data.annotation.Id;
-import org.springframework.data.mongodb.core.mapping.Document;
-import java.time.LocalDateTime;
-
-@Document(collection = "eventos")
-@Data
-@NoArgsConstructor
-@AllArgsConstructor
-public class Evento {
-
- @Id
- private String id;
-
- private String titulo;
-
- private String descricao;
-
- private TipoEvento tipo;
-
- private LocalDateTime dataInicio;
-
- private LocalDateTime dataFim;
-
- private String local;
-
- private String disciplinaId;
-
- private String estudanteId;
-
- private LocalDateTime dataCriacao;
-
- public enum TipoEvento {
- AULA, PROVA, TRABALHO, ATIVIDADE, EVENTO, LEMBRETE
- }
-}
diff --git a/src/main/java/com/agendaestudantil/entity/Tarefa.java b/src/main/java/com/agendaestudantil/entity/Tarefa.java
deleted file mode 100644
index 1b61ed5..0000000
--- a/src/main/java/com/agendaestudantil/entity/Tarefa.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.agendaestudantil.entity;
-
-import lombok.Data;
-import lombok.NoArgsConstructor;
-import lombok.AllArgsConstructor;
-import org.springframework.data.annotation.Id;
-import org.springframework.data.mongodb.core.mapping.Document;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-
-@Document(collection = "tarefas")
-@Data
-@NoArgsConstructor
-@AllArgsConstructor
-public class Tarefa {
-
- @Id
- private String id;
-
- private String titulo;
-
- private String descricao;
-
- private Prioridade prioridade;
-
- private StatusTarefa status;
-
- private LocalDate dataEntrega;
-
- private String disciplinaId;
-
- private String estudanteId;
-
- private LocalDateTime dataCriacao;
-
- private LocalDateTime dataAtualizacao;
-
- public enum Prioridade {
- BAIXA, MEDIA, ALTA, URGENTE
- }
-
- public enum StatusTarefa {
- PENDENTE, EM_ANDAMENTO, CONCLUIDA, ATRASADA
- }
-}
diff --git a/src/main/java/com/agendaestudantil/excecao/ExcecaoNegocio.java b/src/main/java/com/agendaestudantil/excecao/ExcecaoNegocio.java
new file mode 100644
index 0000000..d516f1e
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/excecao/ExcecaoNegocio.java
@@ -0,0 +1,7 @@
+package com.agendaestudantil.excecao;
+
+public class ExcecaoNegocio extends RuntimeException {
+ public ExcecaoNegocio(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/com/agendaestudantil/excecao/ExcecaoRecursoNaoEncontrado.java b/src/main/java/com/agendaestudantil/excecao/ExcecaoRecursoNaoEncontrado.java
new file mode 100644
index 0000000..a099dd3
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/excecao/ExcecaoRecursoNaoEncontrado.java
@@ -0,0 +1,7 @@
+package com.agendaestudantil.excecao;
+
+public class ExcecaoRecursoNaoEncontrado extends RuntimeException {
+ public ExcecaoRecursoNaoEncontrado(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/com/agendaestudantil/excecao/ManipuladorExcecaoGlobal.java b/src/main/java/com/agendaestudantil/excecao/ManipuladorExcecaoGlobal.java
new file mode 100644
index 0000000..a3dc5d2
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/excecao/ManipuladorExcecaoGlobal.java
@@ -0,0 +1,53 @@
+package com.agendaestudantil.excecao;
+
+import com.agendaestudantil.dto.RespostaApi;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.validation.FieldError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.server.ResponseStatusException;
+
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.Map;
+
+@RestControllerAdvice
+public class ManipuladorExcecaoGlobal {
+
+ @ExceptionHandler(ExcecaoRecursoNaoEncontrado.class)
+ public ResponseEntity> handleResourceNotFound(ExcecaoRecursoNaoEncontrado ex) {
+ return ResponseEntity.status(HttpStatus.NOT_FOUND)
+ .body(new RespostaApi<>(null, ex.getMessage(), LocalDateTime.now()));
+ }
+
+ @ExceptionHandler(ExcecaoNegocio.class)
+ public ResponseEntity> handleExcecaoNegocio(ExcecaoNegocio ex) {
+ return ResponseEntity.status(HttpStatus.BAD_REQUEST)
+ .body(new RespostaApi<>(null, ex.getMessage(), LocalDateTime.now()));
+ }
+
+ @ExceptionHandler(MethodArgumentNotValidException.class)
+ public ResponseEntity>> handleValidationException(
+ MethodArgumentNotValidException ex) {
+ Map errors = new HashMap<>();
+ for (FieldError error : ex.getBindingResult().getFieldErrors()) {
+ errors.put(error.getField(), error.getDefaultMessage());
+ }
+ return ResponseEntity.status(HttpStatus.BAD_REQUEST)
+ .body(new RespostaApi<>(errors, "Falha na validação", LocalDateTime.now()));
+ }
+
+ @ExceptionHandler(ResponseStatusException.class)
+ public ResponseEntity> handleResponseStatusException(ResponseStatusException ex) {
+ return ResponseEntity.status(ex.getStatusCode())
+ .body(new RespostaApi<>(null, ex.getReason(), LocalDateTime.now()));
+ }
+
+ @ExceptionHandler(Exception.class)
+ public ResponseEntity> handleGenericException(Exception ex) {
+ return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
+ .body(new RespostaApi<>(null, "Erro interno no servidor: " + ex.getMessage(), LocalDateTime.now()));
+ }
+}
diff --git a/src/main/java/com/agendaestudantil/filtro/FiltroJwt.java b/src/main/java/com/agendaestudantil/filtro/FiltroJwt.java
new file mode 100644
index 0000000..12713e9
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/filtro/FiltroJwt.java
@@ -0,0 +1,53 @@
+package com.agendaestudantil.filtro;
+
+import com.agendaestudantil.utilitario.UtilJwt;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import java.io.IOException;
+
+@Component
+public class FiltroJwt extends OncePerRequestFilter {
+
+ private final UtilJwt utilJwt;
+ private final UserDetailsService userDetailsService;
+
+ public FiltroJwt(UtilJwt utilJwt, UserDetailsService userDetailsService) {
+ this.utilJwt = utilJwt;
+ this.userDetailsService = userDetailsService;
+ }
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
+ throws ServletException, IOException {
+
+ String header = request.getHeader("Authorization");
+ String token = null;
+ String estudanteId = null;
+
+ if (header != null && header.startsWith("Bearer ")) {
+ token = header.substring(7);
+ estudanteId = utilJwt.getEstudanteIdFromToken(token);
+ }
+
+ if (estudanteId != null && SecurityContextHolder.getContext().getAuthentication() == null) {
+ UserDetails userDetails = userDetailsService.loadUserByUsername(estudanteId);
+
+ if (utilJwt.validateToken(token)) {
+ UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
+ userDetails, null, userDetails.getAuthorities());
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ }
+ }
+
+ filterChain.doFilter(request, response);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/agendaestudantil/repository/DisciplinaRepository.java b/src/main/java/com/agendaestudantil/repositorio/DisciplinaRepositorio.java
similarity index 55%
rename from src/main/java/com/agendaestudantil/repository/DisciplinaRepository.java
rename to src/main/java/com/agendaestudantil/repositorio/DisciplinaRepositorio.java
index 6aa614b..7443433 100644
--- a/src/main/java/com/agendaestudantil/repository/DisciplinaRepository.java
+++ b/src/main/java/com/agendaestudantil/repositorio/DisciplinaRepositorio.java
@@ -1,11 +1,11 @@
-package com.agendaestudantil.repository;
+package com.agendaestudantil.repositorio;
-import com.agendaestudantil.entity.Disciplina;
+import com.agendaestudantil.entidade.Disciplina;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
-public interface DisciplinaRepository extends MongoRepository {
+public interface DisciplinaRepositorio extends MongoRepository {
List findByEstudanteId(String estudanteId);
}
diff --git a/src/main/java/com/agendaestudantil/repository/EstudanteRepository.java b/src/main/java/com/agendaestudantil/repositorio/EstudanteRepositorio.java
similarity index 59%
rename from src/main/java/com/agendaestudantil/repository/EstudanteRepository.java
rename to src/main/java/com/agendaestudantil/repositorio/EstudanteRepositorio.java
index 9381975..5124576 100644
--- a/src/main/java/com/agendaestudantil/repository/EstudanteRepository.java
+++ b/src/main/java/com/agendaestudantil/repositorio/EstudanteRepositorio.java
@@ -1,12 +1,12 @@
-package com.agendaestudantil.repository;
+package com.agendaestudantil.repositorio;
-import com.agendaestudantil.entity.Estudante;
+import com.agendaestudantil.entidade.Estudante;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
-public interface EstudanteRepository extends MongoRepository {
+public interface EstudanteRepositorio extends MongoRepository {
Optional findByEmail(String email);
boolean existsByEmail(String email);
}
diff --git a/src/main/java/com/agendaestudantil/repository/EventoRepository.java b/src/main/java/com/agendaestudantil/repositorio/EventoRepositorio.java
similarity index 75%
rename from src/main/java/com/agendaestudantil/repository/EventoRepository.java
rename to src/main/java/com/agendaestudantil/repositorio/EventoRepositorio.java
index d8e147b..ef55801 100644
--- a/src/main/java/com/agendaestudantil/repository/EventoRepository.java
+++ b/src/main/java/com/agendaestudantil/repositorio/EventoRepositorio.java
@@ -1,22 +1,22 @@
-package com.agendaestudantil.repository;
+package com.agendaestudantil.repositorio;
-import com.agendaestudantil.entity.Evento;
+import com.agendaestudantil.entidade.Evento;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;
-import org.springframework.data.repository.query.Param;
+
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.util.List;
@Repository
-public interface EventoRepository extends MongoRepository {
+public interface EventoRepositorio extends MongoRepository {
List findByEstudanteId(String estudanteId);
-
+
List findByDisciplinaId(String disciplinaId);
-
+
@Query("{'estudanteId': ?0, 'dataInicio': {$gte: ?1, $lte: ?2}}")
List findByEstudanteIdAndDataInicioBetween(String estudanteId, LocalDateTime inicio, LocalDateTime fim);
-
+
@Query("{'estudanteId': ?0, 'dataInicio': {$gte: ?1}}")
List findProximosEventosByEstudanteId(String estudanteId, LocalDateTime data);
}
diff --git a/src/main/java/com/agendaestudantil/repository/TarefaRepository.java b/src/main/java/com/agendaestudantil/repositorio/TarefaRepositorio.java
similarity index 72%
rename from src/main/java/com/agendaestudantil/repository/TarefaRepository.java
rename to src/main/java/com/agendaestudantil/repositorio/TarefaRepositorio.java
index 02cc277..cbd6883 100644
--- a/src/main/java/com/agendaestudantil/repository/TarefaRepository.java
+++ b/src/main/java/com/agendaestudantil/repositorio/TarefaRepositorio.java
@@ -1,27 +1,27 @@
-package com.agendaestudantil.repository;
+package com.agendaestudantil.repositorio;
-import com.agendaestudantil.entity.Tarefa;
+import com.agendaestudantil.entidade.Tarefa;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;
-import org.springframework.data.repository.query.Param;
+
import org.springframework.stereotype.Repository;
import java.time.LocalDate;
import java.util.List;
@Repository
-public interface TarefaRepository extends MongoRepository {
+public interface TarefaRepositorio extends MongoRepository {
List findByEstudanteId(String estudanteId);
-
+
List findByEstudanteIdAndStatus(String estudanteId, Tarefa.StatusTarefa status);
-
+
List findByDisciplinaId(String disciplinaId);
-
+
@Query("{'estudanteId': ?0, 'dataEntrega': ?1}")
List findByEstudanteIdAndDataEntrega(String estudanteId, LocalDate data);
-
+
@Query("{'estudanteId': ?0, 'dataEntrega': {$gte: ?1, $lte: ?2}}")
List findByEstudanteIdAndDataEntregaBetween(String estudanteId, LocalDate inicio, LocalDate fim);
-
- @Query("{'estudanteId': ?0, 'status': {$ne: 'CONCLUIDA'}}")
- List findTarefasPendentesByEstudanteId(String estudanteId);
+
+ @Query("{'estudanteId': ?0, 'status': {$ne: ?1}}")
+ List findTarefasPendentesByEstudanteId(String estudanteId, Tarefa.StatusTarefa status);
}
diff --git a/src/main/java/com/agendaestudantil/seguranca/DetalhesUsuarioPersonalizado.java b/src/main/java/com/agendaestudantil/seguranca/DetalhesUsuarioPersonalizado.java
new file mode 100644
index 0000000..d094e09
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/seguranca/DetalhesUsuarioPersonalizado.java
@@ -0,0 +1,53 @@
+package com.agendaestudantil.seguranca;
+
+import com.agendaestudantil.entidade.Estudante;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import java.util.Collection;
+import java.util.Collections;
+
+public class DetalhesUsuarioPersonalizado implements UserDetails {
+
+ private final Estudante estudante;
+
+ public DetalhesUsuarioPersonalizado(Estudante estudante) {
+ this.estudante = estudante;
+ }
+
+ @Override
+ public Collection extends GrantedAuthority> getAuthorities() {
+ return Collections.singleton(new SimpleGrantedAuthority("ROLE_USER"));
+ }
+
+ @Override
+ public String getPassword() {
+ return estudante.getSenha();
+ }
+
+ @Override
+ public String getUsername() {
+ return estudante.getId();
+ }
+
+ @Override
+ public boolean isAccountNonExpired() {
+ return true;
+ }
+
+ @Override
+ public boolean isAccountNonLocked() {
+ return true;
+ }
+
+ @Override
+ public boolean isCredentialsNonExpired() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/agendaestudantil/seguranca/ServicoAutenticacaoUsuario.java b/src/main/java/com/agendaestudantil/seguranca/ServicoAutenticacaoUsuario.java
new file mode 100644
index 0000000..876f7a0
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/seguranca/ServicoAutenticacaoUsuario.java
@@ -0,0 +1,26 @@
+package com.agendaestudantil.seguranca;
+
+import com.agendaestudantil.entidade.Estudante;
+import com.agendaestudantil.repositorio.EstudanteRepositorio;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ServicoAutenticacaoUsuario implements UserDetailsService {
+
+ private final EstudanteRepositorio estudanteRepositorio;
+
+ public ServicoAutenticacaoUsuario(EstudanteRepositorio estudanteRepositorio) {
+ this.estudanteRepositorio = estudanteRepositorio;
+ }
+
+ @Override
+ public UserDetails loadUserByUsername(String estudanteId) throws UsernameNotFoundException {
+ Estudante estudante = estudanteRepositorio.findById(estudanteId)
+ .orElseThrow(() -> new UsernameNotFoundException("Estudante não encontrado"));
+
+ return new DetalhesUsuarioPersonalizado(estudante);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/agendaestudantil/service/EstudanteService.java b/src/main/java/com/agendaestudantil/service/EstudanteService.java
deleted file mode 100644
index 048953c..0000000
--- a/src/main/java/com/agendaestudantil/service/EstudanteService.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package com.agendaestudantil.service;
-
-import com.agendaestudantil.dto.CadastroRequestDTO;
-import com.agendaestudantil.dto.EstudanteResponseDTO;
-import com.agendaestudantil.dto.LoginRequestDTO;
-import com.agendaestudantil.entity.Estudante;
-import com.agendaestudantil.repository.EstudanteRepository;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.HttpStatus;
-import org.springframework.stereotype.Service;
-import org.springframework.web.server.ResponseStatusException;
-
-import java.time.LocalDateTime;
-import java.util.Optional;
-
-@Service
-public class EstudanteService {
-
- @Autowired
- private EstudanteRepository estudanteRepository;
-
- public EstudanteResponseDTO cadastrar(CadastroRequestDTO dto) {
- Optional existente = estudanteRepository.findByEmail(dto.getEmail());
- if (existente.isPresent()) {
- throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Email já cadastrado");
- }
-
- Estudante estudante = new Estudante();
- estudante.setNome(dto.getNome());
- estudante.setEmail(dto.getEmail());
- estudante.setSenha(dto.getSenha());
- estudante.setCurso(dto.getCurso());
- estudante.setPeriodo(dto.getPeriodo());
- estudante.setDataCriacao(LocalDateTime.now());
- estudante.setDataAtualizacao(LocalDateTime.now());
-
- Estudante salvo = estudanteRepository.save(estudante);
- return toResponse(salvo);
- }
-
- public EstudanteResponseDTO login(LoginRequestDTO dto) {
- Optional estudante = estudanteRepository.findByEmail(dto.getEmail());
-
- if (estudante.isEmpty()) {
- throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Email ou senha incorretos");
- }
-
- if (!estudante.get().getSenha().equals(dto.getSenha())) {
- throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Email ou senha incorretos");
- }
-
- return toResponse(estudante.get());
- }
-
- private EstudanteResponseDTO toResponse(Estudante estudante) {
- return new EstudanteResponseDTO(
- estudante.getId(),
- estudante.getNome(),
- estudante.getEmail(),
- estudante.getCurso(),
- estudante.getPeriodo()
- );
- }
-}
diff --git a/src/main/java/com/agendaestudantil/service/TarefaService.java b/src/main/java/com/agendaestudantil/service/TarefaService.java
deleted file mode 100644
index 01a776c..0000000
--- a/src/main/java/com/agendaestudantil/service/TarefaService.java
+++ /dev/null
@@ -1,98 +0,0 @@
-package com.agendaestudantil.service;
-
-import com.agendaestudantil.dto.TarefaRequestDTO;
-import com.agendaestudantil.entity.Disciplina;
-import com.agendaestudantil.entity.Tarefa;
-import com.agendaestudantil.repository.DisciplinaRepository;
-import com.agendaestudantil.repository.TarefaRepository;
-import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.util.List;
-import java.util.Optional;
-
-@Service
-@RequiredArgsConstructor
-public class TarefaService {
-
- private final TarefaRepository tarefaRepository;
- private final DisciplinaRepository disciplinaRepository;
-
- @Transactional
- public Tarefa criarTarefa(TarefaRequestDTO dto) {
- Tarefa tarefa = new Tarefa();
- tarefa.setTitulo(dto.getTitulo());
- tarefa.setDescricao(dto.getDescricao());
- tarefa.setPrioridade(dto.getPrioridade() != null ? dto.getPrioridade() : Tarefa.Prioridade.MEDIA);
- tarefa.setStatus(dto.getStatus() != null ? dto.getStatus() : Tarefa.StatusTarefa.PENDENTE);
- tarefa.setDataEntrega(dto.getDataEntrega());
- tarefa.setEstudanteId(dto.getEstudanteId());
- tarefa.setDataCriacao(LocalDateTime.now());
- tarefa.setDataAtualizacao(LocalDateTime.now());
-
- if (dto.getDisciplinaId() != null) {
- Disciplina disciplina = disciplinaRepository.findById(dto.getDisciplinaId())
- .orElseThrow(() -> new RuntimeException("Disciplina não encontrada"));
- tarefa.setDisciplinaId(disciplina.getId());
- }
-
- return tarefaRepository.save(tarefa);
- }
-
- public List listarTarefasPorEstudante(String estudanteId) {
- return tarefaRepository.findByEstudanteId(estudanteId);
- }
-
- public List listarTarefasPendentes(String estudanteId) {
- return tarefaRepository.findTarefasPendentesByEstudanteId(estudanteId);
- }
-
- public List listarTarefasPorData(String estudanteId, LocalDate data) {
- return tarefaRepository.findByEstudanteIdAndDataEntrega(estudanteId, data);
- }
-
- public List listarTarefasPorPeriodo(String estudanteId, LocalDate inicio, LocalDate fim) {
- return tarefaRepository.findByEstudanteIdAndDataEntregaBetween(estudanteId, inicio, fim);
- }
-
- public Optional buscarTarefaPorId(String id) {
- return tarefaRepository.findById(id);
- }
-
- @Transactional
- public Tarefa atualizarTarefa(String id, TarefaRequestDTO dto) {
- Tarefa tarefa = tarefaRepository.findById(id)
- .orElseThrow(() -> new RuntimeException("Tarefa não encontrada"));
-
- tarefa.setTitulo(dto.getTitulo());
- tarefa.setDescricao(dto.getDescricao());
- tarefa.setPrioridade(dto.getPrioridade());
- tarefa.setStatus(dto.getStatus());
- tarefa.setDataEntrega(dto.getDataEntrega());
- tarefa.setDataAtualizacao(LocalDateTime.now());
-
- if (dto.getDisciplinaId() != null) {
- Disciplina disciplina = disciplinaRepository.findById(dto.getDisciplinaId())
- .orElseThrow(() -> new RuntimeException("Disciplina não encontrada"));
- tarefa.setDisciplinaId(disciplina.getId());
- }
-
- return tarefaRepository.save(tarefa);
- }
-
- @Transactional
- public void excluirTarefa(String id) {
- tarefaRepository.deleteById(id);
- }
-
- @Transactional
- public Tarefa marcarConcluida(String id) {
- Tarefa tarefa = tarefaRepository.findById(id)
- .orElseThrow(() -> new RuntimeException("Tarefa não encontrada"));
- tarefa.setStatus(Tarefa.StatusTarefa.CONCLUIDA);
- tarefa.setDataAtualizacao(LocalDateTime.now());
- return tarefaRepository.save(tarefa);
- }
-}
diff --git a/src/main/java/com/agendaestudantil/servico/EstudanteServico.java b/src/main/java/com/agendaestudantil/servico/EstudanteServico.java
new file mode 100644
index 0000000..56b601a
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/servico/EstudanteServico.java
@@ -0,0 +1,83 @@
+package com.agendaestudantil.servico;
+
+import com.agendaestudantil.dto.RequisicaoCadastroDTO;
+import com.agendaestudantil.dto.RespostaEstudanteDTO;
+import com.agendaestudantil.dto.RequisicaoLoginDTO;
+import com.agendaestudantil.dto.RespostaLoginDTO;
+import com.agendaestudantil.entidade.Estudante;
+import com.agendaestudantil.excecao.ExcecaoNegocio;
+import com.agendaestudantil.repositorio.EstudanteRepositorio;
+import com.agendaestudantil.utilitario.UtilJwt;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
+import org.springframework.web.server.ResponseStatusException;
+
+import java.util.Optional;
+
+@Service
+public class EstudanteServico {
+
+ private static final Logger log = LoggerFactory.getLogger(EstudanteServico.class);
+
+ private final EstudanteRepositorio estudanteRepositorio;
+ private final PasswordEncoder passwordEncoder;
+ private final UtilJwt utilJwt;
+
+ public EstudanteServico(EstudanteRepositorio estudanteRepositorio, PasswordEncoder passwordEncoder,
+ UtilJwt utilJwt) {
+ this.estudanteRepositorio = estudanteRepositorio;
+ this.passwordEncoder = passwordEncoder;
+ this.utilJwt = utilJwt;
+ }
+
+ public RespostaEstudanteDTO cadastrar(RequisicaoCadastroDTO dto) {
+ log.debug("Acessando método cadastrar para email: {}", dto.email());
+ Optional existente = estudanteRepositorio.findByEmail(dto.email());
+ if (existente.isPresent()) {
+ log.error("Email já cadastrado: {}", dto.email());
+ throw new ExcecaoNegocio("Email já cadastrado");
+ }
+
+ Estudante estudante = new Estudante();
+ estudante.setNome(dto.nome());
+ estudante.setEmail(dto.email());
+ estudante.setSenha(passwordEncoder.encode(dto.senha()));
+ estudante.setCurso(dto.curso());
+ estudante.setPeriodo(dto.periodo());
+
+ Estudante salvo = estudanteRepositorio.save(estudante);
+ return toResponse(salvo);
+ }
+
+ public RespostaLoginDTO login(RequisicaoLoginDTO dto) {
+ log.debug("Acessando método login para email: {}", dto.email());
+ Optional estudanteParam = estudanteRepositorio.findByEmail(dto.email());
+
+ if (estudanteParam.isEmpty()) {
+ log.error("Email ou senha incorretos para email: {}", dto.email());
+ throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Email ou senha incorretos");
+ }
+
+ Estudante estudante = estudanteParam.get();
+
+ if (!passwordEncoder.matches(dto.senha(), estudante.getSenha())) {
+ log.error("Falha ao validar senha para email: {}", dto.email());
+ throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Email ou senha incorretos");
+ }
+
+ String token = utilJwt.generateToken(estudante.getId());
+ return new RespostaLoginDTO(token, toResponse(estudante));
+ }
+
+ private RespostaEstudanteDTO toResponse(Estudante estudante) {
+ return new RespostaEstudanteDTO(
+ estudante.getId(),
+ estudante.getNome(),
+ estudante.getEmail(),
+ estudante.getCurso(),
+ estudante.getPeriodo());
+ }
+}
diff --git a/src/main/java/com/agendaestudantil/servico/TarefaServico.java b/src/main/java/com/agendaestudantil/servico/TarefaServico.java
new file mode 100644
index 0000000..a2effef
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/servico/TarefaServico.java
@@ -0,0 +1,140 @@
+package com.agendaestudantil.servico;
+
+import com.agendaestudantil.dto.RequisicaoTarefaDTO;
+import com.agendaestudantil.dto.RespostaTarefaDTO;
+import com.agendaestudantil.entidade.Tarefa;
+import com.agendaestudantil.excecao.ExcecaoRecursoNaoEncontrado;
+import com.agendaestudantil.repositorio.TarefaRepositorio;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Service;
+import org.springframework.web.server.ResponseStatusException;
+
+import java.time.LocalDate;
+import java.util.List;
+
+@Service
+public class TarefaServico {
+
+ private static final Logger log = LoggerFactory.getLogger(TarefaServico.class);
+
+ private final TarefaRepositorio tarefaRepositorio;
+
+ public TarefaServico(TarefaRepositorio tarefaRepositorio) {
+ this.tarefaRepositorio = tarefaRepositorio;
+ }
+
+ private void validarAcesso(String estudanteId) {
+ String authUser = SecurityContextHolder.getContext().getAuthentication().getName();
+ if (!authUser.equals(estudanteId)) {
+ log.error("Acesso negado. Usuário {} tentou acessar recurso do estudante {}", authUser, estudanteId);
+ throw new ResponseStatusException(HttpStatus.FORBIDDEN, "Acesso negado");
+ }
+ }
+
+ private void validarAcessoTarefa(Tarefa tarefa) {
+ validarAcesso(tarefa.getEstudanteId());
+ }
+
+ public RespostaTarefaDTO criarTarefa(RequisicaoTarefaDTO dto) {
+ log.debug("Criando tarefa para estudante: {}", dto.estudanteId());
+ validarAcesso(dto.estudanteId());
+
+ Tarefa tarefa = new Tarefa();
+ tarefa.setTitulo(dto.titulo());
+ tarefa.setDescricao(dto.descricao());
+ tarefa.setPrioridade(dto.prioridade() != null ? dto.prioridade() : Tarefa.Prioridade.MEDIA);
+ tarefa.setStatus(dto.status() != null ? dto.status() : Tarefa.StatusTarefa.PENDENTE);
+ tarefa.setDataEntrega(dto.dataEntrega());
+ tarefa.setEstudanteId(dto.estudanteId());
+
+ if (dto.disciplinaId() != null) {
+ tarefa.setDisciplinaId(dto.disciplinaId());
+ }
+
+ return toDTO(tarefaRepositorio.save(tarefa));
+ }
+
+ public List listarTarefasPorEstudante(String estudanteId) {
+ log.debug("Listando tarefas para estudante: {}", estudanteId);
+ validarAcesso(estudanteId);
+ return tarefaRepositorio.findByEstudanteId(estudanteId).stream()
+ .map(this::toDTO)
+ .toList();
+ }
+
+ public List listarTarefasPendentes(String estudanteId) {
+ log.debug("Listando tarefas pendentes para estudante: {}", estudanteId);
+ validarAcesso(estudanteId);
+ return tarefaRepositorio.findTarefasPendentesByEstudanteId(estudanteId, Tarefa.StatusTarefa.CONCLUIDA).stream()
+ .map(this::toDTO)
+ .toList();
+ }
+
+ public List listarTarefasPorData(String estudanteId, LocalDate data) {
+ validarAcesso(estudanteId);
+ return tarefaRepositorio.findByEstudanteIdAndDataEntrega(estudanteId, data).stream()
+ .map(this::toDTO)
+ .toList();
+ }
+
+ public List listarTarefasPorPeriodo(String estudanteId, LocalDate inicio, LocalDate fim) {
+ validarAcesso(estudanteId);
+ return tarefaRepositorio.findByEstudanteIdAndDataEntregaBetween(estudanteId, inicio, fim).stream()
+ .map(this::toDTO)
+ .toList();
+ }
+
+ public RespostaTarefaDTO buscarTarefaPorId(String id) {
+ return toDTO(getTarefaEntity(id));
+ }
+
+ public RespostaTarefaDTO atualizarTarefa(String id, RequisicaoTarefaDTO dto) {
+ Tarefa tarefa = getTarefaEntity(id);
+ validarAcesso(dto.estudanteId());
+
+ tarefa.setTitulo(dto.titulo());
+ tarefa.setDescricao(dto.descricao());
+ tarefa.setPrioridade(dto.prioridade());
+ tarefa.setStatus(dto.status());
+ tarefa.setDataEntrega(dto.dataEntrega());
+ tarefa.setEstudanteId(dto.estudanteId());
+
+ if (dto.disciplinaId() != null) {
+ tarefa.setDisciplinaId(dto.disciplinaId());
+ }
+
+ return toDTO(tarefaRepositorio.save(tarefa));
+ }
+
+ public void excluirTarefa(String id) {
+ tarefaRepositorio.delete(getTarefaEntity(id));
+ }
+
+ public RespostaTarefaDTO marcarConcluida(String id) {
+ Tarefa tarefa = getTarefaEntity(id);
+ tarefa.setStatus(Tarefa.StatusTarefa.CONCLUIDA);
+ return toDTO(tarefaRepositorio.save(tarefa));
+ }
+
+ private Tarefa getTarefaEntity(String id) {
+ Tarefa tarefa = tarefaRepositorio.findById(id)
+ .orElseThrow(() -> new ExcecaoRecursoNaoEncontrado("Tarefa não encontrada"));
+ validarAcessoTarefa(tarefa);
+ return tarefa;
+ }
+
+ private RespostaTarefaDTO toDTO(Tarefa tarefa) {
+ return new RespostaTarefaDTO(
+ tarefa.getId(),
+ tarefa.getTitulo(),
+ tarefa.getDescricao(),
+ tarefa.getPrioridade() != null ? tarefa.getPrioridade().name() : null,
+ tarefa.getStatus() != null ? tarefa.getStatus().name() : null,
+ tarefa.getDataEntrega(),
+ tarefa.getDisciplinaId(),
+ tarefa.getEstudanteId());
+ }
+}
diff --git a/src/main/java/com/agendaestudantil/utilitario/UtilJwt.java b/src/main/java/com/agendaestudantil/utilitario/UtilJwt.java
new file mode 100644
index 0000000..8fa79a1
--- /dev/null
+++ b/src/main/java/com/agendaestudantil/utilitario/UtilJwt.java
@@ -0,0 +1,56 @@
+package com.agendaestudantil.utilitario;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.security.Keys;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import javax.crypto.SecretKey;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+
+@Component
+public class UtilJwt {
+
+ private final String secret;
+
+ public UtilJwt(@Value("${jwt.secret}") String secret) {
+ this.secret = secret;
+ }
+
+ private SecretKey getSigningKey() {
+ return Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
+ }
+
+ public String generateToken(String estudanteId) {
+ Date now = new Date();
+ Date expiryDate = new Date(now.getTime() + 24 * 60 * 60 * 1000L);
+
+ return Jwts.builder()
+ .subject(estudanteId)
+ .issuedAt(now)
+ .expiration(expiryDate)
+ .signWith(getSigningKey())
+ .compact();
+ }
+
+ public String getEstudanteIdFromToken(String token) {
+ Claims claims = Jwts.parser()
+ .verifyWith(getSigningKey())
+ .build()
+ .parseSignedClaims(token)
+ .getPayload();
+
+ return claims.getSubject();
+ }
+
+ public boolean validateToken(String token) {
+ try {
+ Jwts.parser().verifyWith(getSigningKey()).build().parseSignedClaims(token);
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties
new file mode 100644
index 0000000..49be875
--- /dev/null
+++ b/src/main/resources/application-dev.properties
@@ -0,0 +1,4 @@
+spring.data.mongodb.uri=${MONGO_URI:mongodb://localhost:27017/agenda_estudantil}
+cors.allowed.origins=${CORS_ORIGINS:http://localhost:3000}
+jwt.secret=${JWT_SECRET:8a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p7q8r9s0t1u2v3w4x5y6z7A8B9C0D1E2F3}
+logging.level.org.springframework.data.mongodb=DEBUG
diff --git a/src/main/resources/application-prod.properties b/src/main/resources/application-prod.properties
new file mode 100644
index 0000000..a365b3f
--- /dev/null
+++ b/src/main/resources/application-prod.properties
@@ -0,0 +1,5 @@
+spring.data.mongodb.uri=${MONGO_URI}
+cors.allowed.origins=${CORS_ORIGINS}
+jwt.secret=${JWT_SECRET}
+logging.level.root=WARN
+logging.level.com.agendaestudantil=INFO
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 86ead23..3ce5e32 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1,8 +1,4 @@
spring.application.name=${APP_NAME:Agenda Digital para Estudantes}
server.port=${SERVER_PORT:8080}
-spring.data.mongodb.host=${MONGODB_HOST:localhost}
-spring.data.mongodb.port=${MONGODB_PORT:27017}
-spring.data.mongodb.database=${MONGODB_DATABASE:agenda_estudantil}
-
-logging.level.org.springframework.data.mongodb=DEBUG
+spring.profiles.active=${SPRING_PROFILES_ACTIVE:dev}
diff --git a/src/test/java/com/agendaestudantil/servico/EstudanteServicoTest.java b/src/test/java/com/agendaestudantil/servico/EstudanteServicoTest.java
new file mode 100644
index 0000000..a501c29
--- /dev/null
+++ b/src/test/java/com/agendaestudantil/servico/EstudanteServicoTest.java
@@ -0,0 +1,107 @@
+package com.agendaestudantil.servico;
+
+import com.agendaestudantil.dto.RequisicaoCadastroDTO;
+import com.agendaestudantil.dto.RespostaEstudanteDTO;
+import com.agendaestudantil.dto.RequisicaoLoginDTO;
+import com.agendaestudantil.dto.RespostaLoginDTO;
+import com.agendaestudantil.entidade.Estudante;
+import com.agendaestudantil.excecao.ExcecaoNegocio;
+import com.agendaestudantil.repositorio.EstudanteRepositorio;
+import com.agendaestudantil.utilitario.UtilJwt;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.web.server.ResponseStatusException;
+
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+public class EstudanteServicoTest {
+
+ @Mock
+ private EstudanteRepositorio estudanteRepositorio;
+
+ @Mock
+ private PasswordEncoder passwordEncoder;
+
+ @Mock
+ private UtilJwt utilJwt;
+
+ @InjectMocks
+ private EstudanteServico estudanteServico;
+
+ private RequisicaoCadastroDTO requisicaoCadastro;
+ private RequisicaoLoginDTO requisicaoLogin;
+ private Estudante estudante;
+
+ @BeforeEach
+ void setUp() {
+ requisicaoCadastro = new RequisicaoCadastroDTO("Teste", "teste@teste.com", "123456", "Curso", 1);
+ requisicaoLogin = new RequisicaoLoginDTO("teste@teste.com", "123456");
+
+ estudante = new Estudante();
+ estudante.setId("1");
+ estudante.setNome("Teste");
+ estudante.setEmail("teste@teste.com");
+ estudante.setSenha("encodedPassword");
+ estudante.setCurso("Curso");
+ estudante.setPeriodo(1);
+ }
+
+ @Test
+ void deveCadastrarEstudanteComSucesso() {
+ when(estudanteRepositorio.findByEmail(anyString())).thenReturn(Optional.empty());
+ when(passwordEncoder.encode(anyString())).thenReturn("encodedPassword");
+ when(estudanteRepositorio.save(any(Estudante.class))).thenReturn(estudante);
+
+ RespostaEstudanteDTO resposta = estudanteServico.cadastrar(requisicaoCadastro);
+
+ assertNotNull(resposta);
+ assertEquals("Teste", resposta.nome());
+ verify(estudanteRepositorio, times(1)).save(any(Estudante.class));
+ }
+
+ @Test
+ void deveLancarExcecaoAoCadastrarEmailExistente() {
+ when(estudanteRepositorio.findByEmail(anyString())).thenReturn(Optional.of(estudante));
+
+ assertThrows(ExcecaoNegocio.class, () -> estudanteServico.cadastrar(requisicaoCadastro));
+ verify(estudanteRepositorio, never()).save(any(Estudante.class));
+ }
+
+ @Test
+ void deveRealizarLoginComSucesso() {
+ when(estudanteRepositorio.findByEmail(anyString())).thenReturn(Optional.of(estudante));
+ when(passwordEncoder.matches(anyString(), anyString())).thenReturn(true);
+ when(utilJwt.generateToken(anyString())).thenReturn("token123");
+
+ RespostaLoginDTO resposta = estudanteServico.login(requisicaoLogin);
+
+ assertNotNull(resposta);
+ assertEquals("token123", resposta.token());
+ assertEquals("Teste", resposta.estudante().nome());
+ }
+
+ @Test
+ void deveRejeitarSenhaInvalida() {
+ when(estudanteRepositorio.findByEmail(anyString())).thenReturn(Optional.of(estudante));
+ when(passwordEncoder.matches(anyString(), anyString())).thenReturn(false);
+
+ assertThrows(ResponseStatusException.class, () -> estudanteServico.login(requisicaoLogin));
+ }
+
+ @Test
+ void deveRejeitarEmailNaoEncontrado() {
+ when(estudanteRepositorio.findByEmail(anyString())).thenReturn(Optional.empty());
+
+ assertThrows(ResponseStatusException.class, () -> estudanteServico.login(requisicaoLogin));
+ }
+}
diff --git a/src/test/java/com/agendaestudantil/servico/TarefaServicoTest.java b/src/test/java/com/agendaestudantil/servico/TarefaServicoTest.java
new file mode 100644
index 0000000..cf3b160
--- /dev/null
+++ b/src/test/java/com/agendaestudantil/servico/TarefaServicoTest.java
@@ -0,0 +1,123 @@
+package com.agendaestudantil.servico;
+
+import com.agendaestudantil.dto.RequisicaoTarefaDTO;
+import com.agendaestudantil.dto.RespostaTarefaDTO;
+import com.agendaestudantil.entidade.Tarefa;
+import com.agendaestudantil.excecao.ExcecaoRecursoNaoEncontrado;
+import com.agendaestudantil.repositorio.TarefaRepositorio;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.web.server.ResponseStatusException;
+
+import java.time.LocalDate;
+import java.util.List;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+public class TarefaServicoTest {
+
+ @Mock
+ private TarefaRepositorio tarefaRepositorio;
+
+ @Mock
+ private SecurityContext securityContext;
+
+ @Mock
+ private Authentication authentication;
+
+ @InjectMocks
+ private TarefaServico tarefaServico;
+
+ private RequisicaoTarefaDTO requisicaoTarefa;
+ private Tarefa tarefa;
+ private final String estudanteId = "estudante123";
+
+ @BeforeEach
+ void setUp() {
+ requisicaoTarefa = new RequisicaoTarefaDTO(
+ "Estudar Spring",
+ "Aprender Spring Boot 3",
+ Tarefa.Prioridade.ALTA,
+ Tarefa.StatusTarefa.PENDENTE,
+ LocalDate.now(),
+ null,
+ estudanteId);
+
+ tarefa = new Tarefa();
+ tarefa.setId("tarefa1");
+ tarefa.setTitulo("Estudar Spring");
+ tarefa.setDescricao("Aprender Spring Boot 3");
+ tarefa.setPrioridade(Tarefa.Prioridade.ALTA);
+ tarefa.setStatus(Tarefa.StatusTarefa.PENDENTE);
+ tarefa.setDataEntrega(LocalDate.now());
+ tarefa.setEstudanteId(estudanteId);
+ }
+
+ private void mockAuthentication(String user) {
+ when(securityContext.getAuthentication()).thenReturn(authentication);
+ when(authentication.getName()).thenReturn(user);
+ SecurityContextHolder.setContext(securityContext);
+ }
+
+ @Test
+ void deveCriarTarefaComSucesso() {
+ mockAuthentication(estudanteId);
+ when(tarefaRepositorio.save(any(Tarefa.class))).thenReturn(tarefa);
+
+ RespostaTarefaDTO resposta = tarefaServico.criarTarefa(requisicaoTarefa);
+
+ assertNotNull(resposta);
+ assertEquals("Estudar Spring", resposta.titulo());
+ verify(tarefaRepositorio, times(1)).save(any(Tarefa.class));
+ }
+
+ @Test
+ void deveLancarExcecaoAoCriarTarefaParaOutroUsuario() {
+ mockAuthentication("outroEstudante");
+
+ assertThrows(ResponseStatusException.class, () -> tarefaServico.criarTarefa(requisicaoTarefa));
+ verify(tarefaRepositorio, never()).save(any(Tarefa.class));
+ }
+
+ @Test
+ void deveListarTarefasPorEstudanteComSucesso() {
+ mockAuthentication(estudanteId);
+ when(tarefaRepositorio.findByEstudanteId(estudanteId)).thenReturn(List.of(tarefa));
+
+ List tarefas = tarefaServico.listarTarefasPorEstudante(estudanteId);
+
+ assertFalse(tarefas.isEmpty());
+ assertEquals(1, tarefas.size());
+ assertEquals("Estudar Spring", tarefas.get(0).titulo());
+ }
+
+ @Test
+ void deveMudarStatusTarefaParaConcluidoComSucesso() {
+ mockAuthentication(estudanteId);
+ when(tarefaRepositorio.findById(anyString())).thenReturn(Optional.of(tarefa));
+ when(tarefaRepositorio.save(any(Tarefa.class))).thenReturn(tarefa);
+
+ RespostaTarefaDTO resposta = tarefaServico.marcarConcluida("tarefa1");
+
+ assertNotNull(resposta);
+ verify(tarefaRepositorio, times(1)).save(any(Tarefa.class));
+ }
+
+ @Test
+ void deveLancarExcecaoQuandoTarefaNaoEncontrada() {
+ when(tarefaRepositorio.findById(anyString())).thenReturn(Optional.empty());
+
+ assertThrows(ExcecaoRecursoNaoEncontrado.class, () -> tarefaServico.buscarTarefaPorId("tarefaNaoExistente"));
+ }
+}