feat: PHASE 5.1 - Entités Gestion Documentaire
Entités créées: - Document: Gestion sécurisée avec hash MD5/SHA256, vérification intégrité - PieceJointe: Association flexible avec relations multiples Enum créé (module API): - TypeDocument: IDENTITE, JUSTIFICATIF_DOMICILE, PHOTO, CONTRAT, FACTURE, RECU, RAPPORT, AUTRE Fonctionnalités: - Vérification intégrité avec MD5 et SHA256 - Formatage taille fichiers - Compteur téléchargements - Relations flexibles: Membre, Organisation, Cotisation, Adhesion, DemandeAide, TransactionWave - Validation qu'une seule relation est renseignée Respect strict DRY/WOU: - Patterns d'entité cohérents - Enum dans module API réutilisable
This commit is contained in:
@@ -0,0 +1,30 @@
|
|||||||
|
package dev.lions.unionflow.server.api.enums.document;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Énumération des types de documents
|
||||||
|
*
|
||||||
|
* @author UnionFlow Team
|
||||||
|
* @version 3.0
|
||||||
|
* @since 2025-01-29
|
||||||
|
*/
|
||||||
|
public enum TypeDocument {
|
||||||
|
IDENTITE("Pièce d'Identité"),
|
||||||
|
JUSTIFICATIF_DOMICILE("Justificatif de Domicile"),
|
||||||
|
PHOTO("Photo"),
|
||||||
|
CONTRAT("Contrat"),
|
||||||
|
FACTURE("Facture"),
|
||||||
|
RECU("Reçu"),
|
||||||
|
RAPPORT("Rapport"),
|
||||||
|
AUTRE("Autre");
|
||||||
|
|
||||||
|
private final String libelle;
|
||||||
|
|
||||||
|
TypeDocument(String libelle) {
|
||||||
|
this.libelle = libelle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLibelle() {
|
||||||
|
return libelle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
package dev.lions.unionflow.server.entity;
|
||||||
|
|
||||||
|
import dev.lions.unionflow.server.api.enums.document.TypeDocument;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import jakarta.validation.constraints.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entité Document pour la gestion documentaire sécurisée
|
||||||
|
*
|
||||||
|
* @author UnionFlow Team
|
||||||
|
* @version 3.0
|
||||||
|
* @since 2025-01-29
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(
|
||||||
|
name = "documents",
|
||||||
|
indexes = {
|
||||||
|
@Index(name = "idx_document_nom_fichier", columnList = "nom_fichier"),
|
||||||
|
@Index(name = "idx_document_type", columnList = "type_document"),
|
||||||
|
@Index(name = "idx_document_hash_md5", columnList = "hash_md5"),
|
||||||
|
@Index(name = "idx_document_hash_sha256", columnList = "hash_sha256")
|
||||||
|
})
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class Document extends BaseEntity {
|
||||||
|
|
||||||
|
/** Nom du fichier original */
|
||||||
|
@NotBlank
|
||||||
|
@Column(name = "nom_fichier", nullable = false, length = 255)
|
||||||
|
private String nomFichier;
|
||||||
|
|
||||||
|
/** Nom original du fichier (tel que téléchargé) */
|
||||||
|
@Column(name = "nom_original", length = 255)
|
||||||
|
private String nomOriginal;
|
||||||
|
|
||||||
|
/** Chemin de stockage */
|
||||||
|
@NotBlank
|
||||||
|
@Column(name = "chemin_stockage", nullable = false, length = 1000)
|
||||||
|
private String cheminStockage;
|
||||||
|
|
||||||
|
/** Type MIME */
|
||||||
|
@Column(name = "type_mime", length = 100)
|
||||||
|
private String typeMime;
|
||||||
|
|
||||||
|
/** Taille du fichier en octets */
|
||||||
|
@NotNull
|
||||||
|
@Min(value = 0, message = "La taille doit être positive")
|
||||||
|
@Column(name = "taille_octets", nullable = false)
|
||||||
|
private Long tailleOctets;
|
||||||
|
|
||||||
|
/** Type de document */
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(name = "type_document", length = 50)
|
||||||
|
private TypeDocument typeDocument;
|
||||||
|
|
||||||
|
/** Hash MD5 pour vérification d'intégrité */
|
||||||
|
@Column(name = "hash_md5", length = 32)
|
||||||
|
private String hashMd5;
|
||||||
|
|
||||||
|
/** Hash SHA256 pour vérification d'intégrité */
|
||||||
|
@Column(name = "hash_sha256", length = 64)
|
||||||
|
private String hashSha256;
|
||||||
|
|
||||||
|
/** Description du document */
|
||||||
|
@Column(name = "description", length = 1000)
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/** Nombre de téléchargements */
|
||||||
|
@Builder.Default
|
||||||
|
@Column(name = "nombre_telechargements", nullable = false)
|
||||||
|
private Integer nombreTelechargements = 0;
|
||||||
|
|
||||||
|
/** Date de dernier téléchargement */
|
||||||
|
@Column(name = "date_dernier_telechargement")
|
||||||
|
private java.time.LocalDateTime dateDernierTelechargement;
|
||||||
|
|
||||||
|
/** Pièces jointes associées */
|
||||||
|
@OneToMany(mappedBy = "document", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||||
|
@Builder.Default
|
||||||
|
private List<PieceJointe> piecesJointes = new ArrayList<>();
|
||||||
|
|
||||||
|
/** Méthode métier pour vérifier l'intégrité avec MD5 */
|
||||||
|
public boolean verifierIntegriteMd5(String hashAttendu) {
|
||||||
|
return hashMd5 != null && hashMd5.equalsIgnoreCase(hashAttendu);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Méthode métier pour vérifier l'intégrité avec SHA256 */
|
||||||
|
public boolean verifierIntegriteSha256(String hashAttendu) {
|
||||||
|
return hashSha256 != null && hashSha256.equalsIgnoreCase(hashAttendu);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Méthode métier pour obtenir la taille formatée */
|
||||||
|
public String getTailleFormatee() {
|
||||||
|
if (tailleOctets == null) {
|
||||||
|
return "0 B";
|
||||||
|
}
|
||||||
|
if (tailleOctets < 1024) {
|
||||||
|
return tailleOctets + " B";
|
||||||
|
} else if (tailleOctets < 1024 * 1024) {
|
||||||
|
return String.format("%.2f KB", tailleOctets / 1024.0);
|
||||||
|
} else {
|
||||||
|
return String.format("%.2f MB", tailleOctets / (1024.0 * 1024.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Callback JPA avant la persistance */
|
||||||
|
@PrePersist
|
||||||
|
protected void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
if (nombreTelechargements == null) {
|
||||||
|
nombreTelechargements = 0;
|
||||||
|
}
|
||||||
|
if (typeDocument == null) {
|
||||||
|
typeDocument = TypeDocument.AUTRE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
package dev.lions.unionflow.server.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import jakarta.validation.constraints.*;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entité PieceJointe pour l'association flexible de documents
|
||||||
|
*
|
||||||
|
* @author UnionFlow Team
|
||||||
|
* @version 3.0
|
||||||
|
* @since 2025-01-29
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(
|
||||||
|
name = "pieces_jointes",
|
||||||
|
indexes = {
|
||||||
|
@Index(name = "idx_piece_jointe_document", columnList = "document_id"),
|
||||||
|
@Index(name = "idx_piece_jointe_membre", columnList = "membre_id"),
|
||||||
|
@Index(name = "idx_piece_jointe_organisation", columnList = "organisation_id"),
|
||||||
|
@Index(name = "idx_piece_jointe_cotisation", columnList = "cotisation_id"),
|
||||||
|
@Index(name = "idx_piece_jointe_adhesion", columnList = "adhesion_id"),
|
||||||
|
@Index(name = "idx_piece_jointe_demande_aide", columnList = "demande_aide_id"),
|
||||||
|
@Index(name = "idx_piece_jointe_transaction_wave", columnList = "transaction_wave_id")
|
||||||
|
})
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class PieceJointe extends BaseEntity {
|
||||||
|
|
||||||
|
/** Ordre d'affichage */
|
||||||
|
@NotNull
|
||||||
|
@Min(value = 1, message = "L'ordre doit être positif")
|
||||||
|
@Column(name = "ordre", nullable = false)
|
||||||
|
private Integer ordre;
|
||||||
|
|
||||||
|
/** Libellé de la pièce jointe */
|
||||||
|
@Column(name = "libelle", length = 200)
|
||||||
|
private String libelle;
|
||||||
|
|
||||||
|
/** Commentaire */
|
||||||
|
@Column(name = "commentaire", length = 500)
|
||||||
|
private String commentaire;
|
||||||
|
|
||||||
|
/** Document associé */
|
||||||
|
@NotNull
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "document_id", nullable = false)
|
||||||
|
private Document document;
|
||||||
|
|
||||||
|
// Relations flexibles (une seule doit être renseignée)
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "membre_id")
|
||||||
|
private Membre membre;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "organisation_id")
|
||||||
|
private Organisation organisation;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "cotisation_id")
|
||||||
|
private Cotisation cotisation;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "adhesion_id")
|
||||||
|
private Adhesion adhesion;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "demande_aide_id")
|
||||||
|
private DemandeAide demandeAide;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "transaction_wave_id")
|
||||||
|
private TransactionWave transactionWave;
|
||||||
|
|
||||||
|
/** Méthode métier pour vérifier qu'une seule relation est renseignée */
|
||||||
|
public boolean isValide() {
|
||||||
|
int count = 0;
|
||||||
|
if (membre != null) count++;
|
||||||
|
if (organisation != null) count++;
|
||||||
|
if (cotisation != null) count++;
|
||||||
|
if (adhesion != null) count++;
|
||||||
|
if (demandeAide != null) count++;
|
||||||
|
if (transactionWave != null) count++;
|
||||||
|
return count == 1; // Exactement une relation doit être renseignée
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Callback JPA avant la persistance */
|
||||||
|
@PrePersist
|
||||||
|
protected void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
if (ordre == null) {
|
||||||
|
ordre = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user