Skip to main content

🧱 Librería Commons Utils v 2.5.2

Esta guía resume cómo instalar, escanear, qué incluye y cómo usar la librería común. Pensada para que cualquier dev la consuma en minutos. ✨


📥 Instalación

Maven

<dependency>
<groupId>com.alpura</groupId>
<artifactId>back-java-lib-common-utilities</artifactId>
<version>2.5.2</version>
</dependency>

🔍 Escaneo de componentes

Agrega el paquete raíz para registrar AOP, validaciones y mappers:

@SpringBootApplication
@ComponentScan({
"com.alpura.utilities", // 🧠 Obligatorio
// tus paquetes…
})
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}

🧱 ¿Qué incluye?

  • VOs: RequestVO, SingleResponseVO, ResponseVO, ResponseErrorVO, HeaderVO, NotificationVO, RecipientVO, FileVO, EmailVO, MultipartRequestVO, DescargaArchivoResponseVO, DownloadImagenRequestVO, ImagenEmailVO, ImagenBase64EmailVO, FileBase64EmailVO, ExcelSheetConfigVO, GenerateDataTemplateRequestVO, GeneraDataTemplateResponseVO, TrabajosRequestVO, TrabajosResponseVO, BaseVO, IdiomaVO, UserLoggedInfoVO.
  • Web: BaseController (healthcheck, handler de validación y helper getRequest()), HomeController (vista / con ModelAndView).
  • Validación: BaseNoBlankGroup (marker), ValidationConfiguration (MessageSource + ValidatorFactory).
  • Utils: LocalDateUtil, ListUtil, StringUtil, ValidatorUtil, ValidationUtil, JsonConverterUtil (JPA), ResourceUtil, AuditoryUtils, BusinessErrorUtil.
  • Excepciones: AlpuraException, ValidationException.
  • Errores: GeneralBusinessErrors, BusinessError.
  • Mapper: DozerConfig, LocalDateTimeToStringMapper, LocalDateToStringMapper.
  • Enums: ChannelEnum, EmailDispositionEnum, ErrorCode.
  • Constantes: GeneralConstants (formatos, regex, tokens, etc.).
  • AOP: @JsonResponseInterceptor, JsonResponseHandlerInterceptor (normaliza errores a JSON estándar).

✨ Nuevas características

Manejo de Fechas Simplificado

  • Ahora puedes declarar fechas como String en tus VO y LocalDate/LocalDateTime en tus DO.
  • Dozer hará la conversión automática con nuestros custom converters.
    • LocalDateTimeString: yyyy-MM-dd'T'HH:mm
    • LocalDateString: yyyy-MM-dd

Swagger/OpenAPI automático

  • Los microservicios exponen documentación en http://localhost:9090 (ver springdoc más abajo).

🛠️ Mejoras

application.yml estandarizado con:

  • Endpoints de gestión (health, info, prometheus)
  • Seguridad Alpura
  • Formatos de fecha y hora

🚨 Cambios Importantes (Breaking Changes)

  1. Actualiza pom.xml<properties>
<alpura.common.utilities.version>2.5.2</alpura.common.utilities.version>
  1. src/main/resources/mappings/mappings.xml (crear si no existe)
<mappings xmlns="http://dozermapper.github.io/schema/bean-mapping">
<configuration>
<custom-converters>
<converter type="com.alpura.utilities.mapper.LocalDateTimeToStringMapper">
<class-a>java.time.LocalDateTime</class-a>
<class-b>java.lang.String</class-b>
</converter>
<converter type="com.alpura.utilities.mapper.LocalDateToStringMapper">
<class-a>java.time.LocalDate</class-a>
<class-b>java.lang.String</class-b>
</converter>
</custom-converters>
</configuration>
</mappings>
  1. application.yml estandarizado
spring:
profiles:
active:
- '@spring.profiles.active@'

dozer:
mapping-files:
- classpath:mappings/mappings.xml

server:
port: 9090
servlet:
context-path: /

springdoc:
api-docs:
path: /v3/api-docs
swagger-ui:
path: /swagger-ui.html
info:
title: Mi API de Alpura
version: v1.0.0
description: Documentación de la API de oficina de Alpura
terms-of-service: https://www.alpura.com/terminos
contact:
name: Equipo de Desarrollo de Alpura
email: desarrollo@alpura.com
license:
name: Apache 2.0
url: https://springdoc.org/license.html

audit:
config:
dbAuditable: true
logAuditable: true

alpuraSecurity:
evaluateRoles: false
disableSecurity: false

date:
string:
datetime:
format: "yyyy-MM-dd'T'HH:mm"
date:
format: yyyy-MM-dd

management:
endpoints:
web:
exposure:
include: health,info,prometheus
  1. @Transactional en Fachada
    Agrega @Transactional en métodos de la capa Facade para consistencia transaccional.

👩‍💻 Ejemplo práctico (VO ⇄ DO con fechas)

Objetivo: El dev trabaja con String en el VO y con tipos de fecha nativos en el DO. Dozer convierte automáticamente según los formatos configurados.

1) Value Object (VO) — expuesto en API

// Expone fechas como String (fáciles para front)
public class OrderVO {
private String orderId;
private String orderDate; // yyyy-MM-dd
private String deliveredAt; // yyyy-MM-dd'T'HH:mm
// getters/setters
}

2) Data Object (DO) — persistencia

import java.time.LocalDate;
import java.time.LocalDateTime;

@Entity
@Table(name = "orders")
public class OrderDO {
@Id
private Long id;

private LocalDate orderDate; // Solo fecha
private LocalDateTime deliveredAt; // Fecha y hora
// getters/setters
}

3) Mapeo (Dozer) — ¡sin reglas adicionales!

  • Los converters globales (LocalDateTimeToStringMapper, LocalDateToStringMapper) ya están activos por mappings.xml.
  • No necesitas crear bean-mapping por clase si los nombres coinciden.

4) Uso en servicio

import com.github.dozermapper.core.Mapper;
import org.springframework.beans.factory.annotation.Autowired;

@Service
public class OrderService {
@Autowired
private Mapper mapper;
@Autowired
private OrderRepository repo;

public OrderVO findAsVO(Long id) {
var entity = repo.findById(id).orElseThrow();
// DO -> VO (LocalDate/LocalDateTime → String usando formatos configurados)
return mapper.map(entity, OrderVO.class);
}

public void createFromVO(OrderVO vo) {
// VO -> DO (String → LocalDate/LocalDateTime)
var entity = mapper.map(vo, OrderDO.class);
repo.save(entity);
}
}

5) Prueba rápida (round-trip)

Entrada JSON (POST /orders):

{
"orderId": "A-1001",
"orderDate": "2025-08-01",
"deliveredAt": "2025-08-01T14:30"
}

En BD (persistido por JPA):

  • order_date: 2025-08-01 (LocalDate)
  • delivered_at: 2025-08-01 14:30:00 (LocalDateTime)

Salida JSON (GET /orders/A-1001):

{
"orderId": "A-1001",
"orderDate": "2025-08-01",
"deliveredAt": "2025-08-01T14:30"
}

Sin código manual de parseo/formateo — Dozer y los converters hacen el trabajo.


⚙️ Overriding de formatos por ambiente

Puedes cambiar los formatos sin recompilar, ajustando application.yml:

date:
string:
datetime:
format: "yyyy-MM-dd'T'HH:mm" # cambia aquí si lo necesitas
date:
format: yyyy-MM-dd

Los converters leen estos valores vía @Value. Mantén el DozerConfig y mappings.xml activos.


✅ Checklist de adopción rápida

  • Subiste a alpura-common-utilities:2.5.
  • Incluiste mappings/mappings.xml con los converters.
  • Tienes application.yml estándar con date.string.(date|datetime).format.
  • Anotaste métodos de Fachada con @Transactional.
  • Validaste VO/DO con un round-trip simple.

📦 VOs núcleo (Alpura)

Esta sección define los contratos obligatorios para entrada/salida en servicios y fachadas.

✅ Contratos estándar obligatorios en la arquitectura Alpura

  • Entrada: RequestVO<T>
  • Salida unitaria: SingleResponseVO<T>
  • Salida paginada: ResponseVO<T>

📥 RequestVO<T> (entrada)

Estructura con contexto y payload de negocio en parameters.

public class RequestVO<T> {
Integer size; // Tamaño de página
Integer offset; // Desplazamiento (0-based)
String token; // Token (si aplica)
String macAddress;
T parameters; // 🧩 Payload de negocio
Date requestDate;
String orderBy, orderType; // Ordenamiento
Double latitude, longitude;
String idioma, timeZoneUsuario;
String idClienteInvoke, codigoOperacionCliente;
String appSource, deviceType, httpMethod;
}

🎯 SingleResponseVO<T> (salida unitaria)

public class SingleResponseVO<T> {
HeaderVO header = new HeaderVO(); // code/message
boolean success; // true/false
List<ResponseErrorVO> errors; // errores (si aplica)
T data; // resultado
String newToken; // opcional
}

📊 ResponseVO<T> (listado/paginación)

Extiende a SingleResponseVO<T> y agrega metadatos.

public class ResponseVO<T> extends SingleResponseVO<T> {
Long totalRows; // Total de registros
Integer page; // Índice de página (0-based)
}

🧾 HeaderVO

Encabezado ligero para toda respuesta.

public class HeaderVO {
int code = 0; // 0 = OK (convención)
String message = "OK"; // Mensaje breve
String detail; // Detalle adicional
}

🚨 ResponseErrorVO

Estructura de error uniforme.

public class ResponseErrorVO {
String code; // Código de error
String key; // Clave
String message; // Mensaje
String detail; // Detalle (opcional)
HttpStatus httpStatus;
}

✅ Validación — Grupos y Mensajes

Esta sección explica cómo activar y usar validaciones por grupos en tus VO (Value Objects) y cómo retornar errores uniformes desde tus controladores con la librería común de Alpura.

📌 Objetivo: marcar campos obligatorios sólo en escenarios concretos (por ejemplo, en create/update) usando grupos de validación.

🏷️ Definir el grupo de validación

package com.alpura.utilities.validation;

public interface BaseNoBlankGroup {}

🧠 Este grupo te permite marcar anotaciones como obligatorias sólo cuando invocas @Validated(BaseNoBlankGroup.class).


🧱 Anotar tus VO con grupos

A continuación un VO de ejemplo (puedes adaptarlo a tu ConfigurationVO o similar):

package com.example.demo.vo;

import com.alpura.utilities.validation.BaseNoBlankGroup;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.Setter;

@Getter @Setter
public class ConfigurationMinimalVO {

@NotBlank(message = "El nombre de la aplicación es obligatorio.", groups = BaseNoBlankGroup.class)
private String nameApplication;

@NotBlank(message = "El perfil es obligatorio.", groups = BaseNoBlankGroup.class)
private String profile;

@NotBlank(message = "Ejemplo", groups = BaseNoBlankGroup.class)
private String label;

@NotBlank(message = "Ejemplo", groups = BaseNoBlankGroup.class)
private String propKey;

@NotBlank(message = "Ejemplo", groups = BaseNoBlankGroup.class)
private String propValue;
}

🚦 Validar en el controlador

Usa @Validated(BaseNoBlankGroup.class) para activar las reglas sólo en ese endpoint.
Además, te recomendamos anotar con @JsonResponseInterceptor para respuestas de error uniformes.

@RestController
@RequestMapping("/api/v1/config")
public class ConfigurationController extends BaseController {

@PostMapping(path="/create", consumes=MediaType.APPLICATION_JSON_VALUE, produces=MediaType.APPLICATION_JSON_VALUE)
@JsonResponseInterceptor
public ResponseEntity<SingleResponseVO<Boolean>> create(
@Validated(BaseNoBlankGroup.class) @RequestBody ConfigurationMinimalVO body) {

}
}

📦 Estructura de respuesta de error (uniforme)

Cuando falla la validación, gracias al handler en BaseController y/o al AOP de @JsonResponseInterceptor, obtendrás una salida consistente:

HTTP 400 — Error de validación
{
"error": [
"El nombre de la aplicación es obligatorio.",
"El perfil es obligatorio."
],
"timeStamp": "2025-07-30T15:04:05.000+00:00",
"status": 400
}

En endpoints anotados con @JsonResponseInterceptor, los errores también pueden envolver un SingleResponseVO con HeaderVO y errors detallados.


🧰 Consejos

  • 🧪 Aplica @Validated(BaseNoBlankGroup.class) sólo donde quieras reglas estrictas (por ejemplo, POST /create, PUT /update).
  • 🧱 Deja reglas no críticas sin grupo para validarlas siempre que uses @Validated o @Valid sin grupo.
  • 🛡️ Mantén @JsonResponseInterceptor en controladores para respuestas homogéneas de errores (validación, negocio, genéricos).

🧭 Checklist rápido

  • BaseNoBlankGroup disponible.
  • ValidationConfiguration registrado.
  • VO anotados con groups = BaseNoBlankGroup.class.
  • Controladores con @Validated(BaseNoBlankGroup.class) (+ @JsonResponseInterceptor).

🚨 Errores, Excepciones y AOP de Respuesta Uniforme

Esta sección explica cómo declarar, lanzar y transportar errores en Alpura, y cómo estandarizar las respuestas JSON con AOP.

🧩 Piezas principales

  • Excepciones
    • AlpuraException
    • ValidationException
  • Errores
    • BusinessError
    • GeneralBusinessErrors
  • Enums
    • ErrorCode
  • AOP (respuesta JSON uniforme)
    • JsonResponseInterceptor (anotación)
    • JsonResponseHandlerInterceptor (aspecto)

🧠 Objetivo: que todos los controladores retornen un formato homogéneo ante errores (validación, negocio o excepciones genéricas).


🧱 Excepciones

AlpuraException (negocio)

  • Úsala para reglas de negocio, colisiones (conflict), no encontrado (not found), etc.
  • Acepta un ErrorCode, message (detalle) y opcionalmente HttpStatus.
throw new AlpuraException(
ErrorCode.REQUIRED_FIELD,
"La configuración ya existe",
HttpStatus.CONFLICT
);

También puedes construirla sólo con ErrorCode o con int code:

throw new AlpuraException(ErrorCode.NO_RESULTS); // sin HttpStatus → 500 por defecto (lo resuelve el AOP)
throw new AlpuraException(1001, "Formato inválido"); // entero custom

ValidationException (validación manual)

  • Úsala cuando quieras forzar una falla de validación fuera de Bean Validation.
if (algoInvalido) {
throw new ValidationException(ErrorCode.REQUIRED_FIELD, "Campo requerido faltante");
}

📝 Para errores de Bean Validation (anotaciones @NotBlank, etc.) el handler de BaseController ya devuelve 400 con el detalle.


🔢 Enum ErrorCode

Algunos códigos incluidos:

public enum ErrorCode {
UNKNOWN_ERROR(0),
REQUIRED_FIELD(1),
EXCEEDS_MAX_LENGTH(2),
INVALID_DATE_FORMAT(1001),
NO_RESULTS(404);
// getCode()
}

Buenas prácticas

  • Usa ErrorCode para comunicar la causa técnica/funcional.
  • El mensaje legible lo controla el AOP y message.properties/env cuando sea necesario.

🛡️ Respuesta JSON uniforme con AOP

Anotación: @JsonResponseInterceptor

Colócala en métodos del controlador que deban estandarizar su salida de error:

@RestController
@RequestMapping("/api/v1/demo")
public class DemoController {

@GetMapping("/boom")
@JsonResponseInterceptor
public ResponseEntity<SingleResponseVO<Boolean>> boom() {
// Lanza algo… será capturado y normalizado:
throw new AlpuraException(ErrorCode.NO_RESULTS, "No hay datos", HttpStatus.NOT_FOUND);
}
}

Aspecto: JsonResponseHandlerInterceptor

  • Intercepta el método, captura:
    • ValidationException → 400
    • AlpuraException → usa el HttpStatus interno o 500
    • Exception → 500
  • Construye una respuesta estándar con SingleResponseVO, HeaderVO y lista de ResponseErrorVO.

Ejemplo de salida (404):

{
"header": { "code": 404, "message": "Not Found" },
"success": false,
"errors": [
{
"code": "404",
"key": null,
"message": "…mensaje resuelto…",
"detail": "No hay datos",
"httpStatus": "NOT_FOUND"
}
],
"data": null
}

🔎 El message de ResponseErrorVO se puede resolver con env.getProperty("arquetipo.error.<code>"). Define esos textos si deseas mensajes por código.


🧪 Ejemplos rápidos

1) Lanzar AlpuraException en Service

if (repo.existsByClave(clave)) {
throw new AlpuraException(ErrorCode.REQUIRED_FIELD, "La clave ya está en uso", HttpStatus.CONFLICT);
}

2) Controlador con AOP

@PostMapping("/create")
@JsonResponseInterceptor
public ResponseEntity<SingleResponseVO<Boolean>> create(@RequestBody Foo body) {
// Si el service lanza AlpuraException, el AOP devuelve un JSON uniforme
return ResponseEntity.ok(new SingleResponseVO<>(true));
}

3) Error de validación (Bean Validation)

Si usas @Validated(BaseNoBlankGroup.class) y fallan campos:

  • El @ExceptionHandler en BaseController retorna 400 con lista de mensajes:
{
"error": ["campo X es obligatorio", "campo Y inválido"],
"timeStamp": "2025-07-30T10:00:00Z",
"status": 400
}

✅ Checklist de adopción

  • Lanzar errores de negocio con AlpuraException (usa ErrorCode + HttpStatus donde aplique).
  • Anotar endpoints con @JsonResponseInterceptor para respuestas homogéneas.
  • (Opcional) Definir arquetipo.error.<code> en application.yml o message.properties.
  • Mantener .properties de errores por idioma (si usas BusinessErrorUtil).
  • Evitar filtrar stacktrace a producción (arquetipo.allowTrace: false).

💡 Consejos

  • Usa ErrorCode para consistencia entre servicios y trazabilidad.
  • Mantén el AOP en todos los controladores públicos para evitar respuestas inconsistentes.
  • Para validación por grupo, combina con @Validated(BaseNoBlankGroup.class) en endpoints de alta/modificación.