diff --git a/deploy/postgres/init.sql b/deploy/postgres/init.sql index 65331c6..776b0fb 100644 --- a/deploy/postgres/init.sql +++ b/deploy/postgres/init.sql @@ -1,4 +1,4 @@ -\c api; +\ c api; -- Criar tipo ENUM para veículos CREATE TYPE tipo_veiculo AS ENUM ( 'Carro', @@ -9,11 +9,7 @@ CREATE TYPE tipo_veiculo AS ENUM ( 'Moto', 'Indefinido' ); -CREATE TYPE nivel_usuario AS ENUM ( - 'Admin', - 'Gestor' -); - +CREATE TYPE nivel_usuario AS ENUM ('Admin', 'Gestor'); -- Endereço CREATE TABLE endereco ( id SERIAL PRIMARY KEY, @@ -22,28 +18,28 @@ CREATE TABLE endereco ( regiao VARCHAR(30), trecho GEOMETRY(LineString, 4326) ); - -- Radar CREATE TABLE radar ( - id VARCHAR(9) PRIMARY KEY, -- camera_numero - id_end INT NOT NULL, --refere-se ao id do endereço + id VARCHAR(9) PRIMARY KEY, + -- camera_numero + id_end INT NOT NULL, + --refere-se ao id do endereço localizacao GEOMETRY(Point, 4326), vel_reg INT NOT NULL, carros_min_med NUMERIC(10, 2), carros_min_max NUMERIC(10, 2), CONSTRAINT fk_radar_endereco FOREIGN KEY (id_end) REFERENCES endereco(id) ); - -- Leitura CREATE TABLE leitura ( id SERIAL PRIMARY KEY, - id_rad VARCHAR(9) NOT NULL, --refere-se ao id do radar + id_rad VARCHAR(9) NOT NULL, + --refere-se ao id do radar dat_hora TIMESTAMP NOT NULL, tip_vei tipo_veiculo NOT NULL, vel INT NOT NULL, CONSTRAINT fk_leitura_radar FOREIGN KEY (id_rad) REFERENCES radar(id) ); - -- Usuário CREATE TABLE usuario ( id SERIAL PRIMARY KEY, @@ -52,27 +48,26 @@ CREATE TABLE usuario ( senha VARCHAR(100) NOT NULL, nivel nivel_usuario NOT NULL ); - -- Log de Notificações CREATE TABLE log_notificacao ( id SERIAL PRIMARY KEY, - id_usuario INT NOT NULL, - titulo VARCHAR(50) NOT NULL, + id_usuario INT, mensagem TEXT NOT NULL, + texto_relatorio TEXT, + tipo_indice VARCHAR(20) NOT NULL, + valor_indice INT NOT NULL, data_emissao TIMESTAMP DEFAULT NOW(), data_conclusao TIMESTAMP, CONSTRAINT fk_log_usuario FOREIGN KEY (id_usuario) REFERENCES usuario(id) ON DELETE CASCADE ); - -- Regiões CREATE TABLE regioes ( id SERIAL PRIMARY KEY, nome_regiao VARCHAR(100) NOT NULL UNIQUE, area_regiao GEOMETRY(Polygon, 4326) NOT NULL ); - -- Pontos de onibus CREATE TABLE pontos_onibus( id BIGINT PRIMARY KEY, ponto GEOMETRY(Point, 4326) NOT NULL -); +); \ No newline at end of file diff --git a/src/main/java/com/sqlutions/api_4_semestre_backend/controller/NotificationLogController.java b/src/main/java/com/sqlutions/api_4_semestre_backend/controller/NotificationLogController.java index 2a577e5..37568bf 100644 --- a/src/main/java/com/sqlutions/api_4_semestre_backend/controller/NotificationLogController.java +++ b/src/main/java/com/sqlutions/api_4_semestre_backend/controller/NotificationLogController.java @@ -1,12 +1,16 @@ package com.sqlutions.api_4_semestre_backend.controller; +import java.net.URI; import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -14,34 +18,74 @@ import com.sqlutions.api_4_semestre_backend.entity.NotificationLog; import com.sqlutions.api_4_semestre_backend.service.NotificationLogService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; + @RestController -@RequestMapping("/logs") @CrossOrigin +@RequestMapping("/logs") +@Tag(name = "Notification Log", description = "Endpoints for managing notification logs") public class NotificationLogController { - private final NotificationLogService service; - - public NotificationLogController(NotificationLogService service) { - this.service = service; - } + @Autowired + private NotificationLogService service; @GetMapping - public List getAllLogs() { - return service.findAll(); + @Operation(summary = "Get all notification logs") + @ApiResponse(responseCode = "200", description = "Logs retrieved successfully") + public ResponseEntity> getAllLogs() { + return ResponseEntity.ok(service.getAllLogs()); } @GetMapping("/{id}") - public NotificationLog getLogById(@PathVariable Long id) { - return service.findById(id).orElse(null); + @Operation(summary = "Get notification log by ID") + @ApiResponse(responseCode = "200", description = "Log retrieved successfully") + @ApiResponse(responseCode = "404", description = "Log not found") + public ResponseEntity getLogById(@PathVariable Long id) { + NotificationLog log = service.getLogById(id); // já lança 404 no service + return ResponseEntity.ok(log); } @PostMapping - public NotificationLog createLog(@RequestBody NotificationLog log) { - return service.save(log); + @Operation(summary = "Create a new notification log") + @ApiResponse(responseCode = "201", description = "Log created successfully") + public ResponseEntity createLog(@RequestBody NotificationLog log) { + NotificationLog saved = service.createLog(log); + return ResponseEntity + .created(URI.create("/logs/" + saved.getId())) + .body(saved); + } + + @PostMapping("/test") + @Operation(summary = "Create a dummy test notification log") + @ApiResponse(responseCode = "201", description = "Test log created successfully") + public ResponseEntity createTestLog() { + + NotificationLog saved = service.createTestLog(); + + return ResponseEntity + .created(URI.create("/logs/" + saved.getId())) + .body(saved); + } + + + + @PutMapping + @Operation(summary = "Update an existing notification log") + @ApiResponse(responseCode = "200", description = "Log updated successfully") + @ApiResponse(responseCode = "404", description = "Log not found") + public ResponseEntity updateLog(@RequestBody NotificationLog log) { + NotificationLog updated = service.updateLog(log); + return ResponseEntity.ok(updated); } @DeleteMapping("/{id}") - public void deleteLog(@PathVariable Long id) { - service.delete(id); + @Operation(summary = "Delete a notification log") + @ApiResponse(responseCode = "202", description = "Log deletion accepted") + @ApiResponse(responseCode = "404", description = "Log not found") + public ResponseEntity deleteLog(@PathVariable Long id) { + return ResponseEntity.accepted() + .body(service.deleteLog(id)); } } diff --git a/src/main/java/com/sqlutions/api_4_semestre_backend/entity/NotificationLog.java b/src/main/java/com/sqlutions/api_4_semestre_backend/entity/NotificationLog.java index 0dc1e64..0745c39 100644 --- a/src/main/java/com/sqlutions/api_4_semestre_backend/entity/NotificationLog.java +++ b/src/main/java/com/sqlutions/api_4_semestre_backend/entity/NotificationLog.java @@ -1,127 +1,126 @@ package com.sqlutions.api_4_semestre_backend.entity; import java.time.LocalDateTime; +import java.util.HashSet; +import java.util.Set; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.JoinTable; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; @Entity -@Table(name = "notification_logs") +@Table(name = "log_notificacao") public class NotificationLog { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; + private Long id; - @Column(columnDefinition = "TEXT") - private String reportText; // descrição + @ManyToOne + @JoinColumn(name = "id_usuario", nullable = true) + private User user; - private String title; // título do log + @Column(name = "mensagem", columnDefinition = "TEXT", nullable = false) + private String messageText; - private String indexType; // tipo do índice (Segurança, Trafego ou Volume) + @Column(name = "texto_relatorio", columnDefinition = "TEXT") + private String reportText; - private Integer indexValue; // valor do índice (1-5) + @Column(name = "tipo_indice", nullable = false) + private String indexType; - private LocalDateTime startedAt; + @Column(name = "valor_indice", nullable = false) + private Integer indexValue; - private LocalDateTime completedAt; + @Column(name = "data_emissao") + private LocalDateTime emissionDate; - private boolean success; + @Column(name = "data_conclusao") + private LocalDateTime completionDate; - @Column(columnDefinition = "TEXT") - private String errorDetails; + public NotificationLog() { + } - public NotificationLog() {} + public NotificationLog(String reportText, String messageText) { + this.reportText = reportText; + this.messageText = messageText; + } - public NotificationLog( - String reportText, - String title, - String indexType, - Integer indexValue, - LocalDateTime startedAt, - LocalDateTime completedAt, - boolean success, - String errorDetails - ) { - this.reportText = reportText; - this.title = title; - this.indexType = indexType; - this.indexValue = indexValue; - this.startedAt = startedAt; - this.completedAt = completedAt; - this.success = success; - this.errorDetails = errorDetails; + public NotificationLog(User user, String messageText, String reportText, LocalDateTime emissionDate, LocalDateTime completionDate) { + this.user = user; + this.messageText = messageText; + this.reportText = reportText; + this.emissionDate = emissionDate; + this.completionDate = completionDate; } + // Getters and Setters public Long getId() { - return id; + return id; } - public String getReportText() { - return reportText; + public User getUser() { + return user; } - public void setReportText(String reportText) { - this.reportText = reportText; + public void setUser(User user) { + this.user = user; } - public String getTitle() { - return title; + public String getMessageText() { + return messageText; } - public void setTitle(String title) { - this.title = title; + public void setMessageText(String messageText) { + this.messageText = messageText; } - public String getIndexType() { - return indexType; + public String getReportText() { + return reportText; } + public void setReportText(String reportText) { + this.reportText = reportText; + } + + public String getIndexType() { + return indexType; +} + public void setIndexType(String indexType) { this.indexType = indexType; } public Integer getIndexValue() { - return indexValue; + return indexValue; } public void setIndexValue(Integer indexValue) { this.indexValue = indexValue; } - public LocalDateTime getStartedAt() { - return startedAt; - } - - public void setStartedAt(LocalDateTime startedAt) { - this.startedAt = startedAt; - } - - public LocalDateTime getCompletedAt() { - return completedAt; - } - public void setCompletedAt(LocalDateTime completedAt) { - this.completedAt = completedAt; + public LocalDateTime getEmissionDate() { + return emissionDate; } - public boolean isSuccess() { - return success; + public void setEmissionDate(LocalDateTime emissionDate) { + this.emissionDate = emissionDate; } - public void setSuccess(boolean success) { - this.success = success; + public LocalDateTime getCompletionDate() { + return completionDate; } - public String getErrorDetails() { - return errorDetails; + public void setCompletionDate(LocalDateTime completionDate) { + this.completionDate = completionDate; } - public void setErrorDetails(String errorDetails) { - this.errorDetails = errorDetails; - } -} +} \ No newline at end of file diff --git a/src/main/java/com/sqlutions/api_4_semestre_backend/repository/NotificationLogRepository.java b/src/main/java/com/sqlutions/api_4_semestre_backend/repository/NotificationLogRepository.java index 15acc79..a98df74 100644 --- a/src/main/java/com/sqlutions/api_4_semestre_backend/repository/NotificationLogRepository.java +++ b/src/main/java/com/sqlutions/api_4_semestre_backend/repository/NotificationLogRepository.java @@ -7,4 +7,5 @@ @Repository public interface NotificationLogRepository extends JpaRepository { + } diff --git a/src/main/java/com/sqlutions/api_4_semestre_backend/service/NotificationLogService.java b/src/main/java/com/sqlutions/api_4_semestre_backend/service/NotificationLogService.java index b058dbe..1486fa0 100644 --- a/src/main/java/com/sqlutions/api_4_semestre_backend/service/NotificationLogService.java +++ b/src/main/java/com/sqlutions/api_4_semestre_backend/service/NotificationLogService.java @@ -1,35 +1,20 @@ package com.sqlutions.api_4_semestre_backend.service; import java.util.List; -import java.util.Optional; - -import org.springframework.stereotype.Service; import com.sqlutions.api_4_semestre_backend.entity.NotificationLog; -import com.sqlutions.api_4_semestre_backend.repository.NotificationLogRepository; -@Service -public class NotificationLogService { +public interface NotificationLogService { - private final NotificationLogRepository repository; + List getAllLogs(); - public NotificationLogService(NotificationLogRepository repository) { - this.repository = repository; - } + NotificationLog getLogById(Long id); - public List findAll() { - return repository.findAll(); - } + NotificationLog createLog(NotificationLog log); - public Optional findById(Long id) { - return repository.findById(id); - } + NotificationLog createTestLog(); - public NotificationLog save(NotificationLog log) { - return repository.save(log); - } + NotificationLog updateLog(NotificationLog log); - public void delete(Long id) { - repository.deleteById(id); - } + Void deleteLog(Long id); } diff --git a/src/main/java/com/sqlutions/api_4_semestre_backend/service/NotificationLogServiceImpl.java b/src/main/java/com/sqlutions/api_4_semestre_backend/service/NotificationLogServiceImpl.java new file mode 100644 index 0000000..07458c8 --- /dev/null +++ b/src/main/java/com/sqlutions/api_4_semestre_backend/service/NotificationLogServiceImpl.java @@ -0,0 +1,105 @@ +package com.sqlutions.api_4_semestre_backend.service; + +import java.util.List; + +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.util.Random; + +import com.sqlutions.api_4_semestre_backend.entity.NotificationLog; +import com.sqlutions.api_4_semestre_backend.repository.NotificationLogRepository; + +@Service +public class NotificationLogServiceImpl implements NotificationLogService { + + @Autowired + private NotificationLogRepository repository; + + @Override + public List getAllLogs() { + return repository.findAll(); + } + + @Override + public NotificationLog getLogById(Long id) { + return repository.findById(id) + .orElseThrow(() -> new ResponseStatusException( + HttpStatus.NOT_FOUND, + "NotificationLog with id " + id + " not found")); + } + + @Override + public NotificationLog createLog(NotificationLog log) { + + if (log.getMessageText() == null || log.getMessageText().isBlank()) { + throw new ResponseStatusException( + HttpStatus.BAD_REQUEST, + "Message must be provided"); + } + + return repository.save(log); + } + + @Override + public NotificationLog createTestLog() { + + NotificationLog log = new NotificationLog(); + + Random random = new Random(); + + boolean isTransit = random.nextBoolean(); + + String indexType = isTransit ? "Transito" : "Seguranca"; + + int indexValue = random.nextBoolean() ? 4 : 5; + + String message = "Notificação de teste criada para " + + indexType + + " com valor " + + indexValue; + + log.setIndexType(indexType); + log.setIndexValue(indexValue); + log.setMessageText(message); + + log.setReportText(null); + log.setCompletionDate(null); + + return repository.save(log); + } + + @Override + public NotificationLog updateLog(NotificationLog log) { + + if (log.getId() == null) { + throw new ResponseStatusException( + HttpStatus.BAD_REQUEST, + "Log ID must be provided for update"); + } + + NotificationLog existing = getLogById(log.getId()); + + if (log.getMessageText() != null) { + existing.setMessageText(log.getMessageText()); + } + + if (log.getReportText() != null) { + existing.setReportText(log.getReportText()); + } + + if (log.getCompletionDate() != null) { + existing.setCompletionDate(log.getCompletionDate()); + } + + return repository.save(existing); + } + + @Override + public Void deleteLog(Long id) { + getLogById(id); // valida se existe + repository.deleteById(id); + return null; + } +} diff --git a/src/main/java/com/sqlutions/api_4_semestre_backend/service/NotificationService.java b/src/main/java/com/sqlutions/api_4_semestre_backend/service/NotificationService.java index c743fa2..5873088 100644 --- a/src/main/java/com/sqlutions/api_4_semestre_backend/service/NotificationService.java +++ b/src/main/java/com/sqlutions/api_4_semestre_backend/service/NotificationService.java @@ -1,81 +1,7 @@ package com.sqlutions.api_4_semestre_backend.service; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.stereotype.Service; -import org.springframework.web.client.RestTemplate; +public interface NotificationService { -import com.sqlutions.api_4_semestre_backend.entity.NotificationLog; -import com.sqlutions.api_4_semestre_backend.repository.NotificationLogRepository; + void sendAlert(String reportText, String indexType, Integer indexValue); -@Service -public class NotificationService { - - @Value("${telegram.bot.token}") - private String botToken; - - @Value("${telegram.bot.chat-id}") - private String chatId; - - private final NotificationLogRepository logRepository; - private final TimeService timeService; - - public NotificationService(NotificationLogRepository logRepository, TimeService timeService) { - this.logRepository = logRepository; - this.timeService = timeService; - } - - - public void sendAlert(String reportText, String indexType, Integer indexValue) { - - NotificationLog log = new NotificationLog( - reportText, - "Notificação Automática", - indexType, - indexValue, - null, - null, - false, - null - ); - - log.setStartedAt(timeService.getCurrentTimeClampedToDatabase()); - - System.out.println("Enviando notificação..."); - - try { - String url = "https://api.telegram.org/bot" + botToken + "/sendMessage"; - - String json = String.format( - "{\"chat_id\": \"%s\", \"text\": \"%s\", \"parse_mode\": \"Markdown\"}", - chatId, reportText - ); - - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - - HttpEntity request = new HttpEntity<>(json, headers); - new RestTemplate().postForObject(url, request, String.class); - - log.setSuccess(true); - - System.out.println("✅ Notificação enviada com sucesso"); - - } catch (Exception e) { - - log.setErrorDetails(e.getMessage()); - log.setSuccess(false); - - System.out.println("❌ Erro ao enviar notificação: " + e.getMessage()); - } - - log.setCompletedAt(timeService.getCurrentTimeClampedToDatabase()); - logRepository.save(log); - - System.out.println("📄 Log salvo no banco"); - System.out.println("Iniciado em: " + log.getStartedAt()); - System.out.println("Finalizado em: " + log.getCompletedAt()); - } } diff --git a/src/main/java/com/sqlutions/api_4_semestre_backend/service/NotificationServiceImpl.java b/src/main/java/com/sqlutions/api_4_semestre_backend/service/NotificationServiceImpl.java new file mode 100644 index 0000000..f80b0e4 --- /dev/null +++ b/src/main/java/com/sqlutions/api_4_semestre_backend/service/NotificationServiceImpl.java @@ -0,0 +1,74 @@ +package com.sqlutions.api_4_semestre_backend.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import com.sqlutions.api_4_semestre_backend.entity.NotificationLog; +import com.sqlutions.api_4_semestre_backend.repository.NotificationLogRepository; + +@Service +public class NotificationServiceImpl implements NotificationService { + + @Value("${telegram.bot.token}") + private String botToken; + + @Value("${telegram.bot.chat-id}") + private String chatId; + + @Autowired + private NotificationLogRepository logRepository; + + @Autowired + private TimeService timeService; + + @Override + public void sendAlert(String reportText, String indexType, Integer indexValue) { + + NotificationLog log = new NotificationLog(); + + log.setReportText(reportText); + log.setMessageText("Notificação Automática"); + log.setIndexType(indexType); + log.setIndexValue(indexValue); + + log.setEmissionDate(timeService.getCurrentTimeClampedToDatabase()); + + System.out.println("📡 Enviando notificação..."); + + try { + String url = "https://api.telegram.org/bot" + botToken + "/sendMessage"; + + String json = String.format( + "{\"chat_id\": \"%s\", \"text\": \"%s\", \"parse_mode\": \"Markdown\"}", + chatId, + reportText + ); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity request = new HttpEntity<>(json, headers); + + new RestTemplate().postForObject(url, request, String.class); + + System.out.println("✅ Notificação enviada com sucesso"); + + } catch (Exception e) { + System.out.println("❌ Erro ao enviar notificação: " + e.getMessage()); + e.printStackTrace(); + } + + // sempre salva no banco, mesmo se o Telegram falhar + log.setCompletionDate(timeService.getCurrentTimeClampedToDatabase()); + logRepository.save(log); + + System.out.println("📄 Log salvo no banco"); + System.out.println("Iniciado em: " + log.getEmissionDate()); + System.out.println("Finalizado em: " + log.getCompletionDate()); + } +}