fix: Ajout des classes Entity et Mapper manquantes pour Audit

This commit is contained in:
lionsdev
2026-01-03 15:23:47 +00:00
parent 3773fac0b0
commit bf1e9e16d8
2 changed files with 388 additions and 0 deletions

View File

@@ -0,0 +1,209 @@
package dev.lions.user.manager.server.impl.entity;
import dev.lions.user.manager.enums.audit.TypeActionAudit;
import io.quarkus.hibernate.orm.panache.PanacheEntity;
import jakarta.persistence.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* Entité JPA pour la persistance des logs d'audit en base de données PostgreSQL.
*
* <p>Cette entité représente un enregistrement d'audit qui track toutes les actions
* effectuées sur les utilisateurs du système (création, modification, suppression, etc.).</p>
*
* <p><b>Utilisation:</b></p>
* <pre>
* AuditLogEntity auditLog = new AuditLogEntity();
* auditLog.setUserId("user-123");
* auditLog.setAction(TypeActionAudit.CREATION_UTILISATEUR);
* auditLog.setDetails("Utilisateur créé avec succès");
* auditLog.setAuteurAction("admin");
* auditLog.setTimestamp(LocalDateTime.now());
* auditLog.persist();
* </pre>
*
* @see dev.lions.user.manager.server.api.dto.AuditLogDTO
* @see TypeActionAudit
* @author Lions Development Team
* @version 1.0.0
* @since 2026-01-02
*/
@Entity
@Table(
name = "audit_logs",
indexes = {
@Index(name = "idx_audit_user_id", columnList = "user_id"),
@Index(name = "idx_audit_action", columnList = "action"),
@Index(name = "idx_audit_timestamp", columnList = "timestamp"),
@Index(name = "idx_audit_auteur", columnList = "auteur_action")
}
)
@Data
@EqualsAndHashCode(callSuper = true)
public class AuditLogEntity extends PanacheEntity {
/**
* ID de l'utilisateur concerné par l'action.
* <p>Peut être null pour les actions système qui ne concernent pas un utilisateur spécifique.</p>
*/
@Column(name = "user_id", length = 255)
private String userId;
/**
* Type d'action effectuée (CREATION_UTILISATEUR, MODIFICATION_UTILISATEUR, etc.).
* <p>Stocké en tant que STRING pour faciliter la lecture en base de données.</p>
*/
@Column(name = "action", nullable = false, length = 100)
@Enumerated(EnumType.STRING)
private TypeActionAudit action;
/**
* Détails complémentaires sur l'action effectuée.
* <p>Peut contenir des informations contextuelles comme les champs modifiés,
* les raisons d'une action, ou des messages d'erreur.</p>
*/
@Column(name = "details", columnDefinition = "TEXT")
private String details;
/**
* Identifiant de l'utilisateur qui a effectué l'action.
* <p>Généralement l'username ou l'ID de l'administrateur/utilisateur connecté.</p>
*/
@Column(name = "auteur_action", nullable = false, length = 255)
private String auteurAction;
/**
* Timestamp précis de l'action.
* <p>Utilisé pour l'ordre chronologique des logs et le filtrage temporel.</p>
*/
@Column(name = "timestamp", nullable = false)
private LocalDateTime timestamp;
/**
* Adresse IP de l'auteur de l'action.
* <p>Utile pour la traçabilité et la détection d'anomalies.</p>
*/
@Column(name = "ip_address", length = 45)
private String ipAddress;
/**
* User-Agent du client (navigateur, application, etc.).
* <p>Permet d'identifier le type de client utilisé pour l'action.</p>
*/
@Column(name = "user_agent", length = 500)
private String userAgent;
/**
* Nom du realm Keycloak concerné.
* <p>Important dans un environnement multi-tenant pour isoler les logs par realm.</p>
*/
@Column(name = "realm_name", length = 255)
private String realmName;
/**
* Indique si l'action a réussi ou échoué.
* <p>Permet de filtrer facilement les actions en erreur pour analyse.</p>
*/
@Column(name = "success", nullable = false)
private Boolean success = true;
/**
* Message d'erreur en cas d'échec de l'action.
* <p>Null si success = true.</p>
*/
@Column(name = "error_message", columnDefinition = "TEXT")
private String errorMessage;
/**
* Constructeur par défaut requis par JPA.
*/
public AuditLogEntity() {
this.timestamp = LocalDateTime.now();
}
/**
* Recherche tous les logs d'audit pour un utilisateur donné.
*
* @param userId ID de l'utilisateur
* @return Liste des logs triés par timestamp décroissant
*/
public static java.util.List<AuditLogEntity> findByUserId(String userId) {
return list("userId = ?1 order by timestamp desc", userId);
}
/**
* Recherche tous les logs d'audit d'un type d'action donné.
*
* @param action Type d'action
* @return Liste des logs triés par timestamp décroissant
*/
public static java.util.List<AuditLogEntity> findByAction(TypeActionAudit action) {
return list("action = ?1 order by timestamp desc", action);
}
/**
* Recherche tous les logs d'audit pour un auteur donné.
*
* @param auteurAction Identifiant de l'auteur
* @return Liste des logs triés par timestamp décroissant
*/
public static java.util.List<AuditLogEntity> findByAuteur(String auteurAction) {
return list("auteurAction = ?1 order by timestamp desc", auteurAction);
}
/**
* Recherche tous les logs d'audit dans une période donnée.
*
* @param startDate Date de début (inclusive)
* @param endDate Date de fin (inclusive)
* @return Liste des logs dans la période, triés par timestamp décroissant
*/
public static java.util.List<AuditLogEntity> findByPeriod(LocalDateTime startDate, LocalDateTime endDate) {
return list("timestamp >= ?1 and timestamp <= ?2 order by timestamp desc", startDate, endDate);
}
/**
* Recherche tous les logs d'audit pour un realm donné.
*
* @param realmName Nom du realm
* @return Liste des logs triés par timestamp décroissant
*/
public static java.util.List<AuditLogEntity> findByRealm(String realmName) {
return list("realmName = ?1 order by timestamp desc", realmName);
}
/**
* Supprime tous les logs d'audit plus anciens qu'une date donnée.
* <p>Utile pour la maintenance et le respect des politiques de rétention.</p>
*
* @param beforeDate Date limite (les logs avant cette date seront supprimés)
* @return Nombre de logs supprimés
*/
public static long deleteOlderThan(LocalDateTime beforeDate) {
return delete("timestamp < ?1", beforeDate);
}
/**
* Compte le nombre d'actions effectuées par un auteur donné.
*
* @param auteurAction Identifiant de l'auteur
* @return Nombre d'actions
*/
public static long countByAuteur(String auteurAction) {
return count("auteurAction = ?1", auteurAction);
}
/**
* Compte le nombre d'échecs pour un utilisateur donné.
* <p>Utile pour détecter des problèmes récurrents.</p>
*
* @param userId ID de l'utilisateur
* @return Nombre d'échecs
*/
public static long countFailuresByUserId(String userId) {
return count("userId = ?1 and success = false", userId);
}
}

View File

@@ -0,0 +1,179 @@
package dev.lions.user.manager.server.impl.mapper;
import dev.lions.user.manager.dto.audit.AuditLogDTO;
import dev.lions.user.manager.server.impl.entity.AuditLogEntity;
import org.mapstruct.*;
import java.util.List;
/**
* Mapper MapStruct pour la conversion entre AuditLogEntity (JPA) et AuditLogDTO (API).
*
* <p>Ce mapper gère la transformation bidirectionnelle entre l'entité de persistance
* et le DTO exposé via l'API REST, avec mapping automatique des champs compatibles.</p>
*
* <p><b>Fonctionnalités:</b></p>
* <ul>
* <li>Conversion Entity → DTO pour lecture/API</li>
* <li>Conversion DTO → Entity pour persistance</li>
* <li>Mapping de listes pour opérations bulk</li>
* <li>Gestion automatique des types LocalDateTime</li>
* <li>Mapping des enums (TypeActionAudit)</li>
* </ul>
*
* <p><b>Utilisation:</b></p>
* <pre>
* {@literal @}Inject
* AuditLogMapper mapper;
*
* // Entity → DTO
* AuditLogDTO dto = mapper.toDTO(entity);
*
* // DTO → Entity
* AuditLogEntity entity = mapper.toEntity(dto);
*
* // Liste Entity → Liste DTO
* List&lt;AuditLogDTO&gt; dtos = mapper.toDTOList(entities);
* </pre>
*
* @see AuditLogEntity
* @see AuditLogDTO
* @author Lions Development Team
* @version 1.0.0
* @since 2026-01-02
*/
@Mapper(
componentModel = MappingConstants.ComponentModel.JAKARTA_CDI,
injectionStrategy = InjectionStrategy.CONSTRUCTOR,
unmappedTargetPolicy = ReportingPolicy.IGNORE
)
public interface AuditLogMapper {
/**
* Convertit une entité AuditLogEntity en DTO AuditLogDTO.
*
* <p>Mapping des champs Entity → DTO:</p>
* <ul>
* <li>id (Long) → id (String)</li>
* <li>userId → ressourceId</li>
* <li>action → typeAction</li>
* <li>details → description</li>
* <li>auteurAction → acteurUsername</li>
* <li>timestamp → dateAction</li>
* <li>ipAddress → ipAddress</li>
* <li>userAgent → userAgent</li>
* <li>realmName → realmName</li>
* <li>success → success</li>
* <li>errorMessage → errorMessage</li>
* </ul>
*
* @param entity L'entité JPA à convertir (peut être null)
* @return Le DTO correspondant, ou null si l'entité est null
*/
@Mapping(target = "id", source = "id", qualifiedByName = "longToString")
@Mapping(target = "ressourceId", source = "userId")
@Mapping(target = "typeAction", source = "action")
@Mapping(target = "description", source = "details")
@Mapping(target = "acteurUsername", source = "auteurAction")
@Mapping(target = "dateAction", source = "timestamp")
AuditLogDTO toDTO(AuditLogEntity entity);
/**
* Convertit un DTO AuditLogDTO en entité AuditLogEntity.
*
* <p>Utilisé pour créer une nouvelle entité à persister depuis les données API.</p>
*
* <p><b>Note:</b> L'ID de l'entité sera null (auto-généré par la DB),
* même si l'ID du DTO est renseigné.</p>
*
* @param dto Le DTO à convertir (peut être null)
* @return L'entité JPA correspondante, ou null si le DTO est null
*/
@Mapping(target = "id", ignore = true) // L'ID sera généré par la DB
@Mapping(target = "userId", source = "ressourceId")
@Mapping(target = "action", source = "typeAction")
@Mapping(target = "details", source = "description")
@Mapping(target = "auteurAction", source = "acteurUsername")
@Mapping(target = "timestamp", source = "dateAction")
AuditLogEntity toEntity(AuditLogDTO dto);
/**
* Convertit une liste d'entités en liste de DTOs.
*
* <p>Utile pour les recherches qui retournent plusieurs résultats.</p>
*
* @param entities Liste des entités à convertir (peut être null ou vide)
* @return Liste des DTOs correspondants, ou liste vide si entities est null/vide
*/
List<AuditLogDTO> toDTOList(List<AuditLogEntity> entities);
/**
* Convertit une liste de DTOs en liste d'entités.
*
* <p>Utile pour les opérations d'import ou de création en masse.</p>
*
* @param dtos Liste des DTOs à convertir (peut être null ou vide)
* @return Liste des entités correspondantes, ou liste vide si dtos est null/vide
*/
List<AuditLogEntity> toEntityList(List<AuditLogDTO> dtos);
/**
* Met à jour une entité existante avec les données d'un DTO.
*
* <p>Préserve l'ID de l'entité et ne met à jour que les champs
* présents dans le DTO.</p>
*
* <p><b>Utilisation:</b></p>
* <pre>
* AuditLogEntity existingEntity = AuditLogEntity.findById(id);
* mapper.updateEntityFromDTO(dto, existingEntity);
* existingEntity.persist();
* </pre>
*
* @param dto Le DTO source contenant les nouvelles valeurs
* @param entity L'entité cible à mettre à jour
*/
@Mapping(target = "id", ignore = true) // Préserve l'ID existant
@Mapping(target = "userId", source = "ressourceId")
@Mapping(target = "action", source = "typeAction")
@Mapping(target = "details", source = "description")
@Mapping(target = "auteurAction", source = "acteurUsername")
@Mapping(target = "timestamp", source = "dateAction")
@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
void updateEntityFromDTO(AuditLogDTO dto, @MappingTarget AuditLogEntity entity);
/**
* Convertit un Long (ID de l'entité) en String (ID du DTO).
*
* <p>MapStruct appelle automatiquement cette méthode pour le mapping de l'ID.</p>
*
* @param id L'ID de type Long (peut être null)
* @return L'ID converti en String, ou null si l'input est null
*/
@Named("longToString")
default String longToString(Long id) {
return id != null ? id.toString() : null;
}
/**
* Convertit un String (ID du DTO) en Long (ID de l'entité).
*
* <p>Utilisé lors de la conversion DTO → Entity si nécessaire.</p>
*
* @param id L'ID de type String (peut être null)
* @return L'ID converti en Long, ou null si l'input est null ou invalide
*/
@Named("stringToLong")
default Long stringToLong(String id) {
if (id == null || id.isBlank()) {
return null;
}
try {
return Long.parseLong(id);
} catch (NumberFormatException e) {
// Log warning et retourne null en cas de format invalide
System.err.println("WARN: Invalid ID format for conversion to Long: " + id);
return null;
}
}
}