feat: otimização de performance e ajustes finais
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
|
||||
http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>br.gov.sigefp</groupId>
|
||||
<artifactId>sigefp-parent</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>sigefp-common</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>SIGEFP Common</name>
|
||||
<description>Utilitários e classes compartilhadas</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-commons</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Lombok para reduzir boilerplate -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- Excel (Apache POI) -->
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>5.2.3</version>
|
||||
</dependency>
|
||||
|
||||
<!-- PDF (OpenPDF) -->
|
||||
<dependency>
|
||||
<groupId>com.github.librepdf</groupId>
|
||||
<artifactId>openpdf</artifactId>
|
||||
<version>1.3.30</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.30</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
package br.gov.sigefp.common.api;
|
||||
|
||||
import br.gov.sigefp.common.api.dto.BankDTO;
|
||||
import br.gov.sigefp.common.service.BankService;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Controller REST para gestão de bancos.
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/common/banks")
|
||||
@RequiredArgsConstructor
|
||||
public class BankController {
|
||||
|
||||
private final BankService bankService;
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<Page<BankDTO>> findAll(
|
||||
@RequestParam(name = "page", defaultValue = "0") int page,
|
||||
@RequestParam(name = "size", defaultValue = "20") int size,
|
||||
@RequestParam(name = "sortBy", required = false) String sortBy,
|
||||
@RequestParam(name = "sortDirection", required = false, defaultValue = "ASC") String sortDirection) {
|
||||
|
||||
Sort sort = sortBy != null
|
||||
? Sort.by(Sort.Direction.fromString(sortDirection), sortBy)
|
||||
: Sort.by(Sort.Direction.ASC, "code");
|
||||
|
||||
Pageable pageable = PageRequest.of(page, size, sort);
|
||||
Page<BankDTO> result = bankService.findAll(pageable);
|
||||
return ResponseEntity.ok(result);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<BankDTO> findById(@PathVariable(name = "id") UUID id) {
|
||||
try {
|
||||
BankDTO dto = bankService.findById(id);
|
||||
return ResponseEntity.ok(dto);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<BankDTO> create(@Valid @RequestBody BankDTO dto) {
|
||||
try {
|
||||
BankDTO created = bankService.create(dto);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(created);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public ResponseEntity<BankDTO> update(
|
||||
@PathVariable(name = "id") UUID id,
|
||||
@Valid @RequestBody BankDTO dto) {
|
||||
try {
|
||||
BankDTO updated = bankService.update(id, dto);
|
||||
return ResponseEntity.ok(updated);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package br.gov.sigefp.common.api.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* DTO para transferência de dados de banco.
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BankDTO {
|
||||
|
||||
private UUID id;
|
||||
|
||||
@NotBlank(message = "Código é obrigatório")
|
||||
@Size(min = 1, max = 20, message = "Código deve ter entre 1 e 20 caracteres")
|
||||
private String code;
|
||||
|
||||
@NotBlank(message = "Nome é obrigatório")
|
||||
@Size(max = 200, message = "Nome deve ter no máximo 200 caracteres")
|
||||
private String name;
|
||||
|
||||
@Size(max = 20, message = "Código SWIFT deve ter no máximo 20 caracteres")
|
||||
private String swiftCode;
|
||||
}
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package br.gov.sigefp.common.api.dto;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Standard DTO for API error responses.
|
||||
*/
|
||||
@Getter
|
||||
@Builder
|
||||
public class ErrorResponse {
|
||||
private String timestamp;
|
||||
private int status;
|
||||
private String error;
|
||||
private String code;
|
||||
private String message;
|
||||
private String path;
|
||||
private List<ValidationError> errors;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public static class ValidationError {
|
||||
private String field;
|
||||
private String message;
|
||||
}
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
package br.gov.sigefp.common.api.exception;
|
||||
|
||||
import br.gov.sigefp.common.api.dto.ErrorResponse;
|
||||
import br.gov.sigefp.common.exception.BusinessException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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 java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Global exception handler for all modules.
|
||||
*/
|
||||
@Slf4j
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
private static final DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
|
||||
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex, HttpServletRequest request) {
|
||||
log.warn("Business rule violation: {} - Code: {}", ex.getMessage(), ex.getCode());
|
||||
|
||||
ErrorResponse error = ErrorResponse.builder()
|
||||
.timestamp(LocalDateTime.now().format(formatter))
|
||||
.status(ex.getStatus().value())
|
||||
.error(ex.getStatus().getReasonPhrase())
|
||||
.code(ex.getCode())
|
||||
.message(ex.getMessage())
|
||||
.path(request.getRequestURI())
|
||||
.build();
|
||||
|
||||
return new ResponseEntity<>(error, ex.getStatus());
|
||||
}
|
||||
|
||||
@ExceptionHandler(IllegalArgumentException.class)
|
||||
public ResponseEntity<ErrorResponse> handleIllegalArgumentException(IllegalArgumentException ex, HttpServletRequest request) {
|
||||
log.warn("Illegal argument: {}", ex.getMessage());
|
||||
|
||||
ErrorResponse error = ErrorResponse.builder()
|
||||
.timestamp(LocalDateTime.now().format(formatter))
|
||||
.status(400)
|
||||
.error("Bad Request")
|
||||
.code("ILLEGAL_ARGUMENT")
|
||||
.message(ex.getMessage())
|
||||
.path(request.getRequestURI())
|
||||
.build();
|
||||
|
||||
return ResponseEntity.badRequest().body(error);
|
||||
}
|
||||
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public ResponseEntity<ErrorResponse> handleValidationException(MethodArgumentNotValidException ex,
|
||||
HttpServletRequest request) {
|
||||
List<ErrorResponse.ValidationError> validationErrors = ex.getBindingResult().getFieldErrors().stream()
|
||||
.map(error -> ErrorResponse.ValidationError.builder()
|
||||
.field(error.getField())
|
||||
.message(error.getDefaultMessage())
|
||||
.build())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
ErrorResponse error = ErrorResponse.builder()
|
||||
.timestamp(LocalDateTime.now().format(formatter))
|
||||
.status(400)
|
||||
.error("Bad Request")
|
||||
.code("VALIDATION_ERROR")
|
||||
.message("Validation failed")
|
||||
.path(request.getRequestURI())
|
||||
.errors(validationErrors)
|
||||
.build();
|
||||
|
||||
return ResponseEntity.badRequest().body(error);
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<ErrorResponse> handleGenericException(Exception ex, HttpServletRequest request) {
|
||||
log.error("Internal Server Error: ", ex);
|
||||
|
||||
ErrorResponse error = ErrorResponse.builder()
|
||||
.timestamp(LocalDateTime.now().format(formatter))
|
||||
.status(500)
|
||||
.error("Internal Server Error")
|
||||
.code("INTERNAL_ERROR")
|
||||
.message("An unexpected error occurred")
|
||||
.path(request.getRequestURI())
|
||||
.build();
|
||||
|
||||
return ResponseEntity.internalServerError().body(error);
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package br.gov.sigefp.common.application;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* DTO para requisições de paginação.
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PageRequest {
|
||||
|
||||
@Builder.Default
|
||||
private int page = 0;
|
||||
|
||||
@Builder.Default
|
||||
private int size = 20;
|
||||
|
||||
private String sortBy;
|
||||
|
||||
private String sortDirection; // ASC, DESC
|
||||
}
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package br.gov.sigefp.common.domain;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Entidade global para auditoria de ações no sistema.
|
||||
* Desacoplada de UserAccount para permitir uso em todos os módulos.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "audit_log", indexes = {
|
||||
@Index(name = "idx_audit_user_id", columnList = "user_id"),
|
||||
@Index(name = "idx_audit_module", columnList = "module"),
|
||||
@Index(name = "idx_audit_entity", columnList = "entity,entity_id"),
|
||||
@Index(name = "idx_audit_created", columnList = "created_at")
|
||||
})
|
||||
@EntityListeners(AuditingEntityListener.class)
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class AuditLog {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
@Column(updatable = false, nullable = false)
|
||||
private UUID id;
|
||||
|
||||
@Column(name = "user_id")
|
||||
private UUID userId;
|
||||
|
||||
@Column(name = "username", length = 100)
|
||||
private String username;
|
||||
|
||||
@Column(nullable = false, length = 50)
|
||||
private String module;
|
||||
|
||||
@Column(nullable = false, length = 50)
|
||||
private String action;
|
||||
|
||||
@Column(nullable = false, length = 100)
|
||||
private String entity;
|
||||
|
||||
@Column(name = "entity_id")
|
||||
private UUID entityId;
|
||||
|
||||
@Column(length = 2000)
|
||||
private String description;
|
||||
|
||||
@CreatedDate
|
||||
@Column(nullable = false, updatable = false, name = "created_at")
|
||||
private Instant createdAt;
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package br.gov.sigefp.common.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Entidade base com campos de auditoria (quem criou/modificou).
|
||||
*/
|
||||
@MappedSuperclass
|
||||
@Getter
|
||||
@Setter
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public abstract class AuditableEntity extends BaseEntity {
|
||||
|
||||
@Column(name = "created_by")
|
||||
private UUID createdBy;
|
||||
|
||||
@Column(name = "updated_by")
|
||||
private UUID updatedBy;
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package br.gov.sigefp.common.domain;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* Entidade que representa um banco.
|
||||
* Será usada por:
|
||||
* - rh.agent_bank_account (ligação de agentes a contas bancárias)
|
||||
* - treasury/payment (para identificar bancos em ordens de pagamento)
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "bank", indexes = {
|
||||
@Index(name = "idx_bank_code", columnList = "code"),
|
||||
@Index(name = "idx_bank_swift", columnList = "swift_code")
|
||||
})
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class Bank extends BaseEntity {
|
||||
|
||||
@Column(nullable = false, unique = true, length = 20)
|
||||
private String code; // Código do banco (ex: 001, 237, etc.)
|
||||
|
||||
@Column(nullable = false, length = 200)
|
||||
private String name; // Nome do banco
|
||||
|
||||
@Column(length = 20, name = "swift_code")
|
||||
private String swiftCode; // Código SWIFT/BIC (opcional)
|
||||
}
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package br.gov.sigefp.common.domain;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.annotation.LastModifiedDate;
|
||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Entidade base com campos comuns a todas as entidades do sistema.
|
||||
* Usa UUID como identificador para evitar problemas de sincronização em
|
||||
* ambientes distribuídos.
|
||||
*/
|
||||
@MappedSuperclass
|
||||
@EntityListeners(AuditingEntityListener.class)
|
||||
@Getter
|
||||
@Setter
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public abstract class BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
@Column(updatable = false, nullable = false)
|
||||
private UUID id;
|
||||
|
||||
@CreatedDate
|
||||
@Column(nullable = false, updatable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@LastModifiedDate
|
||||
@Column(nullable = false)
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
@Version
|
||||
private Long version;
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package br.gov.sigefp.common.domain;
|
||||
|
||||
import jakarta.persistence.Embeddable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.YearMonth;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* Value Object para representar um período (ano-mês).
|
||||
* Evita concatenar strings para datas e garante consistência.
|
||||
*/
|
||||
@Embeddable
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode
|
||||
public class PeriodId {
|
||||
|
||||
private Integer year;
|
||||
private Integer month;
|
||||
|
||||
public static PeriodId of(int year, int month) {
|
||||
if (month < 1 || month > 12) {
|
||||
throw new IllegalArgumentException("Mês deve estar entre 1 e 12");
|
||||
}
|
||||
if (year < 2000 || year > 2100) {
|
||||
throw new IllegalArgumentException("Ano deve estar entre 2000 e 2100");
|
||||
}
|
||||
return new PeriodId(year, month);
|
||||
}
|
||||
|
||||
public static PeriodId of(YearMonth yearMonth) {
|
||||
return new PeriodId(yearMonth.getYear(), yearMonth.getMonthValue());
|
||||
}
|
||||
|
||||
public static PeriodId current() {
|
||||
return of(YearMonth.now());
|
||||
}
|
||||
|
||||
public YearMonth toYearMonth() {
|
||||
return YearMonth.of(year, month);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return String.format("%04d-%02d", year, month);
|
||||
}
|
||||
|
||||
public static PeriodId fromString(String period) {
|
||||
YearMonth ym = YearMonth.parse(period, DateTimeFormatter.ofPattern("yyyy-MM"));
|
||||
return of(ym);
|
||||
}
|
||||
}
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package br.gov.sigefp.common.exception;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
/**
|
||||
* Base exception for business rule violations in the SIGEFP system.
|
||||
*/
|
||||
@Getter
|
||||
public class BusinessException extends RuntimeException {
|
||||
|
||||
private final HttpStatus status;
|
||||
private final String code;
|
||||
|
||||
public BusinessException(String message) {
|
||||
this(message, "BUSINESS_ERROR", HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
public BusinessException(String message, String code, HttpStatus status) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package br.gov.sigefp.common.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
/**
|
||||
* Exception thrown when there is no sufficient budget to perform an operation.
|
||||
*/
|
||||
public class InsufficientBudgetException extends BusinessException {
|
||||
|
||||
public InsufficientBudgetException(String message) {
|
||||
super(message, "INSUFFICIENT_BUDGET", HttpStatus.UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package br.gov.sigefp.common.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
/**
|
||||
* Exception thrown when a requested resource is not found.
|
||||
*/
|
||||
public class ResourceNotFoundException extends BusinessException {
|
||||
|
||||
public ResourceNotFoundException(String message) {
|
||||
super(message, "RESOURCE_NOT_FOUND", HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package br.gov.sigefp.common.infrastructure.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
|
||||
|
||||
@Configuration
|
||||
@EnableJpaAuditing
|
||||
public class JpaAuditingConfig {
|
||||
}
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package br.gov.sigefp.common.repository;
|
||||
|
||||
import br.gov.sigefp.common.domain.AuditLog;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.UUID;
|
||||
|
||||
@Repository
|
||||
public interface AuditLogRepository extends JpaRepository<AuditLog, UUID> {
|
||||
|
||||
Page<AuditLog> findByUserId(UUID userId, Pageable pageable);
|
||||
|
||||
Page<AuditLog> findByModule(String module, Pageable pageable);
|
||||
|
||||
Page<AuditLog> findByCreatedAtBetween(Instant start, Instant end, Pageable pageable);
|
||||
|
||||
Page<AuditLog> findByUserIdAndModule(UUID userId, String module, Pageable pageable);
|
||||
|
||||
Page<AuditLog> findByUserIdAndModuleAndCreatedAtBetween(UUID userId, String module, Instant start, Instant end,
|
||||
Pageable pageable);
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package br.gov.sigefp.common.repository;
|
||||
|
||||
import br.gov.sigefp.common.domain.Bank;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Repository
|
||||
public interface BankRepository extends JpaRepository<Bank, UUID> {
|
||||
|
||||
Optional<Bank> findByCode(String code);
|
||||
|
||||
boolean existsByCode(String code);
|
||||
}
|
||||
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
package br.gov.sigefp.common.service;
|
||||
|
||||
import br.gov.sigefp.common.api.dto.BankDTO;
|
||||
import br.gov.sigefp.common.domain.Bank;
|
||||
import br.gov.sigefp.common.repository.BankRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Serviço de aplicação para gestão de bancos.
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional
|
||||
public class BankService {
|
||||
|
||||
private final BankRepository bankRepository;
|
||||
|
||||
public BankDTO create(BankDTO dto) {
|
||||
// Validação: garantir unicidade de code
|
||||
if (bankRepository.existsByCode(dto.getCode())) {
|
||||
throw new IllegalArgumentException("Código de banco já existe: " + dto.getCode());
|
||||
}
|
||||
|
||||
Bank bank = Bank.builder()
|
||||
.code(dto.getCode())
|
||||
.name(dto.getName())
|
||||
.swiftCode(dto.getSwiftCode())
|
||||
.build();
|
||||
|
||||
Bank saved = bankRepository.save(bank);
|
||||
return toDTO(saved);
|
||||
}
|
||||
|
||||
public BankDTO update(UUID id, BankDTO dto) {
|
||||
Bank bank = bankRepository.findById(id)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Banco não encontrado: " + id));
|
||||
|
||||
if (dto.getCode() != null && !dto.getCode().equals(bank.getCode())) {
|
||||
// Validação: garantir unicidade de code
|
||||
if (bankRepository.existsByCode(dto.getCode())) {
|
||||
throw new IllegalArgumentException("Código de banco já existe: " + dto.getCode());
|
||||
}
|
||||
bank.setCode(dto.getCode());
|
||||
}
|
||||
|
||||
if (dto.getName() != null) {
|
||||
bank.setName(dto.getName());
|
||||
}
|
||||
if (dto.getSwiftCode() != null) {
|
||||
bank.setSwiftCode(dto.getSwiftCode());
|
||||
}
|
||||
|
||||
Bank saved = bankRepository.save(bank);
|
||||
return toDTO(saved);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public BankDTO findById(UUID id) {
|
||||
Bank bank = bankRepository.findById(id)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Banco não encontrado: " + id));
|
||||
return toDTO(bank);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public BankDTO findByCode(String code) {
|
||||
Bank bank = bankRepository.findByCode(code)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Banco não encontrado com código: " + code));
|
||||
return toDTO(bank);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Page<BankDTO> findAll(Pageable pageable) {
|
||||
return bankRepository.findAll(pageable).map(this::toDTO);
|
||||
}
|
||||
|
||||
private BankDTO toDTO(Bank bank) {
|
||||
return BankDTO.builder()
|
||||
.id(bank.getId())
|
||||
.code(bank.getCode())
|
||||
.name(bank.getName())
|
||||
.swiftCode(bank.getSwiftCode())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package br.gov.sigefp.common.service;
|
||||
|
||||
import br.gov.sigefp.common.domain.AuditLog;
|
||||
import br.gov.sigefp.common.repository.AuditLogRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Serviço global para registro de logs de auditoria.
|
||||
* Disponível para todos os módulos do sistema.
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional
|
||||
public class GlobalAuditLogService {
|
||||
|
||||
private final AuditLogRepository auditLogRepository;
|
||||
|
||||
/**
|
||||
* Registra uma ação no log de auditoria.
|
||||
*
|
||||
* @param userId ID do utilizador (opcional)
|
||||
* @param username Nome do utilizador (opcional)
|
||||
* @param module Módulo (ADMIN, ORG, RH, etc)
|
||||
* @param action Ação (CREATE, UPDATE, DELETE, etc)
|
||||
* @param entity Nome da entidade objeto
|
||||
* @param entityId ID da entidade objeto
|
||||
* @param description Descrição detalhada (Ex: Campo: [Antigo] -> [Novo])
|
||||
*/
|
||||
public void logAction(UUID userId, String username, String module, String action, String entity, UUID entityId,
|
||||
String description) {
|
||||
AuditLog log = AuditLog.builder()
|
||||
.userId(userId)
|
||||
.username(username)
|
||||
.module(module)
|
||||
.action(action)
|
||||
.entity(entity)
|
||||
.entityId(entityId)
|
||||
.description(description)
|
||||
.build();
|
||||
|
||||
auditLogRepository.save(log);
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package br.gov.sigefp.common.service;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Interface para geração de pagamentos, permitindo inversão de dependência
|
||||
* entre módulos RH e Tesouraria.
|
||||
*/
|
||||
public interface PaymentGenerator {
|
||||
/**
|
||||
* Gera ordens de pagamento a partir de uma execução de folha.
|
||||
*
|
||||
* @param payrollRunId ID da execução de folha
|
||||
* @param paymentBatchId ID do lote de pagamento (opcional, pode ser null para
|
||||
* criar novo)
|
||||
*/
|
||||
void generateOrdersFromPayrollRun(UUID payrollRunId, UUID paymentBatchId);
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package br.gov.sigefp.common.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Serviço genérico para geração de relatórios.
|
||||
*/
|
||||
public interface ReportService {
|
||||
|
||||
/**
|
||||
* Gera um relatório em formato Excel (XLSX).
|
||||
*
|
||||
* @param data Lista de dados a serem exportados.
|
||||
* @param headers Mapa de chaves (propriedades) e títulos das colunas.
|
||||
* @param sheetName Nome da aba na planilha.
|
||||
* @return Byte array do arquivo gerado.
|
||||
*/
|
||||
byte[] generateExcel(List<?> data, Map<String, String> headers, String sheetName);
|
||||
|
||||
/**
|
||||
* Gera um relatório em formato PDF.
|
||||
*
|
||||
* @param data Lista de dados a serem exportados.
|
||||
* @param headers Mapa de chaves (propriedades) e títulos das colunas.
|
||||
* @param title Título do relatório.
|
||||
* @return Byte array do arquivo gerado.
|
||||
*/
|
||||
byte[] generatePdf(List<?> data, Map<String, String> headers, String title);
|
||||
}
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
package br.gov.sigefp.common.service;
|
||||
|
||||
import com.lowagie.text.Document;
|
||||
import com.lowagie.text.Element;
|
||||
import com.lowagie.text.FontFactory;
|
||||
import com.lowagie.text.PageSize;
|
||||
import com.lowagie.text.Paragraph;
|
||||
import com.lowagie.text.Phrase;
|
||||
import com.lowagie.text.pdf.PdfPCell;
|
||||
import com.lowagie.text.pdf.PdfPTable;
|
||||
import com.lowagie.text.pdf.PdfWriter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.CellStyle;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class ReportServiceImpl implements ReportService {
|
||||
|
||||
@Override
|
||||
public byte[] generateExcel(List<?> data, Map<String, String> headers, String sheetName) {
|
||||
try (Workbook workbook = new XSSFWorkbook(); ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
Sheet sheet = workbook.createSheet(sheetName);
|
||||
|
||||
// Create Header Row
|
||||
Row headerRow = sheet.createRow(0);
|
||||
CellStyle headerStyle = workbook.createCellStyle();
|
||||
org.apache.poi.ss.usermodel.Font font = workbook.createFont();
|
||||
font.setBold(true);
|
||||
headerStyle.setFont(font);
|
||||
|
||||
int col = 0;
|
||||
for (String title : headers.values()) {
|
||||
Cell cell = headerRow.createCell(col++);
|
||||
cell.setCellValue(title);
|
||||
cell.setCellStyle(headerStyle);
|
||||
}
|
||||
|
||||
// Populate Data
|
||||
int rowIdx = 1;
|
||||
for (Object item : data) {
|
||||
Row row = sheet.createRow(rowIdx++);
|
||||
col = 0;
|
||||
for (String fieldName : headers.keySet()) {
|
||||
Cell cell = row.createCell(col++);
|
||||
Object value = getFieldValue(item, fieldName);
|
||||
if (value != null) {
|
||||
cell.setCellValue(formatValue(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
workbook.write(out);
|
||||
return out.toByteArray();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Erro ao gerar relatório Excel", e);
|
||||
throw new RuntimeException("Falha na geração do Excel", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] generatePdf(List<?> data, Map<String, String> headers, String title) {
|
||||
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
Document document = new Document(PageSize.A4.rotate());
|
||||
PdfWriter.getInstance(document, out);
|
||||
|
||||
document.open();
|
||||
|
||||
// --- Cabelho Institucional ---
|
||||
|
||||
// Fonte para o cabeçalho
|
||||
com.lowagie.text.Font headerFont = FontFactory.getFont(FontFactory.HELVETICA_BOLD, 12);
|
||||
com.lowagie.text.Font subHeaderFont = FontFactory.getFont(FontFactory.HELVETICA, 10);
|
||||
|
||||
Paragraph textHeader = new Paragraph();
|
||||
textHeader.setAlignment(Element.ALIGN_CENTER);
|
||||
textHeader.add(new Phrase("REPÚBLICA DA GUINÉ-BISSAU\n", headerFont));
|
||||
textHeader.add(new Phrase("MINISTÉRIO DA ECONOMIA E FINANÇAS\n", headerFont));
|
||||
textHeader.add(new Phrase("DIREÇÃO GERAL DO ORÇAMENTO\n", subHeaderFont));
|
||||
textHeader.add(new Phrase("SIGEFP - SISTEMA INTEGRADO DE GESTÃO FINANCEIRA\n", subHeaderFont));
|
||||
textHeader.setSpacingAfter(20);
|
||||
document.add(textHeader);
|
||||
|
||||
// Linha separadora
|
||||
com.lowagie.text.pdf.draw.LineSeparator ls = new com.lowagie.text.pdf.draw.LineSeparator();
|
||||
document.add(new Paragraph(new com.lowagie.text.Chunk(ls)));
|
||||
document.add(new Paragraph(" ")); // Espaço
|
||||
|
||||
// --- Título do Relatório ---
|
||||
com.lowagie.text.Font titleFont = FontFactory.getFont(FontFactory.HELVETICA_BOLD, 14);
|
||||
Paragraph titlePara = new Paragraph(title.toUpperCase(), titleFont);
|
||||
titlePara.setAlignment(Element.ALIGN_CENTER);
|
||||
titlePara.setSpacingAfter(15);
|
||||
document.add(titlePara);
|
||||
|
||||
// Data de Emissão
|
||||
Paragraph datePara = new Paragraph(
|
||||
"Emitido em: "
|
||||
+ java.time.LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm")),
|
||||
FontFactory.getFont(FontFactory.HELVETICA, 8));
|
||||
datePara.setAlignment(Element.ALIGN_RIGHT);
|
||||
datePara.setSpacingAfter(10);
|
||||
document.add(datePara);
|
||||
|
||||
// --- Tabela de Dados ---
|
||||
PdfPTable table = new PdfPTable(headers.size());
|
||||
table.setWidthPercentage(100);
|
||||
table.setSpacingBefore(10f);
|
||||
table.setSpacingAfter(10f);
|
||||
|
||||
// Estilo do Cabeçalho da Tabela
|
||||
com.lowagie.text.Font tableHeaderFont = FontFactory.getFont(FontFactory.HELVETICA_BOLD, 10,
|
||||
java.awt.Color.WHITE);
|
||||
|
||||
for (String headerTitle : headers.values()) {
|
||||
PdfPCell cell = new PdfPCell(new Phrase(headerTitle, tableHeaderFont));
|
||||
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
|
||||
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
|
||||
cell.setBackgroundColor(java.awt.Color.DARK_GRAY);
|
||||
cell.setPadding(5);
|
||||
table.addCell(cell);
|
||||
}
|
||||
|
||||
// Estilo dos Dados
|
||||
com.lowagie.text.Font dataFont = FontFactory.getFont(FontFactory.HELVETICA, 9);
|
||||
boolean alternate = false;
|
||||
java.awt.Color lightGray = new java.awt.Color(240, 240, 240);
|
||||
|
||||
for (Object item : data) {
|
||||
for (String fieldName : headers.keySet()) {
|
||||
Object value = getFieldValue(item, fieldName);
|
||||
PdfPCell cell = new PdfPCell(new Phrase(value != null ? formatValue(value) : "", dataFont));
|
||||
cell.setPadding(4);
|
||||
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
|
||||
|
||||
if (alternate) {
|
||||
cell.setBackgroundColor(lightGray);
|
||||
}
|
||||
table.addCell(cell);
|
||||
}
|
||||
alternate = !alternate;
|
||||
}
|
||||
|
||||
document.add(table);
|
||||
|
||||
// --- Rodapé Simples ---
|
||||
Paragraph footer = new Paragraph(
|
||||
"Documento processado eletronicamente pelo SIGEFP. A autenticidade pode ser verificada no sistema.",
|
||||
FontFactory.getFont(FontFactory.HELVETICA_OBLIQUE, 8));
|
||||
footer.setAlignment(Element.ALIGN_CENTER);
|
||||
footer.setSpacingBefore(20);
|
||||
document.add(footer);
|
||||
|
||||
document.close();
|
||||
|
||||
return out.toByteArray();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Erro ao gerar relatório PDF", e);
|
||||
throw new RuntimeException("Falha na geração do PDF", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to get field value via reflection
|
||||
private Object getFieldValue(Object object, String fieldName) {
|
||||
if (object == null || fieldName == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Suporte para Map (colunas dinâmicas)
|
||||
if (object instanceof Map) {
|
||||
return ((Map<?, ?>) object).get(fieldName);
|
||||
}
|
||||
|
||||
// Primeiro, tentar via getter (mais seguro com Lombok @Data)
|
||||
try {
|
||||
String getterName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
|
||||
java.lang.reflect.Method getter = object.getClass().getMethod(getterName);
|
||||
return getter.invoke(object);
|
||||
} catch (NoSuchMethodException e) {
|
||||
// Tentar campo direto
|
||||
} catch (Exception e) {
|
||||
log.debug("Erro ao invocar getter para {}: {}", fieldName, e.getMessage());
|
||||
}
|
||||
|
||||
// Fallback: tentar campo direto
|
||||
try {
|
||||
Field field = object.getClass().getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.get(object);
|
||||
} catch (NoSuchFieldException e) {
|
||||
log.debug("Campo não encontrado: {}", fieldName);
|
||||
return null;
|
||||
} catch (IllegalAccessException e) {
|
||||
log.debug("Erro ao acessar campo {}: {}", fieldName, e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String formatValue(Object value) {
|
||||
if (value == null)
|
||||
return "";
|
||||
|
||||
if (value instanceof java.time.LocalDate) {
|
||||
return ((java.time.LocalDate) value).format(DateTimeFormatter.ofPattern("dd/MM/yyyy"));
|
||||
} else if (value instanceof java.time.LocalDateTime) {
|
||||
return ((java.time.LocalDateTime) value).format(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm"));
|
||||
} else if (value instanceof java.math.BigDecimal) {
|
||||
// Formatar moeda (XOF/BCEAO geralmente usa espaço como separador de milhar e
|
||||
// vírgula ou ponto, vamos usar padrão PT-BR para consistência com o frontend)
|
||||
java.text.NumberFormat nf = java.text.NumberFormat.getInstance(new java.util.Locale("pt", "BR"));
|
||||
nf.setMinimumFractionDigits(2);
|
||||
return nf.format(value);
|
||||
} else if (value instanceof String) {
|
||||
// Traduções rápidas para valores conhecidos
|
||||
String s = (String) value;
|
||||
return switch (s) {
|
||||
case "COMMITMENT" -> "Empenho";
|
||||
case "LIQUIDATION" -> "Liquidação";
|
||||
case "PAYMENT" -> "Pagamento";
|
||||
case "INITIAL_ALLOCATION" -> "Dotação Inicial";
|
||||
case "SUPPLEMENTARY_CREDIT" -> "Crédito Suplementar";
|
||||
default -> s;
|
||||
};
|
||||
}
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
package br.gov.sigefp.common.util;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Configurações e utilitários específicos para Guiné-Bissau.
|
||||
*/
|
||||
public class GuineaBissauConfig {
|
||||
|
||||
/**
|
||||
* Código do país: GW (ISO 3166-1 alpha-2)
|
||||
*/
|
||||
public static final String COUNTRY_CODE = "GW";
|
||||
|
||||
/**
|
||||
* Nome do país
|
||||
*/
|
||||
public static final String COUNTRY_NAME = "Guiné-Bissau";
|
||||
|
||||
/**
|
||||
* Código da moeda: XOF (Franco CFA da África Ocidental)
|
||||
*/
|
||||
public static final String CURRENCY_CODE = "XOF";
|
||||
|
||||
/**
|
||||
* Símbolo da moeda
|
||||
*/
|
||||
public static final String CURRENCY_SYMBOL = "FCFA";
|
||||
|
||||
/**
|
||||
* Timezone: Africa/Bissau (UTC+0)
|
||||
*/
|
||||
public static final ZoneId TIMEZONE = ZoneId.of("Africa/Bissau");
|
||||
|
||||
/**
|
||||
* Locale: Português da Guiné-Bissau
|
||||
*/
|
||||
public static final Locale LOCALE = new Locale("pt", "GW");
|
||||
|
||||
/**
|
||||
* Formato de data: DD/MM/YYYY
|
||||
*/
|
||||
public static final String DATE_FORMAT = "dd/MM/yyyy";
|
||||
|
||||
/**
|
||||
* Formato de data e hora: DD/MM/YYYY HH:mm
|
||||
*/
|
||||
public static final String DATETIME_FORMAT = "dd/MM/yyyy HH:mm";
|
||||
|
||||
/**
|
||||
* Formato de data e hora completo: DD/MM/YYYY HH:mm:ss
|
||||
*/
|
||||
public static final String DATETIME_FULL_FORMAT = "dd/MM/yyyy HH:mm:ss";
|
||||
|
||||
/**
|
||||
* Código telefônico internacional: +245
|
||||
*/
|
||||
public static final String PHONE_CODE = "+245";
|
||||
|
||||
/**
|
||||
* Formata um valor monetário no formato da Guiné-Bissau.
|
||||
*
|
||||
* @param value Valor a formatar
|
||||
* @return String formatada (ex: "1.234,56 FCFA")
|
||||
*/
|
||||
public static String formatCurrency(BigDecimal value) {
|
||||
if (value == null) {
|
||||
return "0,00 " + CURRENCY_SYMBOL;
|
||||
}
|
||||
|
||||
NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(LOCALE);
|
||||
// Como XOF não tem suporte nativo em todos os Locales, formatamos manualmente
|
||||
NumberFormat numberFormat = NumberFormat.getNumberInstance(LOCALE);
|
||||
numberFormat.setMinimumFractionDigits(2);
|
||||
numberFormat.setMaximumFractionDigits(2);
|
||||
|
||||
return numberFormat.format(value) + " " + CURRENCY_SYMBOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formata um número no formato da Guiné-Bissau.
|
||||
*
|
||||
* @param value Valor a formatar
|
||||
* @return String formatada (ex: "1.234,56")
|
||||
*/
|
||||
public static String formatNumber(BigDecimal value) {
|
||||
if (value == null) {
|
||||
return "0,00";
|
||||
}
|
||||
|
||||
NumberFormat numberFormat = NumberFormat.getNumberInstance(LOCALE);
|
||||
numberFormat.setMinimumFractionDigits(2);
|
||||
numberFormat.setMaximumFractionDigits(2);
|
||||
|
||||
return numberFormat.format(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formata um número inteiro no formato da Guiné-Bissau.
|
||||
*
|
||||
* @param value Valor a formatar
|
||||
* @return String formatada (ex: "1.234")
|
||||
*/
|
||||
public static String formatInteger(Long value) {
|
||||
if (value == null) {
|
||||
return "0";
|
||||
}
|
||||
|
||||
NumberFormat numberFormat = NumberFormat.getNumberInstance(LOCALE);
|
||||
numberFormat.setMinimumFractionDigits(0);
|
||||
numberFormat.setMaximumFractionDigits(0);
|
||||
|
||||
return numberFormat.format(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna um DateTimeFormatter para datas no formato DD/MM/YYYY.
|
||||
*/
|
||||
public static DateTimeFormatter getDateFormatter() {
|
||||
return DateTimeFormatter.ofPattern(DATE_FORMAT, LOCALE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna um DateTimeFormatter para data e hora no formato DD/MM/YYYY HH:mm.
|
||||
*/
|
||||
public static DateTimeFormatter getDateTimeFormatter() {
|
||||
return DateTimeFormatter.ofPattern(DATETIME_FORMAT, LOCALE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna um DateTimeFormatter para data e hora completo no formato DD/MM/YYYY HH:mm:ss.
|
||||
*/
|
||||
public static DateTimeFormatter getDateTimeFullFormatter() {
|
||||
return DateTimeFormatter.ofPattern(DATETIME_FULL_FORMAT, LOCALE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna um SimpleDateFormat para datas no formato DD/MM/YYYY.
|
||||
*/
|
||||
public static SimpleDateFormat getSimpleDateFormat() {
|
||||
return new SimpleDateFormat(DATE_FORMAT, LOCALE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna um SimpleDateFormat para data e hora no formato DD/MM/YYYY HH:mm.
|
||||
*/
|
||||
public static SimpleDateFormat getSimpleDateTimeFormat() {
|
||||
return new SimpleDateFormat(DATETIME_FORMAT, LOCALE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user