Criada branch dev para testes de api

This commit is contained in:
2026-03-01 19:17:39 -03:00
parent 133f847d50
commit 61271a98b8
54 changed files with 1691 additions and 595 deletions

View File

@@ -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());
}
}
}
}

View File

@@ -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 {
}

View File

@@ -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();
}
}

View File

@@ -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")));
}
}

View File

@@ -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<RespostaApi<RespostaEstudanteDTO>> cadastrar(@Valid @RequestBody RequisicaoCadastroDTO dto) {
RespostaEstudanteDTO resposta = estudanteServico.cadastrar(dto);
return ResponseEntity.status(HttpStatus.CREATED).body(RespostaApi.sucesso(resposta));
}
@PostMapping("/login")
public ResponseEntity<RespostaApi<RespostaLoginDTO>> login(@Valid @RequestBody RequisicaoLoginDTO dto) {
RespostaLoginDTO resposta = estudanteServico.login(dto);
return ResponseEntity.ok(RespostaApi.sucesso(resposta));
}
}

View File

@@ -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<RespostaApi<RespostaTarefaDTO>> criarTarefa(@Valid @RequestBody RequisicaoTarefaDTO dto) {
RespostaTarefaDTO tarefa = tarefaServico.criarTarefa(dto);
return ResponseEntity.status(HttpStatus.CREATED).body(RespostaApi.sucesso(tarefa));
}
@GetMapping("/estudante/{estudanteId}")
public ResponseEntity<RespostaApi<List<RespostaTarefaDTO>>> listarTarefasPorEstudante(
@PathVariable String estudanteId) {
List<RespostaTarefaDTO> tarefas = tarefaServico.listarTarefasPorEstudante(estudanteId);
return ResponseEntity.ok(RespostaApi.sucesso(tarefas));
}
@GetMapping("/estudante/{estudanteId}/pendentes")
public ResponseEntity<RespostaApi<List<RespostaTarefaDTO>>> listarTarefasPendentes(
@PathVariable String estudanteId) {
List<RespostaTarefaDTO> tarefas = tarefaServico.listarTarefasPendentes(estudanteId);
return ResponseEntity.ok(RespostaApi.sucesso(tarefas));
}
@GetMapping("/estudante/{estudanteId}/data")
public ResponseEntity<RespostaApi<List<RespostaTarefaDTO>>> listarTarefasPorData(
@PathVariable String estudanteId,
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate data) {
List<RespostaTarefaDTO> tarefas = tarefaServico.listarTarefasPorData(estudanteId, data);
return ResponseEntity.ok(RespostaApi.sucesso(tarefas));
}
@GetMapping("/estudante/{estudanteId}/periodo")
public ResponseEntity<RespostaApi<List<RespostaTarefaDTO>>> listarTarefasPorPeriodo(
@PathVariable String estudanteId,
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate inicio,
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate fim) {
List<RespostaTarefaDTO> tarefas = tarefaServico.listarTarefasPorPeriodo(estudanteId, inicio, fim);
return ResponseEntity.ok(RespostaApi.sucesso(tarefas));
}
@GetMapping("/{id}")
public ResponseEntity<RespostaApi<RespostaTarefaDTO>> buscarTarefaPorId(@PathVariable String id) {
RespostaTarefaDTO dto = tarefaServico.buscarTarefaPorId(id);
return ResponseEntity.ok(RespostaApi.sucesso(dto));
}
@PutMapping("/{id}")
public ResponseEntity<RespostaApi<RespostaTarefaDTO>> atualizarTarefa(@PathVariable String id,
@Valid @RequestBody RequisicaoTarefaDTO dto) {
RespostaTarefaDTO tarefa = tarefaServico.atualizarTarefa(id, dto);
return ResponseEntity.ok(RespostaApi.sucesso(tarefa));
}
@DeleteMapping("/{id}")
public ResponseEntity<RespostaApi<Void>> excluirTarefa(@PathVariable String id) {
tarefaServico.excluirTarefa(id);
return ResponseEntity.status(HttpStatus.NO_CONTENT).body(RespostaApi.sucesso(null));
}
@PatchMapping("/{id}/concluir")
public ResponseEntity<RespostaApi<RespostaTarefaDTO>> marcarConcluida(@PathVariable String id) {
RespostaTarefaDTO tarefa = tarefaServico.marcarConcluida(id);
return ResponseEntity.ok(RespostaApi.sucesso(tarefa));
}
}

View File

@@ -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<EstudanteResponseDTO> cadastrar(@RequestBody CadastroRequestDTO dto) {
EstudanteResponseDTO resposta = estudanteService.cadastrar(dto);
return ResponseEntity.status(HttpStatus.CREATED).body(resposta);
}
@PostMapping("/login")
public ResponseEntity<EstudanteResponseDTO> login(@RequestBody LoginRequestDTO dto) {
EstudanteResponseDTO resposta = estudanteService.login(dto);
return ResponseEntity.ok(resposta);
}
}

View File

@@ -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<Tarefa> criarTarefa(@Valid @RequestBody TarefaRequestDTO dto) {
Tarefa tarefa = tarefaService.criarTarefa(dto);
return ResponseEntity.status(HttpStatus.CREATED).body(tarefa);
}
@GetMapping("/estudante/{estudanteId}")
public ResponseEntity<List<Tarefa>> listarTarefasPorEstudante(@PathVariable String estudanteId) {
List<Tarefa> tarefas = tarefaService.listarTarefasPorEstudante(estudanteId);
return ResponseEntity.ok(tarefas);
}
@GetMapping("/estudante/{estudanteId}/pendentes")
public ResponseEntity<List<Tarefa>> listarTarefasPendentes(@PathVariable String estudanteId) {
List<Tarefa> tarefas = tarefaService.listarTarefasPendentes(estudanteId);
return ResponseEntity.ok(tarefas);
}
@GetMapping("/estudante/{estudanteId}/data")
public ResponseEntity<List<Tarefa>> listarTarefasPorData(
@PathVariable String estudanteId,
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate data) {
List<Tarefa> tarefas = tarefaService.listarTarefasPorData(estudanteId, data);
return ResponseEntity.ok(tarefas);
}
@GetMapping("/estudante/{estudanteId}/periodo")
public ResponseEntity<List<Tarefa>> listarTarefasPorPeriodo(
@PathVariable String estudanteId,
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate inicio,
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate fim) {
List<Tarefa> tarefas = tarefaService.listarTarefasPorPeriodo(estudanteId, inicio, fim);
return ResponseEntity.ok(tarefas);
}
@GetMapping("/{id}")
public ResponseEntity<Tarefa> buscarTarefaPorId(@PathVariable String id) {
return tarefaService.buscarTarefaPorId(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@PutMapping("/{id}")
public ResponseEntity<Tarefa> atualizarTarefa(@PathVariable String id, @Valid @RequestBody TarefaRequestDTO dto) {
Tarefa tarefa = tarefaService.atualizarTarefa(id, dto);
return ResponseEntity.ok(tarefa);
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> excluirTarefa(@PathVariable String id) {
tarefaService.excluirTarefa(id);
return ResponseEntity.noContent().build();
}
@PatchMapping("/{id}/concluir")
public ResponseEntity<Tarefa> marcarConcluida(@PathVariable String id) {
Tarefa tarefa = tarefaService.marcarConcluida(id);
return ResponseEntity.ok(tarefa);
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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
) {
}

View File

@@ -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
) {
}

View File

@@ -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
) {
}

View File

@@ -0,0 +1,9 @@
package com.agendaestudantil.dto;
import java.time.LocalDateTime;
public record RespostaApi<T>(T data, String message, LocalDateTime timestamp) {
public static <T> RespostaApi<T> sucesso(T data) {
return new RespostaApi<>(data, "Sucesso", LocalDateTime.now());
}
}

View File

@@ -0,0 +1,10 @@
package com.agendaestudantil.dto;
public record RespostaDisciplinaDTO(
String id,
String estudanteId,
String nome,
String professor,
String sala,
String cor
) {}

View File

@@ -0,0 +1,10 @@
package com.agendaestudantil.dto;
public record RespostaEstudanteDTO(
String id,
String nome,
String email,
String curso,
Integer periodo
) {
}

View File

@@ -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
) {}

View File

@@ -0,0 +1,4 @@
package com.agendaestudantil.dto;
public record RespostaLoginDTO(String token, RespostaEstudanteDTO estudante) {
}

View File

@@ -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
) {}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -0,0 +1,7 @@
package com.agendaestudantil.excecao;
public class ExcecaoNegocio extends RuntimeException {
public ExcecaoNegocio(String message) {
super(message);
}
}

View File

@@ -0,0 +1,7 @@
package com.agendaestudantil.excecao;
public class ExcecaoRecursoNaoEncontrado extends RuntimeException {
public ExcecaoRecursoNaoEncontrado(String message) {
super(message);
}
}

View File

@@ -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<RespostaApi<Void>> handleResourceNotFound(ExcecaoRecursoNaoEncontrado ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new RespostaApi<>(null, ex.getMessage(), LocalDateTime.now()));
}
@ExceptionHandler(ExcecaoNegocio.class)
public ResponseEntity<RespostaApi<Void>> handleExcecaoNegocio(ExcecaoNegocio ex) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new RespostaApi<>(null, ex.getMessage(), LocalDateTime.now()));
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<RespostaApi<Map<String, String>>> handleValidationException(
MethodArgumentNotValidException ex) {
Map<String, String> 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<RespostaApi<Void>> handleResponseStatusException(ResponseStatusException ex) {
return ResponseEntity.status(ex.getStatusCode())
.body(new RespostaApi<>(null, ex.getReason(), LocalDateTime.now()));
}
@ExceptionHandler(Exception.class)
public ResponseEntity<RespostaApi<Void>> handleGenericException(Exception ex) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new RespostaApi<>(null, "Erro interno no servidor: " + ex.getMessage(), LocalDateTime.now()));
}
}

View File

@@ -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);
}
}

View File

@@ -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<Disciplina, String> {
public interface DisciplinaRepositorio extends MongoRepository<Disciplina, String> {
List<Disciplina> findByEstudanteId(String estudanteId);
}

View File

@@ -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<Estudante, String> {
public interface EstudanteRepositorio extends MongoRepository<Estudante, String> {
Optional<Estudante> findByEmail(String email);
boolean existsByEmail(String email);
}

View File

@@ -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<Evento, String> {
public interface EventoRepositorio extends MongoRepository<Evento, String> {
List<Evento> findByEstudanteId(String estudanteId);
List<Evento> findByDisciplinaId(String disciplinaId);
@Query("{'estudanteId': ?0, 'dataInicio': {$gte: ?1, $lte: ?2}}")
List<Evento> findByEstudanteIdAndDataInicioBetween(String estudanteId, LocalDateTime inicio, LocalDateTime fim);
@Query("{'estudanteId': ?0, 'dataInicio': {$gte: ?1}}")
List<Evento> findProximosEventosByEstudanteId(String estudanteId, LocalDateTime data);
}

View File

@@ -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<Tarefa, String> {
public interface TarefaRepositorio extends MongoRepository<Tarefa, String> {
List<Tarefa> findByEstudanteId(String estudanteId);
List<Tarefa> findByEstudanteIdAndStatus(String estudanteId, Tarefa.StatusTarefa status);
List<Tarefa> findByDisciplinaId(String disciplinaId);
@Query("{'estudanteId': ?0, 'dataEntrega': ?1}")
List<Tarefa> findByEstudanteIdAndDataEntrega(String estudanteId, LocalDate data);
@Query("{'estudanteId': ?0, 'dataEntrega': {$gte: ?1, $lte: ?2}}")
List<Tarefa> findByEstudanteIdAndDataEntregaBetween(String estudanteId, LocalDate inicio, LocalDate fim);
@Query("{'estudanteId': ?0, 'status': {$ne: 'CONCLUIDA'}}")
List<Tarefa> findTarefasPendentesByEstudanteId(String estudanteId);
@Query("{'estudanteId': ?0, 'status': {$ne: ?1}}")
List<Tarefa> findTarefasPendentesByEstudanteId(String estudanteId, Tarefa.StatusTarefa status);
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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<Estudante> 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> 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()
);
}
}

View File

@@ -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<Tarefa> listarTarefasPorEstudante(String estudanteId) {
return tarefaRepository.findByEstudanteId(estudanteId);
}
public List<Tarefa> listarTarefasPendentes(String estudanteId) {
return tarefaRepository.findTarefasPendentesByEstudanteId(estudanteId);
}
public List<Tarefa> listarTarefasPorData(String estudanteId, LocalDate data) {
return tarefaRepository.findByEstudanteIdAndDataEntrega(estudanteId, data);
}
public List<Tarefa> listarTarefasPorPeriodo(String estudanteId, LocalDate inicio, LocalDate fim) {
return tarefaRepository.findByEstudanteIdAndDataEntregaBetween(estudanteId, inicio, fim);
}
public Optional<Tarefa> 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);
}
}

View File

@@ -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<Estudante> 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<Estudante> 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());
}
}

View File

@@ -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<RespostaTarefaDTO> listarTarefasPorEstudante(String estudanteId) {
log.debug("Listando tarefas para estudante: {}", estudanteId);
validarAcesso(estudanteId);
return tarefaRepositorio.findByEstudanteId(estudanteId).stream()
.map(this::toDTO)
.toList();
}
public List<RespostaTarefaDTO> 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<RespostaTarefaDTO> listarTarefasPorData(String estudanteId, LocalDate data) {
validarAcesso(estudanteId);
return tarefaRepositorio.findByEstudanteIdAndDataEntrega(estudanteId, data).stream()
.map(this::toDTO)
.toList();
}
public List<RespostaTarefaDTO> 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());
}
}

View File

@@ -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;
}
}
}