Refactoring

This commit is contained in:
DahoudG
2025-09-17 17:54:06 +00:00
parent 12d514d866
commit 63fe107f98
165 changed files with 54220 additions and 276 deletions

View File

@@ -0,0 +1,261 @@
package dev.lions.unionflow.server.api.dto.analytics;
import com.fasterxml.jackson.annotation.JsonFormat;
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
import dev.lions.unionflow.server.api.enums.analytics.TypeMetrique;
import dev.lions.unionflow.server.api.enums.analytics.PeriodeAnalyse;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/**
* DTO pour les données analytics UnionFlow
*
* Représente une donnée analytique avec sa valeur, sa métrique associée,
* sa période d'analyse et ses métadonnées.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AnalyticsDataDTO extends BaseDTO {
private static final long serialVersionUID = 1L;
/** Type de métrique analysée */
@NotNull(message = "Le type de métrique est obligatoire")
private TypeMetrique typeMetrique;
/** Période d'analyse */
@NotNull(message = "La période d'analyse est obligatoire")
private PeriodeAnalyse periodeAnalyse;
/** Valeur numérique de la métrique */
@NotNull(message = "La valeur est obligatoire")
@DecimalMin(value = "0.0", message = "La valeur doit être positive ou nulle")
@Digits(integer = 15, fraction = 4, message = "Format de valeur invalide")
private BigDecimal valeur;
/** Valeur précédente pour comparaison */
@DecimalMin(value = "0.0", message = "La valeur précédente doit être positive ou nulle")
@Digits(integer = 15, fraction = 4, message = "Format de valeur précédente invalide")
private BigDecimal valeurPrecedente;
/** Pourcentage d'évolution par rapport à la période précédente */
@Digits(integer = 6, fraction = 2, message = "Format de pourcentage d'évolution invalide")
private BigDecimal pourcentageEvolution;
/** Date de début de la période analysée */
@NotNull(message = "La date de début est obligatoire")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateDebut;
/** Date de fin de la période analysée */
@NotNull(message = "La date de fin est obligatoire")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateFin;
/** Date de calcul de la métrique */
@NotNull(message = "La date de calcul est obligatoire")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateCalcul;
/** Identifiant de l'organisation (optionnel pour filtrage) */
private UUID organisationId;
/** Nom de l'organisation */
@Size(max = 200, message = "Le nom de l'organisation ne peut pas dépasser 200 caractères")
private String nomOrganisation;
/** Identifiant de l'utilisateur qui a demandé le calcul */
private UUID utilisateurId;
/** Nom de l'utilisateur qui a demandé le calcul */
@Size(max = 200, message = "Le nom de l'utilisateur ne peut pas dépasser 200 caractères")
private String nomUtilisateur;
/** Libellé personnalisé de la métrique */
@Size(max = 300, message = "Le libellé personnalisé ne peut pas dépasser 300 caractères")
private String libellePersonnalise;
/** Description ou commentaire sur la métrique */
@Size(max = 1000, message = "La description ne peut pas dépasser 1000 caractères")
private String description;
/** Données détaillées pour les graphiques (format JSON) */
@Size(max = 10000, message = "Les données détaillées ne peuvent pas dépasser 10000 caractères")
private String donneesDetaillees;
/** Configuration du graphique (couleurs, type, etc.) */
@Size(max = 2000, message = "La configuration graphique ne peut pas dépasser 2000 caractères")
private String configurationGraphique;
/** Métadonnées additionnelles */
private Map<String, Object> metadonnees;
/** Indicateur de fiabilité des données (0-100) */
@DecimalMin(value = "0.0", message = "L'indicateur de fiabilité doit être positif")
@DecimalMax(value = "100.0", message = "L'indicateur de fiabilité ne peut pas dépasser 100")
@Digits(integer = 3, fraction = 1, message = "Format d'indicateur de fiabilité invalide")
private BigDecimal indicateurFiabilite;
/** Nombre d'éléments analysés pour calculer cette métrique */
@DecimalMin(value = "0", message = "Le nombre d'éléments doit être positif")
private Integer nombreElementsAnalyses;
/** Temps de calcul en millisecondes */
@DecimalMin(value = "0", message = "Le temps de calcul doit être positif")
private Long tempsCalculMs;
/** Indicateur si la métrique est en temps réel */
@Builder.Default
private Boolean tempsReel = false;
/** Indicateur si la métrique nécessite une mise à jour */
@Builder.Default
private Boolean necessiteMiseAJour = false;
/** Niveau de priorité de la métrique (1=faible, 5=critique) */
@DecimalMin(value = "1", message = "Le niveau de priorité minimum est 1")
@DecimalMax(value = "5", message = "Le niveau de priorité maximum est 5")
private Integer niveauPriorite;
/** Tags pour catégoriser la métrique */
private List<String> tags;
// === MÉTHODES UTILITAIRES ===
/**
* Retourne le libellé à afficher (personnalisé ou par défaut)
*
* @return Le libellé à afficher
*/
public String getLibelleAffichage() {
return libellePersonnalise != null && !libellePersonnalise.trim().isEmpty()
? libellePersonnalise
: typeMetrique.getLibelle();
}
/**
* Retourne l'unité de mesure de la métrique
*
* @return L'unité de mesure
*/
public String getUnite() {
return typeMetrique.getUnite();
}
/**
* Retourne l'icône de la métrique
*
* @return L'icône Material Design
*/
public String getIcone() {
return typeMetrique.getIcone();
}
/**
* Retourne la couleur de la métrique
*
* @return Le code couleur hexadécimal
*/
public String getCouleur() {
return typeMetrique.getCouleur();
}
/**
* Vérifie si la métrique a évolué positivement
*
* @return true si l'évolution est positive
*/
public boolean hasEvolutionPositive() {
return pourcentageEvolution != null && pourcentageEvolution.compareTo(BigDecimal.ZERO) > 0;
}
/**
* Vérifie si la métrique a évolué négativement
*
* @return true si l'évolution est négative
*/
public boolean hasEvolutionNegative() {
return pourcentageEvolution != null && pourcentageEvolution.compareTo(BigDecimal.ZERO) < 0;
}
/**
* Vérifie si la métrique est stable (pas d'évolution)
*
* @return true si l'évolution est nulle
*/
public boolean isStable() {
return pourcentageEvolution != null && pourcentageEvolution.compareTo(BigDecimal.ZERO) == 0;
}
/**
* Retourne la tendance sous forme de texte
*
* @return "hausse", "baisse" ou "stable"
*/
public String getTendance() {
if (hasEvolutionPositive()) return "hausse";
if (hasEvolutionNegative()) return "baisse";
return "stable";
}
/**
* Vérifie si les données sont fiables (indicateur > 80)
*
* @return true si les données sont considérées comme fiables
*/
public boolean isDonneesFiables() {
return indicateurFiabilite != null &&
indicateurFiabilite.compareTo(new BigDecimal("80.0")) >= 0;
}
/**
* Vérifie si la métrique est critique (priorité >= 4)
*
* @return true si la métrique est critique
*/
public boolean isCritique() {
return niveauPriorite != null && niveauPriorite >= 4;
}
/**
* Constructeur avec les champs essentiels
*
* @param typeMetrique Le type de métrique
* @param periodeAnalyse La période d'analyse
* @param valeur La valeur de la métrique
*/
public AnalyticsDataDTO(TypeMetrique typeMetrique, PeriodeAnalyse periodeAnalyse, BigDecimal valeur) {
super();
this.typeMetrique = typeMetrique;
this.periodeAnalyse = periodeAnalyse;
this.valeur = valeur;
this.dateCalcul = LocalDateTime.now();
this.dateDebut = periodeAnalyse.getDateDebut();
this.dateFin = periodeAnalyse.getDateFin();
this.tempsReel = false;
this.necessiteMiseAJour = false;
this.niveauPriorite = 3; // Priorité normale par défaut
this.indicateurFiabilite = new BigDecimal("95.0"); // Fiabilité élevée par défaut
}
}

View File

@@ -0,0 +1,343 @@
package dev.lions.unionflow.server.api.dto.analytics;
import com.fasterxml.jackson.annotation.JsonFormat;
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
import dev.lions.unionflow.server.api.enums.analytics.TypeMetrique;
import dev.lions.unionflow.server.api.enums.analytics.PeriodeAnalyse;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.UUID;
/**
* DTO pour les widgets de tableau de bord analytics UnionFlow
*
* Représente un widget personnalisable affiché sur le tableau de bord
* avec sa configuration, sa position et ses données.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DashboardWidgetDTO extends BaseDTO {
private static final long serialVersionUID = 1L;
/** Titre du widget */
@NotBlank(message = "Le titre du widget est obligatoire")
@Size(min = 3, max = 200, message = "Le titre du widget doit contenir entre 3 et 200 caractères")
private String titre;
/** Description du widget */
@Size(max = 500, message = "La description ne peut pas dépasser 500 caractères")
private String description;
/** Type de widget (kpi, chart, table, gauge, progress, text) */
@NotBlank(message = "Le type de widget est obligatoire")
@Size(max = 50, message = "Le type de widget ne peut pas dépasser 50 caractères")
private String typeWidget;
/** Type de métrique affiché */
private TypeMetrique typeMetrique;
/** Période d'analyse */
private PeriodeAnalyse periodeAnalyse;
/** Identifiant de l'organisation (optionnel pour filtrage) */
private UUID organisationId;
/** Nom de l'organisation */
@Size(max = 200, message = "Le nom de l'organisation ne peut pas dépasser 200 caractères")
private String nomOrganisation;
/** Identifiant de l'utilisateur propriétaire */
@NotNull(message = "L'identifiant de l'utilisateur propriétaire est obligatoire")
private UUID utilisateurProprietaireId;
/** Nom de l'utilisateur propriétaire */
@Size(max = 200, message = "Le nom de l'utilisateur propriétaire ne peut pas dépasser 200 caractères")
private String nomUtilisateurProprietaire;
/** Position X du widget sur la grille */
@NotNull(message = "La position X est obligatoire")
@DecimalMin(value = "0", message = "La position X doit être positive ou nulle")
private Integer positionX;
/** Position Y du widget sur la grille */
@NotNull(message = "La position Y est obligatoire")
@DecimalMin(value = "0", message = "La position Y doit être positive ou nulle")
private Integer positionY;
/** Largeur du widget (en unités de grille) */
@NotNull(message = "La largeur est obligatoire")
@DecimalMin(value = "1", message = "La largeur minimum est 1")
@DecimalMax(value = "12", message = "La largeur maximum est 12")
private Integer largeur;
/** Hauteur du widget (en unités de grille) */
@NotNull(message = "La hauteur est obligatoire")
@DecimalMin(value = "1", message = "La hauteur minimum est 1")
@DecimalMax(value = "12", message = "La hauteur maximum est 12")
private Integer hauteur;
/** Ordre d'affichage (z-index) */
@DecimalMin(value = "0", message = "L'ordre d'affichage doit être positif ou nul")
@Builder.Default
private Integer ordreAffichage = 0;
/** Configuration visuelle du widget */
@Size(max = 5000, message = "La configuration visuelle ne peut pas dépasser 5000 caractères")
private String configurationVisuelle;
/** Couleur principale du widget */
@Size(max = 7, message = "La couleur doit être au format #RRGGBB")
private String couleurPrincipale;
/** Couleur secondaire du widget */
@Size(max = 7, message = "La couleur secondaire doit être au format #RRGGBB")
private String couleurSecondaire;
/** Icône du widget */
@Size(max = 50, message = "L'icône ne peut pas dépasser 50 caractères")
private String icone;
/** Indicateur si le widget est visible */
@Builder.Default
private Boolean visible = true;
/** Indicateur si le widget est redimensionnable */
@Builder.Default
private Boolean redimensionnable = true;
/** Indicateur si le widget est déplaçable */
@Builder.Default
private Boolean deplacable = true;
/** Indicateur si le widget peut être supprimé */
@Builder.Default
private Boolean supprimable = true;
/** Indicateur si le widget se met à jour automatiquement */
@Builder.Default
private Boolean miseAJourAutomatique = true;
/** Fréquence de mise à jour en secondes */
@DecimalMin(value = "30", message = "La fréquence minimum est 30 secondes")
@Builder.Default
private Integer frequenceMiseAJourSecondes = 300; // 5 minutes par défaut
/** Date de dernière mise à jour des données */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateDerniereMiseAJour;
/** Prochaine mise à jour programmée */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime prochaineMiseAJour;
/** Données du widget (format JSON) */
@Size(max = 50000, message = "Les données du widget ne peuvent pas dépasser 50000 caractères")
private String donneesWidget;
/** Configuration des filtres */
private Map<String, Object> configurationFiltres;
/** Configuration des alertes */
private Map<String, Object> configurationAlertes;
/** Seuil d'alerte bas */
private Double seuilAlerteBas;
/** Seuil d'alerte haut */
private Double seuilAlerteHaut;
/** Indicateur si une alerte est active */
@Builder.Default
private Boolean alerteActive = false;
/** Message d'alerte actuel */
@Size(max = 500, message = "Le message d'alerte ne peut pas dépasser 500 caractères")
private String messageAlerte;
/** Type d'alerte (info, warning, error, success) */
@Size(max = 20, message = "Le type d'alerte ne peut pas dépasser 20 caractères")
private String typeAlerte;
/** Permissions d'accès au widget */
@Size(max = 1000, message = "Les permissions ne peuvent pas dépasser 1000 caractères")
private String permissions;
/** Rôles autorisés à voir le widget */
@Size(max = 500, message = "Les rôles autorisés ne peuvent pas dépasser 500 caractères")
private String rolesAutorises;
/** Template personnalisé du widget */
@Size(max = 10000, message = "Le template personnalisé ne peut pas dépasser 10000 caractères")
private String templatePersonnalise;
/** CSS personnalisé du widget */
@Size(max = 5000, message = "Le CSS personnalisé ne peut pas dépasser 5000 caractères")
private String cssPersonnalise;
/** JavaScript personnalisé du widget */
@Size(max = 10000, message = "Le JavaScript personnalisé ne peut pas dépasser 10000 caractères")
private String javascriptPersonnalise;
/** Métadonnées additionnelles */
private Map<String, Object> metadonnees;
/** Nombre de vues du widget */
@DecimalMin(value = "0", message = "Le nombre de vues doit être positif")
@Builder.Default
private Long nombreVues = 0L;
/** Nombre d'interactions avec le widget */
@DecimalMin(value = "0", message = "Le nombre d'interactions doit être positif")
@Builder.Default
private Long nombreInteractions = 0L;
/** Temps moyen passé sur le widget (en secondes) */
@DecimalMin(value = "0", message = "Le temps moyen doit être positif")
private Integer tempsMoyenSecondes;
/** Taux d'erreur du widget (en pourcentage) */
@DecimalMin(value = "0.0", message = "Le taux d'erreur doit être positif")
@DecimalMax(value = "100.0", message = "Le taux d'erreur ne peut pas dépasser 100%")
@Builder.Default
private Double tauxErreur = 0.0;
/** Date de dernière erreur */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateDerniereErreur;
/** Message de dernière erreur */
@Size(max = 1000, message = "Le message d'erreur ne peut pas dépasser 1000 caractères")
private String messageDerniereErreur;
// === MÉTHODES UTILITAIRES ===
/**
* Retourne le libellé de la métrique si définie
*
* @return Le libellé de la métrique ou null
*/
public String getLibelleMetrique() {
return typeMetrique != null ? typeMetrique.getLibelle() : null;
}
/**
* Retourne l'unité de mesure si métrique définie
*
* @return L'unité de mesure ou chaîne vide
*/
public String getUnite() {
return typeMetrique != null ? typeMetrique.getUnite() : "";
}
/**
* Retourne l'icône de la métrique ou l'icône personnalisée
*
* @return L'icône à afficher
*/
public String getIconeAffichage() {
if (icone != null && !icone.trim().isEmpty()) {
return icone;
}
return typeMetrique != null ? typeMetrique.getIcone() : "dashboard";
}
/**
* Retourne la couleur de la métrique ou la couleur personnalisée
*
* @return La couleur à utiliser
*/
public String getCouleurAffichage() {
if (couleurPrincipale != null && !couleurPrincipale.trim().isEmpty()) {
return couleurPrincipale;
}
return typeMetrique != null ? typeMetrique.getCouleur() : "#757575";
}
/**
* Vérifie si le widget nécessite une mise à jour
*
* @return true si une mise à jour est nécessaire
*/
public boolean necessiteMiseAJour() {
return miseAJourAutomatique && prochaineMiseAJour != null &&
prochaineMiseAJour.isBefore(LocalDateTime.now());
}
/**
* Vérifie si le widget est interactif
*
* @return true si le widget permet des interactions
*/
public boolean isInteractif() {
return "chart".equals(typeWidget) || "table".equals(typeWidget) ||
"gauge".equals(typeWidget);
}
/**
* Vérifie si le widget affiche des données temps réel
*
* @return true si le widget est en temps réel
*/
public boolean isTempsReel() {
return frequenceMiseAJourSecondes != null && frequenceMiseAJourSecondes <= 60;
}
/**
* Retourne la taille du widget (surface occupée)
*
* @return La surface en unités de grille
*/
public int getTailleWidget() {
return largeur * hauteur;
}
/**
* Vérifie si le widget est grand (surface > 6)
*
* @return true si le widget est considéré comme grand
*/
public boolean isWidgetGrand() {
return getTailleWidget() > 6;
}
/**
* Vérifie si le widget a des erreurs récentes (< 24h)
*
* @return true si des erreurs récentes sont détectées
*/
public boolean hasErreursRecentes() {
return dateDerniereErreur != null &&
dateDerniereErreur.isAfter(LocalDateTime.now().minusHours(24));
}
/**
* Retourne le statut du widget
*
* @return "actif", "erreur", "inactif" ou "maintenance"
*/
public String getStatutWidget() {
if (hasErreursRecentes()) return "erreur";
if (!visible) return "inactif";
if (tauxErreur > 10.0) return "maintenance";
return "actif";
}
}

View File

@@ -0,0 +1,315 @@
package dev.lions.unionflow.server.api.dto.analytics;
import com.fasterxml.jackson.annotation.JsonFormat;
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
import dev.lions.unionflow.server.api.enums.analytics.TypeMetrique;
import dev.lions.unionflow.server.api.enums.analytics.PeriodeAnalyse;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;
/**
* DTO pour les tendances et évolutions des KPI UnionFlow
*
* Représente l'évolution d'un KPI dans le temps avec les points de données
* historiques pour générer des graphiques de tendance.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class KPITrendDTO extends BaseDTO {
private static final long serialVersionUID = 1L;
/** Type de métrique pour cette tendance */
@NotNull(message = "Le type de métrique est obligatoire")
private TypeMetrique typeMetrique;
/** Période d'analyse globale */
@NotNull(message = "La période d'analyse est obligatoire")
private PeriodeAnalyse periodeAnalyse;
/** Identifiant de l'organisation (optionnel) */
private UUID organisationId;
/** Nom de l'organisation */
@Size(max = 200, message = "Le nom de l'organisation ne peut pas dépasser 200 caractères")
private String nomOrganisation;
/** Date de début de la période analysée */
@NotNull(message = "La date de début est obligatoire")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateDebut;
/** Date de fin de la période analysée */
@NotNull(message = "La date de fin est obligatoire")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateFin;
/** Points de données pour la tendance */
@NotNull(message = "Les points de données sont obligatoires")
private List<PointDonneeDTO> pointsDonnees;
/** Valeur actuelle du KPI */
@NotNull(message = "La valeur actuelle est obligatoire")
@DecimalMin(value = "0.0", message = "La valeur actuelle doit être positive ou nulle")
@Digits(integer = 15, fraction = 4, message = "Format de valeur actuelle invalide")
private BigDecimal valeurActuelle;
/** Valeur minimale sur la période */
@DecimalMin(value = "0.0", message = "La valeur minimale doit être positive ou nulle")
@Digits(integer = 15, fraction = 4, message = "Format de valeur minimale invalide")
private BigDecimal valeurMinimale;
/** Valeur maximale sur la période */
@DecimalMin(value = "0.0", message = "La valeur maximale doit être positive ou nulle")
@Digits(integer = 15, fraction = 4, message = "Format de valeur maximale invalide")
private BigDecimal valeurMaximale;
/** Valeur moyenne sur la période */
@DecimalMin(value = "0.0", message = "La valeur moyenne doit être positive ou nulle")
@Digits(integer = 15, fraction = 4, message = "Format de valeur moyenne invalide")
private BigDecimal valeurMoyenne;
/** Écart-type des valeurs */
@DecimalMin(value = "0.0", message = "L'écart-type doit être positif ou nul")
@Digits(integer = 15, fraction = 4, message = "Format d'écart-type invalide")
private BigDecimal ecartType;
/** Coefficient de variation (écart-type / moyenne) */
@DecimalMin(value = "0.0", message = "Le coefficient de variation doit être positif ou nul")
@Digits(integer = 6, fraction = 4, message = "Format de coefficient de variation invalide")
private BigDecimal coefficientVariation;
/** Tendance générale (pente de la régression linéaire) */
@Digits(integer = 10, fraction = 6, message = "Format de tendance invalide")
private BigDecimal tendanceGenerale;
/** Coefficient de corrélation R² */
@DecimalMin(value = "0.0", message = "Le coefficient de corrélation doit être positif ou nul")
@DecimalMax(value = "1.0", message = "Le coefficient de corrélation ne peut pas dépasser 1")
@Digits(integer = 1, fraction = 6, message = "Format de coefficient de corrélation invalide")
private BigDecimal coefficientCorrelation;
/** Pourcentage d'évolution depuis le début de la période */
@Digits(integer = 6, fraction = 2, message = "Format de pourcentage d'évolution invalide")
private BigDecimal pourcentageEvolutionGlobale;
/** Prédiction pour la prochaine période */
@DecimalMin(value = "0.0", message = "La prédiction doit être positive ou nulle")
@Digits(integer = 15, fraction = 4, message = "Format de prédiction invalide")
private BigDecimal predictionProchainePeriode;
/** Marge d'erreur de la prédiction (en pourcentage) */
@DecimalMin(value = "0.0", message = "La marge d'erreur doit être positive ou nulle")
@DecimalMax(value = "100.0", message = "La marge d'erreur ne peut pas dépasser 100%")
@Digits(integer = 3, fraction = 2, message = "Format de marge d'erreur invalide")
private BigDecimal margeErreurPrediction;
/** Seuil d'alerte bas */
@DecimalMin(value = "0.0", message = "Le seuil d'alerte bas doit être positif ou nul")
@Digits(integer = 15, fraction = 4, message = "Format de seuil d'alerte bas invalide")
private BigDecimal seuilAlerteBas;
/** Seuil d'alerte haut */
@DecimalMin(value = "0.0", message = "Le seuil d'alerte haut doit être positif ou nul")
@Digits(integer = 15, fraction = 4, message = "Format de seuil d'alerte haut invalide")
private BigDecimal seuilAlerteHaut;
/** Indicateur si une alerte est active */
@Builder.Default
private Boolean alerteActive = false;
/** Type d'alerte (bas, haut, anomalie) */
@Size(max = 50, message = "Le type d'alerte ne peut pas dépasser 50 caractères")
private String typeAlerte;
/** Message d'alerte */
@Size(max = 500, message = "Le message d'alerte ne peut pas dépasser 500 caractères")
private String messageAlerte;
/** Configuration du graphique (couleurs, style, etc.) */
@Size(max = 2000, message = "La configuration graphique ne peut pas dépasser 2000 caractères")
private String configurationGraphique;
/** Intervalle de regroupement des données */
@Size(max = 20, message = "L'intervalle de regroupement ne peut pas dépasser 20 caractères")
private String intervalleRegroupement;
/** Format d'affichage des dates */
@Size(max = 20, message = "Le format de date ne peut pas dépasser 20 caractères")
private String formatDate;
/** Date de dernière mise à jour */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateDerniereMiseAJour;
/** Fréquence de mise à jour en minutes */
@DecimalMin(value = "1", message = "La fréquence de mise à jour minimum est 1 minute")
private Integer frequenceMiseAJourMinutes;
// === CLASSES INTERNES ===
/**
* Classe interne représentant un point de données dans la tendance
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class PointDonneeDTO {
/** Date du point de données */
@NotNull(message = "La date du point de données est obligatoire")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime date;
/** Valeur du point de données */
@NotNull(message = "La valeur du point de données est obligatoire")
@DecimalMin(value = "0.0", message = "La valeur du point doit être positive ou nulle")
@Digits(integer = 15, fraction = 4, message = "Format de valeur du point invalide")
private BigDecimal valeur;
/** Libellé du point (optionnel) */
@Size(max = 100, message = "Le libellé du point ne peut pas dépasser 100 caractères")
private String libelle;
/** Indicateur si le point est une anomalie */
@Builder.Default
private Boolean anomalie = false;
/** Indicateur si le point est une prédiction */
@Builder.Default
private Boolean prediction = false;
/** Métadonnées additionnelles du point */
private String metadonnees;
}
// === MÉTHODES UTILITAIRES ===
/**
* Retourne le libellé de la métrique
*
* @return Le libellé de la métrique
*/
public String getLibelleMetrique() {
return typeMetrique.getLibelle();
}
/**
* Retourne l'unité de mesure
*
* @return L'unité de mesure
*/
public String getUnite() {
return typeMetrique.getUnite();
}
/**
* Retourne l'icône de la métrique
*
* @return L'icône Material Design
*/
public String getIcone() {
return typeMetrique.getIcone();
}
/**
* Retourne la couleur de la métrique
*
* @return Le code couleur hexadécimal
*/
public String getCouleur() {
return typeMetrique.getCouleur();
}
/**
* Vérifie si la tendance est positive
*
* @return true si la tendance générale est positive
*/
public boolean isTendancePositive() {
return tendanceGenerale != null && tendanceGenerale.compareTo(BigDecimal.ZERO) > 0;
}
/**
* Vérifie si la tendance est négative
*
* @return true si la tendance générale est négative
*/
public boolean isTendanceNegative() {
return tendanceGenerale != null && tendanceGenerale.compareTo(BigDecimal.ZERO) < 0;
}
/**
* Vérifie si la tendance est stable
*
* @return true si la tendance générale est stable
*/
public boolean isTendanceStable() {
return tendanceGenerale != null && tendanceGenerale.compareTo(BigDecimal.ZERO) == 0;
}
/**
* Retourne la volatilité du KPI (basée sur le coefficient de variation)
*
* @return "faible", "moyenne" ou "élevée"
*/
public String getVolatilite() {
if (coefficientVariation == null) return "inconnue";
BigDecimal cv = coefficientVariation;
if (cv.compareTo(new BigDecimal("0.1")) <= 0) return "faible";
if (cv.compareTo(new BigDecimal("0.3")) <= 0) return "moyenne";
return "élevée";
}
/**
* Vérifie si la prédiction est fiable (R² > 0.7)
*
* @return true si la prédiction est considérée comme fiable
*/
public boolean isPredictionFiable() {
return coefficientCorrelation != null &&
coefficientCorrelation.compareTo(new BigDecimal("0.7")) >= 0;
}
/**
* Retourne le nombre de points de données
*
* @return Le nombre de points de données
*/
public int getNombrePointsDonnees() {
return pointsDonnees != null ? pointsDonnees.size() : 0;
}
/**
* Vérifie si des anomalies ont été détectées
*
* @return true si au moins un point est marqué comme anomalie
*/
public boolean hasAnomalies() {
return pointsDonnees != null &&
pointsDonnees.stream().anyMatch(PointDonneeDTO::getAnomalie);
}
}

View File

@@ -0,0 +1,337 @@
package dev.lions.unionflow.server.api.dto.analytics;
import com.fasterxml.jackson.annotation.JsonFormat;
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
import dev.lions.unionflow.server.api.enums.analytics.TypeMetrique;
import dev.lions.unionflow.server.api.enums.analytics.PeriodeAnalyse;
import dev.lions.unionflow.server.api.enums.analytics.FormatExport;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.Size;
import jakarta.validation.Valid;
import lombok.Getter;
import lombok.Setter;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/**
* DTO pour la configuration des rapports analytics UnionFlow
*
* Représente la configuration d'un rapport personnalisé avec ses métriques,
* sa mise en forme et ses paramètres d'export.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ReportConfigDTO extends BaseDTO {
private static final long serialVersionUID = 1L;
/** Nom du rapport */
@NotBlank(message = "Le nom du rapport est obligatoire")
@Size(min = 3, max = 200, message = "Le nom du rapport doit contenir entre 3 et 200 caractères")
private String nom;
/** Description du rapport */
@Size(max = 1000, message = "La description ne peut pas dépasser 1000 caractères")
private String description;
/** Type de rapport (executif, analytique, technique, operationnel) */
@NotBlank(message = "Le type de rapport est obligatoire")
@Size(max = 50, message = "Le type de rapport ne peut pas dépasser 50 caractères")
private String typeRapport;
/** Période d'analyse par défaut */
@NotNull(message = "La période d'analyse est obligatoire")
private PeriodeAnalyse periodeAnalyse;
/** Date de début personnalisée (si période personnalisée) */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateDebutPersonnalisee;
/** Date de fin personnalisée (si période personnalisée) */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateFinPersonnalisee;
/** Identifiant de l'organisation (optionnel pour filtrage) */
private UUID organisationId;
/** Nom de l'organisation */
@Size(max = 200, message = "Le nom de l'organisation ne peut pas dépasser 200 caractères")
private String nomOrganisation;
/** Identifiant de l'utilisateur créateur */
@NotNull(message = "L'identifiant de l'utilisateur créateur est obligatoire")
private UUID utilisateurCreateurId;
/** Nom de l'utilisateur créateur */
@Size(max = 200, message = "Le nom de l'utilisateur créateur ne peut pas dépasser 200 caractères")
private String nomUtilisateurCreateur;
/** Métriques incluses dans le rapport */
@NotNull(message = "Les métriques sont obligatoires")
@Valid
private List<MetriqueConfigDTO> metriques;
/** Sections du rapport */
@Valid
private List<SectionRapportDTO> sections;
/** Format d'export par défaut */
@NotNull(message = "Le format d'export est obligatoire")
private FormatExport formatExport;
/** Formats d'export autorisés */
private List<FormatExport> formatsExportAutorises;
/** Modèle de rapport à utiliser */
@Size(max = 100, message = "Le modèle de rapport ne peut pas dépasser 100 caractères")
private String modeleRapport;
/** Configuration de la mise en page */
@Size(max = 2000, message = "La configuration de mise en page ne peut pas dépasser 2000 caractères")
private String configurationMiseEnPage;
/** Logo personnalisé (URL ou base64) */
@Size(max = 5000, message = "Le logo personnalisé ne peut pas dépasser 5000 caractères")
private String logoPersonnalise;
/** Couleurs personnalisées du rapport */
private Map<String, String> couleursPersonnalisees;
/** Indicateur si le rapport est public */
@Builder.Default
private Boolean rapportPublic = false;
/** Indicateur si le rapport est automatique */
@Builder.Default
private Boolean rapportAutomatique = false;
/** Fréquence de génération automatique (en heures) */
@DecimalMin(value = "1", message = "La fréquence minimum est 1 heure")
private Integer frequenceGenerationHeures;
/** Prochaine génération automatique */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime prochaineGeneration;
/** Liste des destinataires pour l'envoi automatique */
private List<String> destinatairesEmail;
/** Objet de l'email pour l'envoi automatique */
@Size(max = 200, message = "L'objet de l'email ne peut pas dépasser 200 caractères")
private String objetEmail;
/** Corps de l'email pour l'envoi automatique */
@Size(max = 2000, message = "Le corps de l'email ne peut pas dépasser 2000 caractères")
private String corpsEmail;
/** Paramètres de filtrage avancé */
private Map<String, Object> parametresFiltrage;
/** Tags pour catégoriser le rapport */
private List<String> tags;
/** Niveau de confidentialité (1=public, 5=confidentiel) */
@DecimalMin(value = "1", message = "Le niveau de confidentialité minimum est 1")
@DecimalMax(value = "5", message = "Le niveau de confidentialité maximum est 5")
@Builder.Default
private Integer niveauConfidentialite = 1;
/** Date de dernière génération */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateDerniereGeneration;
/** Nombre de générations effectuées */
@DecimalMin(value = "0", message = "Le nombre de générations doit être positif")
@Builder.Default
private Integer nombreGenerations = 0;
/** Taille moyenne des rapports générés (en KB) */
@DecimalMin(value = "0", message = "La taille moyenne doit être positive")
private Long tailleMoyenneKB;
/** Temps moyen de génération (en secondes) */
@DecimalMin(value = "0", message = "Le temps moyen de génération doit être positif")
private Integer tempsMoyenGenerationSecondes;
// === CLASSES INTERNES ===
/**
* Configuration d'une métrique dans le rapport
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class MetriqueConfigDTO {
/** Type de métrique */
@NotNull(message = "Le type de métrique est obligatoire")
private TypeMetrique typeMetrique;
/** Libellé personnalisé */
@Size(max = 200, message = "Le libellé personnalisé ne peut pas dépasser 200 caractères")
private String libellePersonnalise;
/** Position dans le rapport (ordre d'affichage) */
@DecimalMin(value = "1", message = "La position minimum est 1")
private Integer position;
/** Taille d'affichage (1=petit, 2=moyen, 3=grand) */
@DecimalMin(value = "1", message = "La taille minimum est 1")
@DecimalMax(value = "3", message = "La taille maximum est 3")
@Builder.Default
private Integer tailleAffichage = 2;
/** Couleur personnalisée */
@Size(max = 7, message = "La couleur doit être au format #RRGGBB")
private String couleurPersonnalisee;
/** Indicateur si la métrique inclut un graphique */
@Builder.Default
private Boolean inclureGraphique = true;
/** Type de graphique (line, bar, pie, area) */
@Size(max = 20, message = "Le type de graphique ne peut pas dépasser 20 caractères")
@Builder.Default
private String typeGraphique = "line";
/** Indicateur si la métrique inclut la tendance */
@Builder.Default
private Boolean inclureTendance = true;
/** Indicateur si la métrique inclut la comparaison */
@Builder.Default
private Boolean inclureComparaison = true;
/** Seuils d'alerte personnalisés */
private Map<String, Object> seuilsAlerte;
/** Filtres spécifiques à cette métrique */
private Map<String, Object> filtresSpecifiques;
}
/**
* Configuration d'une section du rapport
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class SectionRapportDTO {
/** Nom de la section */
@NotBlank(message = "Le nom de la section est obligatoire")
@Size(max = 200, message = "Le nom de la section ne peut pas dépasser 200 caractères")
private String nom;
/** Description de la section */
@Size(max = 500, message = "La description de la section ne peut pas dépasser 500 caractères")
private String description;
/** Position de la section dans le rapport */
@DecimalMin(value = "1", message = "La position minimum est 1")
private Integer position;
/** Type de section (resume, metriques, graphiques, tableaux, analyse) */
@NotBlank(message = "Le type de section est obligatoire")
@Size(max = 50, message = "Le type de section ne peut pas dépasser 50 caractères")
private String typeSection;
/** Métriques incluses dans cette section */
private List<TypeMetrique> metriquesIncluses;
/** Configuration spécifique de la section */
private Map<String, Object> configurationSection;
/** Indicateur si la section est visible */
@Builder.Default
private Boolean visible = true;
/** Indicateur si la section peut être réduite */
@Builder.Default
private Boolean pliable = false;
}
// === MÉTHODES UTILITAIRES ===
/**
* Retourne le nombre de métriques configurées
*
* @return Le nombre de métriques
*/
public int getNombreMetriques() {
return metriques != null ? metriques.size() : 0;
}
/**
* Retourne le nombre de sections configurées
*
* @return Le nombre de sections
*/
public int getNombreSections() {
return sections != null ? sections.size() : 0;
}
/**
* Vérifie si le rapport utilise une période personnalisée
*
* @return true si la période est personnalisée
*/
public boolean isPeriodePersonnalisee() {
return periodeAnalyse == PeriodeAnalyse.PERIODE_PERSONNALISEE;
}
/**
* Vérifie si le rapport est confidentiel (niveau >= 4)
*
* @return true si le rapport est confidentiel
*/
public boolean isConfidentiel() {
return niveauConfidentialite != null && niveauConfidentialite >= 4;
}
/**
* Vérifie si le rapport nécessite une génération
*
* @return true si la prochaine génération est due
*/
public boolean necessiteGeneration() {
return rapportAutomatique && prochaineGeneration != null &&
prochaineGeneration.isBefore(LocalDateTime.now());
}
/**
* Retourne la fréquence de génération en texte
*
* @return La fréquence sous forme de texte
*/
public String getFrequenceTexte() {
if (frequenceGenerationHeures == null) return "Manuelle";
return switch (frequenceGenerationHeures) {
case 1 -> "Toutes les heures";
case 24 -> "Quotidienne";
case 168 -> "Hebdomadaire"; // 24 * 7
case 720 -> "Mensuelle"; // 24 * 30
default -> "Toutes les " + frequenceGenerationHeures + " heures";
};
}
}

View File

@@ -0,0 +1,426 @@
package dev.lions.unionflow.server.api.dto.notification;
import jakarta.validation.constraints.*;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.util.Map;
/**
* DTO pour les actions rapides des notifications UnionFlow
*
* Ce DTO représente une action que l'utilisateur peut exécuter directement
* depuis la notification sans ouvrir l'application.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ActionNotificationDTO {
/**
* Identifiant unique de l'action
*/
@NotBlank(message = "L'identifiant de l'action est obligatoire")
private String id;
/**
* Libellé affiché sur le bouton d'action
*/
@NotBlank(message = "Le libellé de l'action est obligatoire")
@Size(max = 30, message = "Le libellé ne peut pas dépasser 30 caractères")
private String libelle;
/**
* Description de l'action (tooltip)
*/
@Size(max = 100, message = "La description ne peut pas dépasser 100 caractères")
private String description;
/**
* Type d'action à exécuter
*/
@NotBlank(message = "Le type d'action est obligatoire")
private String typeAction;
/**
* Icône de l'action (Material Design)
*/
private String icone;
/**
* Couleur de l'action (hexadécimal)
*/
private String couleur;
/**
* URL à ouvrir (pour les actions de type "url")
*/
private String url;
/**
* Route de l'application à ouvrir (pour les actions de type "route")
*/
private String route;
/**
* Paramètres de l'action
*/
private Map<String, String> parametres;
/**
* Indique si l'action ferme la notification
*/
private Boolean fermeNotification;
/**
* Indique si l'action nécessite une confirmation
*/
private Boolean necessiteConfirmation;
/**
* Message de confirmation à afficher
*/
private String messageConfirmation;
/**
* Indique si l'action est destructive (suppression, etc.)
*/
private Boolean estDestructive;
/**
* Ordre d'affichage de l'action
*/
private Integer ordre;
/**
* Indique si l'action est activée
*/
private Boolean estActivee;
/**
* Condition d'affichage de l'action (expression)
*/
private String conditionAffichage;
/**
* Rôles autorisés à exécuter cette action
*/
private String[] rolesAutorises;
/**
* Permissions requises pour exécuter cette action
*/
private String[] permissionsRequises;
/**
* Délai d'expiration de l'action en minutes
*/
private Integer delaiExpirationMinutes;
/**
* Nombre maximum d'exécutions autorisées
*/
private Integer maxExecutions;
/**
* Nombre d'exécutions actuelles
*/
private Integer nombreExecutions;
/**
* Indique si l'action peut être exécutée plusieurs fois
*/
private Boolean peutEtreRepetee;
/**
* Style du bouton (primary, secondary, outline, text)
*/
private String styleBouton;
/**
* Taille du bouton (small, medium, large)
*/
private String tailleBouton;
/**
* Position du bouton (left, center, right)
*/
private String positionBouton;
/**
* Données personnalisées de l'action
*/
private Map<String, Object> donneesPersonnalisees;
// === CONSTRUCTEURS ===
/**
* Constructeur par défaut
*/
public ActionNotificationDTO() {
this.fermeNotification = true;
this.necessiteConfirmation = false;
this.estDestructive = false;
this.ordre = 0;
this.estActivee = true;
this.maxExecutions = 1;
this.nombreExecutions = 0;
this.peutEtreRepetee = false;
this.styleBouton = "primary";
this.tailleBouton = "medium";
this.positionBouton = "right";
}
/**
* Constructeur avec paramètres essentiels
*/
public ActionNotificationDTO(String id, String libelle, String typeAction) {
this();
this.id = id;
this.libelle = libelle;
this.typeAction = typeAction;
}
/**
* Constructeur pour action URL
*/
public ActionNotificationDTO(String id, String libelle, String url, String icone) {
this(id, libelle, "url");
this.url = url;
this.icone = icone;
}
/**
* Constructeur pour action de route
*/
public ActionNotificationDTO(String id, String libelle, String route, String icone, Map<String, String> parametres) {
this(id, libelle, "route");
this.route = route;
this.icone = icone;
this.parametres = parametres;
}
// === GETTERS ET SETTERS ===
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getLibelle() { return libelle; }
public void setLibelle(String libelle) { this.libelle = libelle; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public String getTypeAction() { return typeAction; }
public void setTypeAction(String typeAction) { this.typeAction = typeAction; }
public String getIcone() { return icone; }
public void setIcone(String icone) { this.icone = icone; }
public String getCouleur() { return couleur; }
public void setCouleur(String couleur) { this.couleur = couleur; }
public String getUrl() { return url; }
public void setUrl(String url) { this.url = url; }
public String getRoute() { return route; }
public void setRoute(String route) { this.route = route; }
public Map<String, String> getParametres() { return parametres; }
public void setParametres(Map<String, String> parametres) { this.parametres = parametres; }
public Boolean getFermeNotification() { return fermeNotification; }
public void setFermeNotification(Boolean fermeNotification) { this.fermeNotification = fermeNotification; }
public Boolean getNecessiteConfirmation() { return necessiteConfirmation; }
public void setNecessiteConfirmation(Boolean necessiteConfirmation) { this.necessiteConfirmation = necessiteConfirmation; }
public String getMessageConfirmation() { return messageConfirmation; }
public void setMessageConfirmation(String messageConfirmation) { this.messageConfirmation = messageConfirmation; }
public Boolean getEstDestructive() { return estDestructive; }
public void setEstDestructive(Boolean estDestructive) { this.estDestructive = estDestructive; }
public Integer getOrdre() { return ordre; }
public void setOrdre(Integer ordre) { this.ordre = ordre; }
public Boolean getEstActivee() { return estActivee; }
public void setEstActivee(Boolean estActivee) { this.estActivee = estActivee; }
public String getConditionAffichage() { return conditionAffichage; }
public void setConditionAffichage(String conditionAffichage) { this.conditionAffichage = conditionAffichage; }
public String[] getRolesAutorises() { return rolesAutorises; }
public void setRolesAutorises(String[] rolesAutorises) { this.rolesAutorises = rolesAutorises; }
public String[] getPermissionsRequises() { return permissionsRequises; }
public void setPermissionsRequises(String[] permissionsRequises) { this.permissionsRequises = permissionsRequises; }
public Integer getDelaiExpirationMinutes() { return delaiExpirationMinutes; }
public void setDelaiExpirationMinutes(Integer delaiExpirationMinutes) { this.delaiExpirationMinutes = delaiExpirationMinutes; }
public Integer getMaxExecutions() { return maxExecutions; }
public void setMaxExecutions(Integer maxExecutions) { this.maxExecutions = maxExecutions; }
public Integer getNombreExecutions() { return nombreExecutions; }
public void setNombreExecutions(Integer nombreExecutions) { this.nombreExecutions = nombreExecutions; }
public Boolean getPeutEtreRepetee() { return peutEtreRepetee; }
public void setPeutEtreRepetee(Boolean peutEtreRepetee) { this.peutEtreRepetee = peutEtreRepetee; }
public String getStyleBouton() { return styleBouton; }
public void setStyleBouton(String styleBouton) { this.styleBouton = styleBouton; }
public String getTailleBouton() { return tailleBouton; }
public void setTailleBouton(String tailleBouton) { this.tailleBouton = tailleBouton; }
public String getPositionBouton() { return positionBouton; }
public void setPositionBouton(String positionBouton) { this.positionBouton = positionBouton; }
public Map<String, Object> getDonneesPersonnalisees() { return donneesPersonnalisees; }
public void setDonneesPersonnalisees(Map<String, Object> donneesPersonnalisees) {
this.donneesPersonnalisees = donneesPersonnalisees;
}
// === MÉTHODES UTILITAIRES ===
/**
* Vérifie si l'action peut être exécutée
*/
public boolean peutEtreExecutee() {
return estActivee && (nombreExecutions < maxExecutions || peutEtreRepetee);
}
/**
* Vérifie si l'action est expirée
*/
public boolean isExpiree() {
// Implémentation basée sur delaiExpirationMinutes et date de création de la notification
return false; // À implémenter selon la logique métier
}
/**
* Incrémente le nombre d'exécutions
*/
public void incrementerExecutions() {
if (nombreExecutions == null) {
nombreExecutions = 0;
}
nombreExecutions++;
}
/**
* Vérifie si l'utilisateur a les permissions requises
*/
public boolean utilisateurAutorise(String[] rolesUtilisateur, String[] permissionsUtilisateur) {
// Vérification des rôles
if (rolesAutorises != null && rolesAutorises.length > 0) {
boolean roleAutorise = false;
for (String roleRequis : rolesAutorises) {
for (String roleUtilisateur : rolesUtilisateur) {
if (roleRequis.equals(roleUtilisateur)) {
roleAutorise = true;
break;
}
}
if (roleAutorise) break;
}
if (!roleAutorise) return false;
}
// Vérification des permissions
if (permissionsRequises != null && permissionsRequises.length > 0) {
boolean permissionAutorisee = false;
for (String permissionRequise : permissionsRequises) {
for (String permissionUtilisateur : permissionsUtilisateur) {
if (permissionRequise.equals(permissionUtilisateur)) {
permissionAutorisee = true;
break;
}
}
if (permissionAutorisee) break;
}
if (!permissionAutorisee) return false;
}
return true;
}
/**
* Retourne la couleur par défaut selon le type d'action
*/
public String getCouleurParDefaut() {
if (couleur != null) return couleur;
return switch (typeAction) {
case "confirm" -> "#4CAF50"; // Vert pour confirmation
case "cancel" -> "#F44336"; // Rouge pour annulation
case "info" -> "#2196F3"; // Bleu pour information
case "warning" -> "#FF9800"; // Orange pour avertissement
case "url", "route" -> "#2196F3"; // Bleu pour navigation
default -> "#9E9E9E"; // Gris par défaut
};
}
/**
* Retourne l'icône par défaut selon le type d'action
*/
public String getIconeParDefaut() {
if (icone != null) return icone;
return switch (typeAction) {
case "confirm" -> "check";
case "cancel" -> "close";
case "info" -> "info";
case "warning" -> "warning";
case "url" -> "open_in_new";
case "route" -> "arrow_forward";
case "call" -> "phone";
case "message" -> "message";
case "email" -> "email";
case "share" -> "share";
default -> "touch_app";
};
}
/**
* Crée une action de confirmation
*/
public static ActionNotificationDTO creerActionConfirmation(String id, String libelle) {
ActionNotificationDTO action = new ActionNotificationDTO(id, libelle, "confirm");
action.setCouleur("#4CAF50");
action.setIcone("check");
action.setStyleBouton("primary");
return action;
}
/**
* Crée une action d'annulation
*/
public static ActionNotificationDTO creerActionAnnulation(String id, String libelle) {
ActionNotificationDTO action = new ActionNotificationDTO(id, libelle, "cancel");
action.setCouleur("#F44336");
action.setIcone("close");
action.setStyleBouton("outline");
action.setEstDestructive(true);
return action;
}
/**
* Crée une action de navigation
*/
public static ActionNotificationDTO creerActionNavigation(String id, String libelle, String route) {
ActionNotificationDTO action = new ActionNotificationDTO(id, libelle, "route");
action.setRoute(route);
action.setCouleur("#2196F3");
action.setIcone("arrow_forward");
return action;
}
@Override
public String toString() {
return String.format("ActionNotificationDTO{id='%s', libelle='%s', type='%s'}",
id, libelle, typeAction);
}
}

View File

@@ -0,0 +1,523 @@
package dev.lions.unionflow.server.api.dto.notification;
import dev.lions.unionflow.server.api.enums.notification.TypeNotification;
import dev.lions.unionflow.server.api.enums.notification.StatutNotification;
import dev.lions.unionflow.server.api.enums.notification.CanalNotification;
import jakarta.validation.constraints.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.List;
/**
* DTO pour les notifications UnionFlow
*
* Ce DTO représente une notification complète avec toutes ses propriétés,
* métadonnées et informations de suivi.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public class NotificationDTO {
/**
* Identifiant unique de la notification
*/
private String id;
/**
* Type de notification
*/
@NotNull(message = "Le type de notification est obligatoire")
private TypeNotification typeNotification;
/**
* Statut actuel de la notification
*/
@NotNull(message = "Le statut de notification est obligatoire")
private StatutNotification statut;
/**
* Canal de notification utilisé
*/
@NotNull(message = "Le canal de notification est obligatoire")
private CanalNotification canal;
/**
* Titre de la notification
*/
@NotBlank(message = "Le titre ne peut pas être vide")
@Size(max = 100, message = "Le titre ne peut pas dépasser 100 caractères")
private String titre;
/**
* Corps du message de la notification
*/
@NotBlank(message = "Le message ne peut pas être vide")
@Size(max = 500, message = "Le message ne peut pas dépasser 500 caractères")
private String message;
/**
* Message court pour l'affichage dans la barre de notification
*/
@Size(max = 150, message = "Le message court ne peut pas dépasser 150 caractères")
private String messageCourt;
/**
* Identifiant de l'expéditeur
*/
private String expediteurId;
/**
* Nom de l'expéditeur
*/
private String expediteurNom;
/**
* Liste des identifiants des destinataires
*/
@NotEmpty(message = "Au moins un destinataire est requis")
private List<String> destinatairesIds;
/**
* Identifiant de l'organisation concernée
*/
private String organisationId;
/**
* Données personnalisées de la notification
*/
private Map<String, Object> donneesPersonnalisees;
/**
* URL de l'image à afficher (optionnel)
*/
private String imageUrl;
/**
* URL de l'icône personnalisée (optionnel)
*/
private String iconeUrl;
/**
* Action à exécuter lors du clic
*/
private String actionClic;
/**
* Paramètres de l'action
*/
private Map<String, String> parametresAction;
/**
* Boutons d'action rapide
*/
private List<ActionNotificationDTO> actionsRapides;
/**
* Date et heure de création
*/
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime dateCreation;
/**
* Date et heure d'envoi programmé
*/
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime dateEnvoiProgramme;
/**
* Date et heure d'envoi effectif
*/
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime dateEnvoi;
/**
* Date et heure d'expiration
*/
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime dateExpiration;
/**
* Date et heure de dernière lecture
*/
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime dateDerniereLecture;
/**
* Priorité de la notification (1=basse, 5=haute)
*/
@Min(value = 1, message = "La priorité doit être comprise entre 1 et 5")
@Max(value = 5, message = "La priorité doit être comprise entre 1 et 5")
private Integer priorite;
/**
* Nombre de tentatives d'envoi
*/
private Integer nombreTentatives;
/**
* Nombre maximum de tentatives autorisées
*/
private Integer maxTentatives;
/**
* Délai entre les tentatives en minutes
*/
private Integer delaiTentativesMinutes;
/**
* Indique si la notification doit vibrer
*/
private Boolean doitVibrer;
/**
* Indique si la notification doit émettre un son
*/
private Boolean doitEmettreSon;
/**
* Indique si la notification doit allumer la LED
*/
private Boolean doitAllumerLED;
/**
* Pattern de vibration personnalisé
*/
private long[] patternVibration;
/**
* Son personnalisé à jouer
*/
private String sonPersonnalise;
/**
* Couleur de la LED
*/
private String couleurLED;
/**
* Indique si la notification est lue
*/
private Boolean estLue;
/**
* Indique si la notification est marquée comme importante
*/
private Boolean estImportante;
/**
* Indique si la notification est archivée
*/
private Boolean estArchivee;
/**
* Nombre de fois que la notification a été affichée
*/
private Integer nombreAffichages;
/**
* Nombre de clics sur la notification
*/
private Integer nombreClics;
/**
* Taux de livraison (pourcentage)
*/
private Double tauxLivraison;
/**
* Taux d'ouverture (pourcentage)
*/
private Double tauxOuverture;
/**
* Temps moyen de lecture en secondes
*/
private Integer tempsMoyenLectureSecondes;
/**
* Message d'erreur en cas d'échec
*/
private String messageErreur;
/**
* Code d'erreur technique
*/
private String codeErreur;
/**
* Trace de la pile d'erreur (pour debug)
*/
private String traceErreur;
/**
* Métadonnées techniques
*/
private Map<String, Object> metadonnees;
/**
* Tags pour catégorisation
*/
private List<String> tags;
/**
* Identifiant de la campagne (si applicable)
*/
private String campagneId;
/**
* Version de l'application qui a créé la notification
*/
private String versionApp;
/**
* Plateforme cible (android, ios, web)
*/
private String plateforme;
/**
* Token FCM du destinataire (usage interne)
*/
private String tokenFCM;
/**
* Identifiant de suivi externe
*/
private String idSuiviExterne;
// === CONSTRUCTEURS ===
/**
* Constructeur par défaut
*/
public NotificationDTO() {
this.dateCreation = LocalDateTime.now();
this.statut = StatutNotification.BROUILLON;
this.nombreTentatives = 0;
this.maxTentatives = 3;
this.delaiTentativesMinutes = 5;
this.estLue = false;
this.estImportante = false;
this.estArchivee = false;
this.nombreAffichages = 0;
this.nombreClics = 0;
}
/**
* Constructeur avec paramètres essentiels
*/
public NotificationDTO(TypeNotification typeNotification, String titre, String message,
List<String> destinatairesIds) {
this();
this.typeNotification = typeNotification;
this.titre = titre;
this.message = message;
this.destinatairesIds = destinatairesIds;
this.canal = CanalNotification.valueOf(typeNotification.getCanalNotification());
this.priorite = typeNotification.getNiveauPriorite();
this.doitVibrer = typeNotification.doitVibrer();
this.doitEmettreSon = typeNotification.doitEmettreSon();
}
// === GETTERS ET SETTERS ===
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public TypeNotification getTypeNotification() { return typeNotification; }
public void setTypeNotification(TypeNotification typeNotification) { this.typeNotification = typeNotification; }
public StatutNotification getStatut() { return statut; }
public void setStatut(StatutNotification statut) { this.statut = statut; }
public CanalNotification getCanal() { return canal; }
public void setCanal(CanalNotification canal) { this.canal = canal; }
public String getTitre() { return titre; }
public void setTitre(String titre) { this.titre = titre; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public String getMessageCourt() { return messageCourt; }
public void setMessageCourt(String messageCourt) { this.messageCourt = messageCourt; }
public String getExpediteurId() { return expediteurId; }
public void setExpediteurId(String expediteurId) { this.expediteurId = expediteurId; }
public String getExpediteurNom() { return expediteurNom; }
public void setExpediteurNom(String expediteurNom) { this.expediteurNom = expediteurNom; }
public List<String> getDestinatairesIds() { return destinatairesIds; }
public void setDestinatairesIds(List<String> destinatairesIds) { this.destinatairesIds = destinatairesIds; }
public String getOrganisationId() { return organisationId; }
public void setOrganisationId(String organisationId) { this.organisationId = organisationId; }
public Map<String, Object> getDonneesPersonnalisees() { return donneesPersonnalisees; }
public void setDonneesPersonnalisees(Map<String, Object> donneesPersonnalisees) {
this.donneesPersonnalisees = donneesPersonnalisees;
}
public String getImageUrl() { return imageUrl; }
public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; }
public String getIconeUrl() { return iconeUrl; }
public void setIconeUrl(String iconeUrl) { this.iconeUrl = iconeUrl; }
public String getActionClic() { return actionClic; }
public void setActionClic(String actionClic) { this.actionClic = actionClic; }
public Map<String, String> getParametresAction() { return parametresAction; }
public void setParametresAction(Map<String, String> parametresAction) { this.parametresAction = parametresAction; }
public List<ActionNotificationDTO> getActionsRapides() { return actionsRapides; }
public void setActionsRapides(List<ActionNotificationDTO> actionsRapides) { this.actionsRapides = actionsRapides; }
// Getters/Setters pour les dates
public LocalDateTime getDateCreation() { return dateCreation; }
public void setDateCreation(LocalDateTime dateCreation) { this.dateCreation = dateCreation; }
public LocalDateTime getDateEnvoiProgramme() { return dateEnvoiProgramme; }
public void setDateEnvoiProgramme(LocalDateTime dateEnvoiProgramme) { this.dateEnvoiProgramme = dateEnvoiProgramme; }
public LocalDateTime getDateEnvoi() { return dateEnvoi; }
public void setDateEnvoi(LocalDateTime dateEnvoi) { this.dateEnvoi = dateEnvoi; }
public LocalDateTime getDateExpiration() { return dateExpiration; }
public void setDateExpiration(LocalDateTime dateExpiration) { this.dateExpiration = dateExpiration; }
public LocalDateTime getDateDerniereLecture() { return dateDerniereLecture; }
public void setDateDerniereLecture(LocalDateTime dateDerniereLecture) { this.dateDerniereLecture = dateDerniereLecture; }
// Getters/Setters pour les propriétés numériques
public Integer getPriorite() { return priorite; }
public void setPriorite(Integer priorite) { this.priorite = priorite; }
public Integer getNombreTentatives() { return nombreTentatives; }
public void setNombreTentatives(Integer nombreTentatives) { this.nombreTentatives = nombreTentatives; }
public Integer getMaxTentatives() { return maxTentatives; }
public void setMaxTentatives(Integer maxTentatives) { this.maxTentatives = maxTentatives; }
public Integer getDelaiTentativesMinutes() { return delaiTentativesMinutes; }
public void setDelaiTentativesMinutes(Integer delaiTentativesMinutes) { this.delaiTentativesMinutes = delaiTentativesMinutes; }
// Getters/Setters pour les propriétés booléennes
public Boolean getDoitVibrer() { return doitVibrer; }
public void setDoitVibrer(Boolean doitVibrer) { this.doitVibrer = doitVibrer; }
public Boolean getDoitEmettreSon() { return doitEmettreSon; }
public void setDoitEmettreSon(Boolean doitEmettreSon) { this.doitEmettreSon = doitEmettreSon; }
public Boolean getDoitAllumerLED() { return doitAllumerLED; }
public void setDoitAllumerLED(Boolean doitAllumerLED) { this.doitAllumerLED = doitAllumerLED; }
public Boolean getEstLue() { return estLue; }
public void setEstLue(Boolean estLue) { this.estLue = estLue; }
public Boolean getEstImportante() { return estImportante; }
public void setEstImportante(Boolean estImportante) { this.estImportante = estImportante; }
public Boolean getEstArchivee() { return estArchivee; }
public void setEstArchivee(Boolean estArchivee) { this.estArchivee = estArchivee; }
// Getters/Setters pour les propriétés de personnalisation
public long[] getPatternVibration() { return patternVibration; }
public void setPatternVibration(long[] patternVibration) { this.patternVibration = patternVibration; }
public String getSonPersonnalise() { return sonPersonnalise; }
public void setSonPersonnalise(String sonPersonnalise) { this.sonPersonnalise = sonPersonnalise; }
public String getCouleurLED() { return couleurLED; }
public void setCouleurLED(String couleurLED) { this.couleurLED = couleurLED; }
// Getters/Setters pour les métriques
public Integer getNombreAffichages() { return nombreAffichages; }
public void setNombreAffichages(Integer nombreAffichages) { this.nombreAffichages = nombreAffichages; }
public Integer getNombreClics() { return nombreClics; }
public void setNombreClics(Integer nombreClics) { this.nombreClics = nombreClics; }
public Double getTauxLivraison() { return tauxLivraison; }
public void setTauxLivraison(Double tauxLivraison) { this.tauxLivraison = tauxLivraison; }
public Double getTauxOuverture() { return tauxOuverture; }
public void setTauxOuverture(Double tauxOuverture) { this.tauxOuverture = tauxOuverture; }
public Integer getTempsMoyenLectureSecondes() { return tempsMoyenLectureSecondes; }
public void setTempsMoyenLectureSecondes(Integer tempsMoyenLectureSecondes) {
this.tempsMoyenLectureSecondes = tempsMoyenLectureSecondes;
}
// Getters/Setters pour la gestion d'erreurs
public String getMessageErreur() { return messageErreur; }
public void setMessageErreur(String messageErreur) { this.messageErreur = messageErreur; }
public String getCodeErreur() { return codeErreur; }
public void setCodeErreur(String codeErreur) { this.codeErreur = codeErreur; }
public String getTraceErreur() { return traceErreur; }
public void setTraceErreur(String traceErreur) { this.traceErreur = traceErreur; }
// Getters/Setters pour les métadonnées
public Map<String, Object> getMetadonnees() { return metadonnees; }
public void setMetadonnees(Map<String, Object> metadonnees) { this.metadonnees = metadonnees; }
public List<String> getTags() { return tags; }
public void setTags(List<String> tags) { this.tags = tags; }
public String getCampagneId() { return campagneId; }
public void setCampagneId(String campagneId) { this.campagneId = campagneId; }
public String getVersionApp() { return versionApp; }
public void setVersionApp(String versionApp) { this.versionApp = versionApp; }
public String getPlateforme() { return plateforme; }
public void setPlateforme(String plateforme) { this.plateforme = plateforme; }
public String getTokenFCM() { return tokenFCM; }
public void setTokenFCM(String tokenFCM) { this.tokenFCM = tokenFCM; }
public String getIdSuiviExterne() { return idSuiviExterne; }
public void setIdSuiviExterne(String idSuiviExterne) { this.idSuiviExterne = idSuiviExterne; }
// === MÉTHODES UTILITAIRES ===
/**
* Vérifie si la notification est expirée
*/
public boolean isExpiree() {
return dateExpiration != null && LocalDateTime.now().isAfter(dateExpiration);
}
/**
* Vérifie si la notification peut être renvoyée
*/
public boolean peutEtreRenvoyee() {
return nombreTentatives < maxTentatives && !statut.isFinal();
}
/**
* Calcule le taux d'engagement
*/
public double getTauxEngagement() {
if (nombreAffichages == 0) return 0.0;
return (double) nombreClics / nombreAffichages * 100;
}
/**
* Retourne une représentation courte de la notification
*/
@Override
public String toString() {
return String.format("NotificationDTO{id='%s', type=%s, statut=%s, titre='%s'}",
id, typeNotification, statut, titre);
}
}

View File

@@ -0,0 +1,92 @@
package dev.lions.unionflow.server.api.dto.notification;
import com.fasterxml.jackson.annotation.JsonInclude;
import jakarta.validation.constraints.*;
/**
* DTO pour les préférences spécifiques à un canal de notification
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public class PreferenceCanalNotificationDTO {
/**
* Indique si ce canal est activé
*/
private Boolean active;
/**
* Niveau d'importance personnalisé (1-5)
*/
@Min(value = 1, message = "L'importance doit être comprise entre 1 et 5")
@Max(value = 5, message = "L'importance doit être comprise entre 1 et 5")
private Integer importance;
/**
* Son personnalisé pour ce canal
*/
private String sonPersonnalise;
/**
* Pattern de vibration personnalisé
*/
private long[] patternVibration;
/**
* Couleur LED personnalisée
*/
private String couleurLED;
/**
* Indique si le son est activé pour ce canal
*/
private Boolean sonActive;
/**
* Indique si la vibration est activée pour ce canal
*/
private Boolean vibrationActive;
/**
* Indique si la LED est activée pour ce canal
*/
private Boolean ledActive;
/**
* Indique si ce canal peut être désactivé par l'utilisateur
*/
private Boolean peutEtreDesactive;
// Constructeurs, getters et setters
public PreferenceCanalNotificationDTO() {}
public Boolean getActive() { return active; }
public void setActive(Boolean active) { this.active = active; }
public Integer getImportance() { return importance; }
public void setImportance(Integer importance) { this.importance = importance; }
public String getSonPersonnalise() { return sonPersonnalise; }
public void setSonPersonnalise(String sonPersonnalise) { this.sonPersonnalise = sonPersonnalise; }
public long[] getPatternVibration() { return patternVibration; }
public void setPatternVibration(long[] patternVibration) { this.patternVibration = patternVibration; }
public String getCouleurLED() { return couleurLED; }
public void setCouleurLED(String couleurLED) { this.couleurLED = couleurLED; }
public Boolean getSonActive() { return sonActive; }
public void setSonActive(Boolean sonActive) { this.sonActive = sonActive; }
public Boolean getVibrationActive() { return vibrationActive; }
public void setVibrationActive(Boolean vibrationActive) { this.vibrationActive = vibrationActive; }
public Boolean getLedActive() { return ledActive; }
public void setLedActive(Boolean ledActive) { this.ledActive = ledActive; }
public Boolean getPeutEtreDesactive() { return peutEtreDesactive; }
public void setPeutEtreDesactive(Boolean peutEtreDesactive) { this.peutEtreDesactive = peutEtreDesactive; }
}

View File

@@ -0,0 +1,102 @@
package dev.lions.unionflow.server.api.dto.notification;
import com.fasterxml.jackson.annotation.JsonInclude;
import jakarta.validation.constraints.*;
/**
* DTO pour les préférences spécifiques à un type de notification
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public class PreferenceTypeNotificationDTO {
/**
* Indique si ce type de notification est activé
*/
private Boolean active;
/**
* Priorité personnalisée (1-5)
*/
@Min(value = 1, message = "La priorité doit être comprise entre 1 et 5")
@Max(value = 5, message = "La priorité doit être comprise entre 1 et 5")
private Integer priorite;
/**
* Son personnalisé pour ce type
*/
private String sonPersonnalise;
/**
* Pattern de vibration personnalisé
*/
private long[] patternVibration;
/**
* Couleur LED personnalisée
*/
private String couleurLED;
/**
* Durée d'affichage personnalisée (secondes)
*/
@Min(value = 1, message = "La durée d'affichage doit être au moins 1 seconde")
@Max(value = 300, message = "La durée d'affichage ne peut pas dépasser 5 minutes")
private Integer dureeAffichageSecondes;
/**
* Indique si les notifications de ce type doivent vibrer
*/
private Boolean doitVibrer;
/**
* Indique si les notifications de ce type doivent émettre un son
*/
private Boolean doitEmettreSon;
/**
* Indique si les notifications de ce type doivent allumer la LED
*/
private Boolean doitAllumerLED;
/**
* Indique si ce type ignore le mode silencieux
*/
private Boolean ignoreModesilencieux;
// Constructeurs, getters et setters
public PreferenceTypeNotificationDTO() {}
public Boolean getActive() { return active; }
public void setActive(Boolean active) { this.active = active; }
public Integer getPriorite() { return priorite; }
public void setPriorite(Integer priorite) { this.priorite = priorite; }
public String getSonPersonnalise() { return sonPersonnalise; }
public void setSonPersonnalise(String sonPersonnalise) { this.sonPersonnalise = sonPersonnalise; }
public long[] getPatternVibration() { return patternVibration; }
public void setPatternVibration(long[] patternVibration) { this.patternVibration = patternVibration; }
public String getCouleurLED() { return couleurLED; }
public void setCouleurLED(String couleurLED) { this.couleurLED = couleurLED; }
public Integer getDureeAffichageSecondes() { return dureeAffichageSecondes; }
public void setDureeAffichageSecondes(Integer dureeAffichageSecondes) { this.dureeAffichageSecondes = dureeAffichageSecondes; }
public Boolean getDoitVibrer() { return doitVibrer; }
public void setDoitVibrer(Boolean doitVibrer) { this.doitVibrer = doitVibrer; }
public Boolean getDoitEmettreSon() { return doitEmettreSon; }
public void setDoitEmettreSon(Boolean doitEmettreSon) { this.doitEmettreSon = doitEmettreSon; }
public Boolean getDoitAllumerLED() { return doitAllumerLED; }
public void setDoitAllumerLED(Boolean doitAllumerLED) { this.doitAllumerLED = doitAllumerLED; }
public Boolean getIgnoreModeSilencieux() { return ignoreModesilencieux; }
public void setIgnoreModeSilencieux(Boolean ignoreModesilencieux) { this.ignoreModesilencieux = ignoreModesilencieux; }
}

View File

@@ -0,0 +1,523 @@
package dev.lions.unionflow.server.api.dto.notification;
import dev.lions.unionflow.server.api.enums.notification.TypeNotification;
import dev.lions.unionflow.server.api.enums.notification.CanalNotification;
import jakarta.validation.constraints.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.time.LocalTime;
import java.util.Map;
import java.util.Set;
/**
* DTO pour les préférences de notification d'un utilisateur
*
* Ce DTO représente les préférences personnalisées d'un utilisateur
* concernant la réception et l'affichage des notifications.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public class PreferencesNotificationDTO {
/**
* Identifiant unique des préférences
*/
private String id;
/**
* Identifiant de l'utilisateur
*/
@NotBlank(message = "L'identifiant utilisateur est obligatoire")
private String utilisateurId;
/**
* Identifiant de l'organisation
*/
private String organisationId;
/**
* Indique si les notifications sont activées globalement
*/
@NotNull(message = "L'activation globale des notifications est obligatoire")
private Boolean notificationsActivees;
/**
* Indique si les notifications push sont activées
*/
private Boolean pushActivees;
/**
* Indique si les notifications par email sont activées
*/
private Boolean emailActivees;
/**
* Indique si les notifications SMS sont activées
*/
private Boolean smsActivees;
/**
* Indique si les notifications in-app sont activées
*/
private Boolean inAppActivees;
/**
* Types de notifications activés
*/
private Set<TypeNotification> typesActives;
/**
* Types de notifications désactivés
*/
private Set<TypeNotification> typesDesactivees;
/**
* Canaux de notification activés
*/
private Set<CanalNotification> canauxActifs;
/**
* Canaux de notification désactivés
*/
private Set<CanalNotification> canauxDesactives;
/**
* Mode Ne Pas Déranger activé
*/
private Boolean modeSilencieux;
/**
* Heure de début du mode silencieux
*/
@JsonFormat(pattern = "HH:mm")
private LocalTime heureDebutSilencieux;
/**
* Heure de fin du mode silencieux
*/
@JsonFormat(pattern = "HH:mm")
private LocalTime heureFinSilencieux;
/**
* Jours de la semaine pour le mode silencieux (1=Lundi, 7=Dimanche)
*/
private Set<Integer> joursSilencieux;
/**
* Indique si les notifications urgentes passent outre le mode silencieux
*/
private Boolean urgentesIgnorentSilencieux;
/**
* Fréquence de regroupement des notifications (minutes)
*/
@Min(value = 0, message = "La fréquence de regroupement doit être positive")
@Max(value = 1440, message = "La fréquence de regroupement ne peut pas dépasser 24h")
private Integer frequenceRegroupementMinutes;
/**
* Nombre maximum de notifications affichées simultanément
*/
@Min(value = 1, message = "Le nombre maximum de notifications doit être au moins 1")
@Max(value = 50, message = "Le nombre maximum de notifications ne peut pas dépasser 50")
private Integer maxNotificationsSimultanees;
/**
* Durée d'affichage par défaut des notifications (secondes)
*/
@Min(value = 1, message = "La durée d'affichage doit être au moins 1 seconde")
@Max(value = 300, message = "La durée d'affichage ne peut pas dépasser 5 minutes")
private Integer dureeAffichageSecondes;
/**
* Indique si les notifications doivent vibrer
*/
private Boolean vibrationActivee;
/**
* Indique si les notifications doivent émettre un son
*/
private Boolean sonActive;
/**
* Indique si la LED doit s'allumer
*/
private Boolean ledActivee;
/**
* Son personnalisé pour les notifications
*/
private String sonPersonnalise;
/**
* Pattern de vibration personnalisé
*/
private long[] patternVibrationPersonnalise;
/**
* Couleur de LED personnalisée
*/
private String couleurLEDPersonnalisee;
/**
* Indique si les aperçus de contenu sont affichés sur l'écran de verrouillage
*/
private Boolean apercuEcranVerrouillage;
/**
* Indique si les notifications sont affichées dans l'historique
*/
private Boolean affichageHistorique;
/**
* Durée de conservation dans l'historique (jours)
*/
@Min(value = 1, message = "La durée de conservation doit être au moins 1 jour")
@Max(value = 365, message = "La durée de conservation ne peut pas dépasser 1 an")
private Integer dureeConservationJours;
/**
* Indique si les notifications sont automatiquement marquées comme lues
*/
private Boolean marquageLectureAutomatique;
/**
* Délai avant marquage automatique comme lu (secondes)
*/
private Integer delaiMarquageLectureSecondes;
/**
* Indique si les notifications sont automatiquement archivées
*/
private Boolean archivageAutomatique;
/**
* Délai avant archivage automatique (heures)
*/
private Integer delaiArchivageHeures;
/**
* Préférences par type de notification
*/
private Map<TypeNotification, PreferenceTypeNotificationDTO> preferencesParType;
/**
* Préférences par canal de notification
*/
private Map<CanalNotification, PreferenceCanalNotificationDTO> preferencesParCanal;
/**
* Mots-clés pour filtrage automatique
*/
private Set<String> motsClesFiltre;
/**
* Expéditeurs bloqués
*/
private Set<String> expediteursBloqués;
/**
* Expéditeurs prioritaires
*/
private Set<String> expediteursPrioritaires;
/**
* Indique si les notifications de test sont activées
*/
private Boolean notificationsTestActivees;
/**
* Niveau de log pour les notifications (DEBUG, INFO, WARN, ERROR)
*/
private String niveauLog;
/**
* Token FCM pour les notifications push
*/
private String tokenFCM;
/**
* Plateforme de l'appareil (android, ios, web)
*/
private String plateforme;
/**
* Version de l'application
*/
private String versionApp;
/**
* Langue préférée pour les notifications
*/
private String langue;
/**
* Fuseau horaire de l'utilisateur
*/
private String fuseauHoraire;
/**
* Métadonnées personnalisées
*/
private Map<String, Object> metadonnees;
// === CONSTRUCTEURS ===
/**
* Constructeur par défaut avec valeurs par défaut
*/
public PreferencesNotificationDTO() {
this.notificationsActivees = true;
this.pushActivees = true;
this.emailActivees = true;
this.smsActivees = false;
this.inAppActivees = true;
this.modeSilencieux = false;
this.urgentesIgnorentSilencieux = true;
this.frequenceRegroupementMinutes = 5;
this.maxNotificationsSimultanees = 10;
this.dureeAffichageSecondes = 10;
this.vibrationActivee = true;
this.sonActive = true;
this.ledActivee = true;
this.apercuEcranVerrouillage = true;
this.affichageHistorique = true;
this.dureeConservationJours = 30;
this.marquageLectureAutomatique = false;
this.archivageAutomatique = true;
this.delaiArchivageHeures = 168; // 1 semaine
this.notificationsTestActivees = false;
this.niveauLog = "INFO";
this.langue = "fr";
}
/**
* Constructeur avec utilisateur
*/
public PreferencesNotificationDTO(String utilisateurId) {
this();
this.utilisateurId = utilisateurId;
}
// === GETTERS ET SETTERS ===
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getUtilisateurId() { return utilisateurId; }
public void setUtilisateurId(String utilisateurId) { this.utilisateurId = utilisateurId; }
public String getOrganisationId() { return organisationId; }
public void setOrganisationId(String organisationId) { this.organisationId = organisationId; }
public Boolean getNotificationsActivees() { return notificationsActivees; }
public void setNotificationsActivees(Boolean notificationsActivees) { this.notificationsActivees = notificationsActivees; }
public Boolean getPushActivees() { return pushActivees; }
public void setPushActivees(Boolean pushActivees) { this.pushActivees = pushActivees; }
public Boolean getEmailActivees() { return emailActivees; }
public void setEmailActivees(Boolean emailActivees) { this.emailActivees = emailActivees; }
public Boolean getSmsActivees() { return smsActivees; }
public void setSmsActivees(Boolean smsActivees) { this.smsActivees = smsActivees; }
public Boolean getInAppActivees() { return inAppActivees; }
public void setInAppActivees(Boolean inAppActivees) { this.inAppActivees = inAppActivees; }
public Set<TypeNotification> getTypesActives() { return typesActives; }
public void setTypesActives(Set<TypeNotification> typesActives) { this.typesActives = typesActives; }
public Set<TypeNotification> getTypesDesactivees() { return typesDesactivees; }
public void setTypesDesactivees(Set<TypeNotification> typesDesactivees) { this.typesDesactivees = typesDesactivees; }
public Set<CanalNotification> getCanauxActifs() { return canauxActifs; }
public void setCanauxActifs(Set<CanalNotification> canauxActifs) { this.canauxActifs = canauxActifs; }
public Set<CanalNotification> getCanauxDesactives() { return canauxDesactives; }
public void setCanauxDesactives(Set<CanalNotification> canauxDesactives) { this.canauxDesactives = canauxDesactives; }
public Boolean getModeSilencieux() { return modeSilencieux; }
public void setModeSilencieux(Boolean modeSilencieux) { this.modeSilencieux = modeSilencieux; }
public LocalTime getHeureDebutSilencieux() { return heureDebutSilencieux; }
public void setHeureDebutSilencieux(LocalTime heureDebutSilencieux) { this.heureDebutSilencieux = heureDebutSilencieux; }
public LocalTime getHeureFinSilencieux() { return heureFinSilencieux; }
public void setHeureFinSilencieux(LocalTime heureFinSilencieux) { this.heureFinSilencieux = heureFinSilencieux; }
public Set<Integer> getJoursSilencieux() { return joursSilencieux; }
public void setJoursSilencieux(Set<Integer> joursSilencieux) { this.joursSilencieux = joursSilencieux; }
public Boolean getUrgentesIgnorentSilencieux() { return urgentesIgnorentSilencieux; }
public void setUrgentesIgnorentSilencieux(Boolean urgentesIgnorentSilencieux) {
this.urgentesIgnorentSilencieux = urgentesIgnorentSilencieux;
}
public Integer getFrequenceRegroupementMinutes() { return frequenceRegroupementMinutes; }
public void setFrequenceRegroupementMinutes(Integer frequenceRegroupementMinutes) {
this.frequenceRegroupementMinutes = frequenceRegroupementMinutes;
}
public Integer getMaxNotificationsSimultanees() { return maxNotificationsSimultanees; }
public void setMaxNotificationsSimultanees(Integer maxNotificationsSimultanees) {
this.maxNotificationsSimultanees = maxNotificationsSimultanees;
}
public Integer getDureeAffichageSecondes() { return dureeAffichageSecondes; }
public void setDureeAffichageSecondes(Integer dureeAffichageSecondes) { this.dureeAffichageSecondes = dureeAffichageSecondes; }
public Boolean getVibrationActivee() { return vibrationActivee; }
public void setVibrationActivee(Boolean vibrationActivee) { this.vibrationActivee = vibrationActivee; }
public Boolean getSonActive() { return sonActive; }
public void setSonActive(Boolean sonActive) { this.sonActive = sonActive; }
public Boolean getLedActivee() { return ledActivee; }
public void setLedActivee(Boolean ledActivee) { this.ledActivee = ledActivee; }
public String getSonPersonnalise() { return sonPersonnalise; }
public void setSonPersonnalise(String sonPersonnalise) { this.sonPersonnalise = sonPersonnalise; }
public long[] getPatternVibrationPersonnalise() { return patternVibrationPersonnalise; }
public void setPatternVibrationPersonnalise(long[] patternVibrationPersonnalise) {
this.patternVibrationPersonnalise = patternVibrationPersonnalise;
}
public String getCouleurLEDPersonnalisee() { return couleurLEDPersonnalisee; }
public void setCouleurLEDPersonnalisee(String couleurLEDPersonnalisee) { this.couleurLEDPersonnalisee = couleurLEDPersonnalisee; }
public Boolean getApercuEcranVerrouillage() { return apercuEcranVerrouillage; }
public void setApercuEcranVerrouillage(Boolean apercuEcranVerrouillage) { this.apercuEcranVerrouillage = apercuEcranVerrouillage; }
public Boolean getAffichageHistorique() { return affichageHistorique; }
public void setAffichageHistorique(Boolean affichageHistorique) { this.affichageHistorique = affichageHistorique; }
public Integer getDureeConservationJours() { return dureeConservationJours; }
public void setDureeConservationJours(Integer dureeConservationJours) { this.dureeConservationJours = dureeConservationJours; }
public Boolean getMarquageLectureAutomatique() { return marquageLectureAutomatique; }
public void setMarquageLectureAutomatique(Boolean marquageLectureAutomatique) {
this.marquageLectureAutomatique = marquageLectureAutomatique;
}
public Integer getDelaiMarquageLectureSecondes() { return delaiMarquageLectureSecondes; }
public void setDelaiMarquageLectureSecondes(Integer delaiMarquageLectureSecondes) {
this.delaiMarquageLectureSecondes = delaiMarquageLectureSecondes;
}
public Boolean getArchivageAutomatique() { return archivageAutomatique; }
public void setArchivageAutomatique(Boolean archivageAutomatique) { this.archivageAutomatique = archivageAutomatique; }
public Integer getDelaiArchivageHeures() { return delaiArchivageHeures; }
public void setDelaiArchivageHeures(Integer delaiArchivageHeures) { this.delaiArchivageHeures = delaiArchivageHeures; }
public Map<TypeNotification, PreferenceTypeNotificationDTO> getPreferencesParType() { return preferencesParType; }
public void setPreferencesParType(Map<TypeNotification, PreferenceTypeNotificationDTO> preferencesParType) {
this.preferencesParType = preferencesParType;
}
public Map<CanalNotification, PreferenceCanalNotificationDTO> getPreferencesParCanal() { return preferencesParCanal; }
public void setPreferencesParCanal(Map<CanalNotification, PreferenceCanalNotificationDTO> preferencesParCanal) {
this.preferencesParCanal = preferencesParCanal;
}
public Set<String> getMotsClesFiltre() { return motsClesFiltre; }
public void setMotsClesFiltre(Set<String> motsClesFiltre) { this.motsClesFiltre = motsClesFiltre; }
public Set<String> getExpediteursBloques() { return expediteursBloqués; }
public void setExpediteursBloques(Set<String> expediteursBloqués) { this.expediteursBloqués = expediteursBloqués; }
public Set<String> getExpediteursPrioritaires() { return expediteursPrioritaires; }
public void setExpediteursPrioritaires(Set<String> expediteursPrioritaires) { this.expediteursPrioritaires = expediteursPrioritaires; }
public Boolean getNotificationsTestActivees() { return notificationsTestActivees; }
public void setNotificationsTestActivees(Boolean notificationsTestActivees) {
this.notificationsTestActivees = notificationsTestActivees;
}
public String getNiveauLog() { return niveauLog; }
public void setNiveauLog(String niveauLog) { this.niveauLog = niveauLog; }
public String getTokenFCM() { return tokenFCM; }
public void setTokenFCM(String tokenFCM) { this.tokenFCM = tokenFCM; }
public String getPlateforme() { return plateforme; }
public void setPlateforme(String plateforme) { this.plateforme = plateforme; }
public String getVersionApp() { return versionApp; }
public void setVersionApp(String versionApp) { this.versionApp = versionApp; }
public String getLangue() { return langue; }
public void setLangue(String langue) { this.langue = langue; }
public String getFuseauHoraire() { return fuseauHoraire; }
public void setFuseauHoraire(String fuseauHoraire) { this.fuseauHoraire = fuseauHoraire; }
public Map<String, Object> getMetadonnees() { return metadonnees; }
public void setMetadonnees(Map<String, Object> metadonnees) { this.metadonnees = metadonnees; }
// === MÉTHODES UTILITAIRES ===
/**
* Vérifie si un type de notification est activé
*/
public boolean isTypeActive(TypeNotification type) {
if (!notificationsActivees) return false;
if (typesDesactivees != null && typesDesactivees.contains(type)) return false;
if (typesActives != null) return typesActives.contains(type);
return type.isActiveeParDefaut();
}
/**
* Vérifie si un canal de notification est activé
*/
public boolean isCanalActif(CanalNotification canal) {
if (!notificationsActivees) return false;
if (canauxDesactives != null && canauxDesactives.contains(canal)) return false;
if (canauxActifs != null) return canauxActifs.contains(canal);
return true;
}
/**
* Vérifie si on est en mode silencieux actuellement
*/
public boolean isEnModeSilencieux() {
if (!modeSilencieux) return false;
if (heureDebutSilencieux == null || heureFinSilencieux == null) return false;
LocalTime maintenant = LocalTime.now();
// Gestion du cas où la période traverse minuit
if (heureDebutSilencieux.isAfter(heureFinSilencieux)) {
return maintenant.isAfter(heureDebutSilencieux) || maintenant.isBefore(heureFinSilencieux);
} else {
return maintenant.isAfter(heureDebutSilencieux) && maintenant.isBefore(heureFinSilencieux);
}
}
/**
* Vérifie si un expéditeur est bloqué
*/
public boolean isExpediteurBloque(String expediteurId) {
return expediteursBloqués != null && expediteursBloqués.contains(expediteurId);
}
/**
* Vérifie si un expéditeur est prioritaire
*/
public boolean isExpediteurPrioritaire(String expediteurId) {
return expediteursPrioritaires != null && expediteursPrioritaires.contains(expediteurId);
}
@Override
public String toString() {
return String.format("PreferencesNotificationDTO{utilisateurId='%s', notificationsActivees=%s}",
utilisateurId, notificationsActivees);
}
}

View File

@@ -0,0 +1,99 @@
package dev.lions.unionflow.server.api.dto.solidarite;
import jakarta.validation.constraints.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.Builder;
import java.time.LocalDate;
/**
* DTO pour les bénéficiaires d'une aide
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class BeneficiaireAideDTO {
/**
* Identifiant unique du bénéficiaire
*/
private String id;
/**
* Nom complet du bénéficiaire
*/
@NotBlank(message = "Le nom du bénéficiaire est obligatoire")
@Size(max = 100, message = "Le nom ne peut pas dépasser 100 caractères")
private String nomComplet;
/**
* Relation avec le demandeur
*/
@NotBlank(message = "La relation avec le demandeur est obligatoire")
private String relationDemandeur;
/**
* Date de naissance
*/
private LocalDate dateNaissance;
/**
* Âge calculé
*/
private Integer age;
/**
* Genre
*/
private String genre;
/**
* Numéro de téléphone
*/
@Pattern(regexp = "^\\+?[0-9]{8,15}$", message = "Le numéro de téléphone n'est pas valide")
private String telephone;
/**
* Adresse email
*/
@Email(message = "L'adresse email n'est pas valide")
private String email;
/**
* Adresse physique
*/
@Size(max = 200, message = "L'adresse ne peut pas dépasser 200 caractères")
private String adresse;
/**
* Situation particulière (handicap, maladie, etc.)
*/
@Size(max = 500, message = "La situation particulière ne peut pas dépasser 500 caractères")
private String situationParticuliere;
/**
* Indique si le bénéficiaire est le demandeur principal
*/
@Builder.Default
private Boolean estDemandeurPrincipal = false;
/**
* Pourcentage de l'aide destiné à ce bénéficiaire
*/
@DecimalMin(value = "0.0", message = "Le pourcentage doit être positif")
@DecimalMax(value = "100.0", message = "Le pourcentage ne peut pas dépasser 100%")
private Double pourcentageAide;
/**
* Montant spécifique pour ce bénéficiaire
*/
@DecimalMin(value = "0.0", message = "Le montant doit être positif")
private Double montantSpecifique;
}

View File

@@ -0,0 +1,130 @@
package dev.lions.unionflow.server.api.dto.solidarite;
import jakarta.validation.constraints.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.Builder;
import java.time.LocalDateTime;
import java.util.List;
/**
* DTO pour les commentaires sur une demande d'aide
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class CommentaireAideDTO {
/**
* Identifiant unique du commentaire
*/
private String id;
/**
* Contenu du commentaire
*/
@NotBlank(message = "Le contenu du commentaire est obligatoire")
@Size(min = 5, max = 2000, message = "Le commentaire doit contenir entre 5 et 2000 caractères")
private String contenu;
/**
* Type de commentaire
*/
@NotBlank(message = "Le type de commentaire est obligatoire")
private String typeCommentaire;
/**
* Date de création du commentaire
*/
@NotNull(message = "La date de création est obligatoire")
@Builder.Default
private LocalDateTime dateCreation = LocalDateTime.now();
/**
* Date de dernière modification
*/
private LocalDateTime dateModification;
/**
* Identifiant de l'auteur du commentaire
*/
@NotBlank(message = "L'identifiant de l'auteur est obligatoire")
private String auteurId;
/**
* Nom de l'auteur du commentaire
*/
private String auteurNom;
/**
* Rôle de l'auteur
*/
private String auteurRole;
/**
* Indique si le commentaire est privé (visible seulement aux évaluateurs)
*/
@Builder.Default
private Boolean estPrive = false;
/**
* Indique si le commentaire est important
*/
@Builder.Default
private Boolean estImportant = false;
/**
* Identifiant du commentaire parent (pour les réponses)
*/
private String commentaireParentId;
/**
* Réponses à ce commentaire
*/
private List<CommentaireAideDTO> reponses;
/**
* Pièces jointes au commentaire
*/
private List<PieceJustificativeDTO> piecesJointes;
/**
* Mentions d'utilisateurs dans le commentaire
*/
private List<String> mentionsUtilisateurs;
/**
* Indique si le commentaire a été modifié
*/
@Builder.Default
private Boolean estModifie = false;
/**
* Nombre de likes/réactions
*/
@Builder.Default
private Integer nombreReactions = 0;
/**
* Indique si le commentaire est résolu (pour les questions)
*/
@Builder.Default
private Boolean estResolu = false;
/**
* Date de résolution
*/
private LocalDateTime dateResolution;
/**
* Identifiant de la personne qui a marqué comme résolu
*/
private String resoluteurId;
}

View File

@@ -0,0 +1,109 @@
package dev.lions.unionflow.server.api.dto.solidarite;
import jakarta.validation.constraints.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.Builder;
/**
* DTO pour les informations de contact du proposant d'aide
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ContactProposantDTO {
/**
* Numéro de téléphone principal
*/
@Pattern(regexp = "^\\+?[0-9]{8,15}$", message = "Le numéro de téléphone n'est pas valide")
private String telephonePrincipal;
/**
* Numéro de téléphone secondaire
*/
@Pattern(regexp = "^\\+?[0-9]{8,15}$", message = "Le numéro de téléphone secondaire n'est pas valide")
private String telephoneSecondaire;
/**
* Adresse email
*/
@Email(message = "L'adresse email n'est pas valide")
private String email;
/**
* Adresse email secondaire
*/
@Email(message = "L'adresse email secondaire n'est pas valide")
private String emailSecondaire;
/**
* Identifiant WhatsApp
*/
@Pattern(regexp = "^\\+?[0-9]{8,15}$", message = "Le numéro WhatsApp n'est pas valide")
private String whatsapp;
/**
* Identifiant Telegram
*/
@Size(max = 50, message = "L'identifiant Telegram ne peut pas dépasser 50 caractères")
private String telegram;
/**
* Autres moyens de contact (réseaux sociaux, etc.)
*/
private java.util.Map<String, String> autresContacts;
/**
* Adresse physique pour rencontres
*/
@Size(max = 200, message = "L'adresse ne peut pas dépasser 200 caractères")
private String adressePhysique;
/**
* Indique si les rencontres physiques sont possibles
*/
@Builder.Default
private Boolean rencontresPhysiquesPossibles = false;
/**
* Indique si les appels téléphoniques sont acceptés
*/
@Builder.Default
private Boolean appelsAcceptes = true;
/**
* Indique si les SMS sont acceptés
*/
@Builder.Default
private Boolean smsAcceptes = true;
/**
* Indique si les emails sont acceptés
*/
@Builder.Default
private Boolean emailsAcceptes = true;
/**
* Horaires de disponibilité pour contact
*/
@Size(max = 200, message = "Les horaires ne peuvent pas dépasser 200 caractères")
private String horairesDisponibilite;
/**
* Langue(s) de communication préférée(s)
*/
private java.util.List<String> languesPreferees;
/**
* Instructions spéciales pour le contact
*/
@Size(max = 300, message = "Les instructions ne peuvent pas dépasser 300 caractères")
private String instructionsSpeciales;
}

View File

@@ -0,0 +1,84 @@
package dev.lions.unionflow.server.api.dto.solidarite;
import jakarta.validation.constraints.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.Builder;
/**
* DTO pour les informations de contact d'urgence
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ContactUrgenceDTO {
/**
* Nom complet du contact d'urgence
*/
@NotBlank(message = "Le nom du contact d'urgence est obligatoire")
@Size(max = 100, message = "Le nom ne peut pas dépasser 100 caractères")
private String nomComplet;
/**
* Relation avec le demandeur
*/
@NotBlank(message = "La relation avec le demandeur est obligatoire")
@Size(max = 50, message = "La relation ne peut pas dépasser 50 caractères")
private String relation;
/**
* Numéro de téléphone principal
*/
@NotBlank(message = "Le numéro de téléphone est obligatoire")
@Pattern(regexp = "^\\+?[0-9]{8,15}$", message = "Le numéro de téléphone n'est pas valide")
private String telephonePrincipal;
/**
* Numéro de téléphone secondaire
*/
@Pattern(regexp = "^\\+?[0-9]{8,15}$", message = "Le numéro de téléphone secondaire n'est pas valide")
private String telephoneSecondaire;
/**
* Adresse email
*/
@Email(message = "L'adresse email n'est pas valide")
private String email;
/**
* Adresse physique
*/
@Size(max = 200, message = "L'adresse ne peut pas dépasser 200 caractères")
private String adresse;
/**
* Disponibilité (horaires)
*/
@Size(max = 100, message = "La disponibilité ne peut pas dépasser 100 caractères")
private String disponibilite;
/**
* Indique si ce contact peut prendre des décisions pour le demandeur
*/
@Builder.Default
private Boolean peutPrendreDecisions = false;
/**
* Indique si ce contact doit être notifié automatiquement
*/
@Builder.Default
private Boolean notificationAutomatique = true;
/**
* Commentaires additionnels
*/
@Size(max = 300, message = "Les commentaires ne peuvent pas dépasser 300 caractères")
private String commentaires;
}

View File

@@ -0,0 +1,179 @@
package dev.lions.unionflow.server.api.dto.solidarite;
import jakarta.validation.constraints.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.Builder;
import java.time.DayOfWeek;
import java.time.LocalTime;
import java.time.LocalDate;
/**
* DTO pour les créneaux de disponibilité du proposant
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class CreneauDisponibiliteDTO {
/**
* Identifiant unique du créneau
*/
private String id;
/**
* Jour de la semaine (pour créneaux récurrents)
*/
private DayOfWeek jourSemaine;
/**
* Date spécifique (pour créneaux ponctuels)
*/
private LocalDate dateSpecifique;
/**
* Heure de début
*/
@NotNull(message = "L'heure de début est obligatoire")
private LocalTime heureDebut;
/**
* Heure de fin
*/
@NotNull(message = "L'heure de fin est obligatoire")
private LocalTime heureFin;
/**
* Type de créneau
*/
@NotNull(message = "Le type de créneau est obligatoire")
@Builder.Default
private TypeCreneau type = TypeCreneau.RECURRENT;
/**
* Indique si le créneau est actif
*/
@Builder.Default
private Boolean estActif = true;
/**
* Fuseau horaire
*/
@Builder.Default
private String fuseauHoraire = "Africa/Abidjan";
/**
* Commentaires sur le créneau
*/
@Size(max = 200, message = "Les commentaires ne peuvent pas dépasser 200 caractères")
private String commentaires;
/**
* Priorité du créneau (1 = haute, 5 = basse)
*/
@Min(value = 1, message = "La priorité doit être au moins 1")
@Max(value = 5, message = "La priorité ne peut pas dépasser 5")
@Builder.Default
private Integer priorite = 3;
/**
* Durée maximale d'intervention en minutes
*/
@Min(value = 15, message = "La durée doit être au moins 15 minutes")
@Max(value = 480, message = "La durée ne peut pas dépasser 8 heures")
private Integer dureeMaxMinutes;
/**
* Indique si des pauses sont nécessaires
*/
@Builder.Default
private Boolean pausesNecessaires = false;
/**
* Durée des pauses en minutes
*/
@Min(value = 5, message = "La durée de pause doit être au moins 5 minutes")
private Integer dureePauseMinutes;
/**
* Énumération des types de créneaux
*/
public enum TypeCreneau {
RECURRENT("Récurrent"),
PONCTUEL("Ponctuel"),
URGENCE("Urgence"),
FLEXIBLE("Flexible");
private final String libelle;
TypeCreneau(String libelle) {
this.libelle = libelle;
}
public String getLibelle() {
return libelle;
}
}
// === MÉTHODES UTILITAIRES ===
/**
* Vérifie si le créneau est valide (heure fin > heure début)
*/
public boolean isValide() {
return heureDebut != null && heureFin != null && heureFin.isAfter(heureDebut);
}
/**
* Calcule la durée du créneau en minutes
*/
public long getDureeMinutes() {
if (!isValide()) return 0;
return java.time.Duration.between(heureDebut, heureFin).toMinutes();
}
/**
* Vérifie si le créneau est disponible à une date donnée
*/
public boolean isDisponibleLe(LocalDate date) {
if (!estActif) return false;
return switch (type) {
case PONCTUEL -> dateSpecifique != null && dateSpecifique.equals(date);
case RECURRENT -> jourSemaine != null && date.getDayOfWeek() == jourSemaine;
case URGENCE, FLEXIBLE -> true;
};
}
/**
* Vérifie si une heure est dans le créneau
*/
public boolean contientHeure(LocalTime heure) {
if (!isValide()) return false;
return !heure.isBefore(heureDebut) && !heure.isAfter(heureFin);
}
/**
* Retourne le libellé du créneau
*/
public String getLibelle() {
StringBuilder sb = new StringBuilder();
if (type == TypeCreneau.RECURRENT && jourSemaine != null) {
sb.append(jourSemaine.name()).append(" ");
} else if (type == TypeCreneau.PONCTUEL && dateSpecifique != null) {
sb.append(dateSpecifique.toString()).append(" ");
}
sb.append(heureDebut.toString()).append(" - ").append(heureFin.toString());
return sb.toString();
}
}

View File

@@ -0,0 +1,71 @@
package dev.lions.unionflow.server.api.dto.solidarite;
import jakarta.validation.constraints.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.Builder;
/**
* DTO pour les critères de sélection des bénéficiaires
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class CritereSelectionDTO {
/**
* Nom du critère
*/
@NotBlank(message = "Le nom du critère est obligatoire")
@Size(max = 100, message = "Le nom ne peut pas dépasser 100 caractères")
private String nom;
/**
* Type de critère (age, situation, localisation, etc.)
*/
@NotBlank(message = "Le type de critère est obligatoire")
private String type;
/**
* Opérateur de comparaison (equals, greater_than, less_than, contains, etc.)
*/
@NotBlank(message = "L'opérateur est obligatoire")
private String operateur;
/**
* Valeur de référence pour la comparaison
*/
@NotBlank(message = "La valeur est obligatoire")
private String valeur;
/**
* Valeur maximale (pour les plages)
*/
private String valeurMax;
/**
* Indique si le critère est obligatoire
*/
@Builder.Default
private Boolean estObligatoire = false;
/**
* Poids du critère dans la sélection (1-10)
*/
@Min(value = 1, message = "Le poids doit être au moins 1")
@Max(value = 10, message = "Le poids ne peut pas dépasser 10")
@Builder.Default
private Integer poids = 5;
/**
* Description du critère
*/
@Size(max = 200, message = "La description ne peut pas dépasser 200 caractères")
private String description;
}

View File

@@ -0,0 +1,374 @@
package dev.lions.unionflow.server.api.dto.solidarite;
import dev.lions.unionflow.server.api.enums.solidarite.TypeAide;
import dev.lions.unionflow.server.api.enums.solidarite.StatutAide;
import dev.lions.unionflow.server.api.enums.solidarite.PrioriteAide;
import jakarta.validation.constraints.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.Builder;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
/**
* DTO pour les demandes d'aide dans le système de solidarité
*
* Ce DTO représente une demande d'aide complète avec toutes les informations
* nécessaires pour le traitement, l'évaluation et le suivi.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class DemandeAideDTO {
// === IDENTIFICATION ===
/**
* Identifiant unique de la demande d'aide
*/
private String id;
/**
* Numéro de référence de la demande (généré automatiquement)
*/
@Pattern(regexp = "^DA-\\d{4}-\\d{6}$", message = "Le numéro de référence doit suivre le format DA-YYYY-NNNNNN")
private String numeroReference;
// === INFORMATIONS DE BASE ===
/**
* Type d'aide demandée
*/
@NotNull(message = "Le type d'aide est obligatoire")
private TypeAide typeAide;
/**
* Titre court de la demande
*/
@NotBlank(message = "Le titre est obligatoire")
@Size(min = 10, max = 100, message = "Le titre doit contenir entre 10 et 100 caractères")
private String titre;
/**
* Description détaillée de la demande
*/
@NotBlank(message = "La description est obligatoire")
@Size(min = 50, max = 2000, message = "La description doit contenir entre 50 et 2000 caractères")
private String description;
/**
* Justification de la demande
*/
@Size(max = 1000, message = "La justification ne peut pas dépasser 1000 caractères")
private String justification;
// === MONTANT ET FINANCES ===
/**
* Montant demandé (si applicable)
*/
@DecimalMin(value = "0.0", inclusive = false, message = "Le montant doit être positif")
@DecimalMax(value = "1000000.0", message = "Le montant ne peut pas dépasser 1 000 000 FCFA")
private Double montantDemande;
/**
* Montant approuvé (si différent du montant demandé)
*/
@DecimalMin(value = "0.0", inclusive = false, message = "Le montant approuvé doit être positif")
private Double montantApprouve;
/**
* Montant versé effectivement
*/
@DecimalMin(value = "0.0", inclusive = false, message = "Le montant versé doit être positif")
private Double montantVerse;
/**
* Devise du montant
*/
@Builder.Default
private String devise = "FCFA";
// === ACTEURS ===
/**
* Identifiant du demandeur
*/
@NotBlank(message = "L'identifiant du demandeur est obligatoire")
private String demandeurId;
/**
* Nom complet du demandeur
*/
private String demandeurNom;
/**
* Identifiant de l'évaluateur assigné
*/
private String evaluateurId;
/**
* Nom de l'évaluateur
*/
private String evaluateurNom;
/**
* Identifiant de l'approbateur
*/
private String approvateurId;
/**
* Nom de l'approbateur
*/
private String approvateurNom;
/**
* Identifiant de l'organisation
*/
@NotBlank(message = "L'identifiant de l'organisation est obligatoire")
private String organisationId;
// === STATUT ET PRIORITÉ ===
/**
* Statut actuel de la demande
*/
@NotNull(message = "Le statut est obligatoire")
@Builder.Default
private StatutAide statut = StatutAide.BROUILLON;
/**
* Priorité de la demande
*/
@NotNull(message = "La priorité est obligatoire")
@Builder.Default
private PrioriteAide priorite = PrioriteAide.NORMALE;
/**
* Motif de rejet (si applicable)
*/
@Size(max = 500, message = "Le motif de rejet ne peut pas dépasser 500 caractères")
private String motifRejet;
/**
* Commentaires de l'évaluateur
*/
@Size(max = 1000, message = "Les commentaires ne peuvent pas dépasser 1000 caractères")
private String commentairesEvaluateur;
// === DATES ===
/**
* Date de création de la demande
*/
@NotNull(message = "La date de création est obligatoire")
@Builder.Default
private LocalDateTime dateCreation = LocalDateTime.now();
/**
* Date de soumission de la demande
*/
private LocalDateTime dateSoumission;
/**
* Date limite de traitement
*/
private LocalDateTime dateLimiteTraitement;
/**
* Date d'évaluation
*/
private LocalDateTime dateEvaluation;
/**
* Date d'approbation
*/
private LocalDateTime dateApprobation;
/**
* Date de versement
*/
private LocalDateTime dateVersement;
/**
* Date de clôture
*/
private LocalDateTime dateCloture;
/**
* Date de dernière modification
*/
@Builder.Default
private LocalDateTime dateModification = LocalDateTime.now();
// === INFORMATIONS COMPLÉMENTAIRES ===
/**
* Pièces justificatives attachées
*/
private List<PieceJustificativeDTO> piecesJustificatives;
/**
* Bénéficiaires de l'aide (si différents du demandeur)
*/
private List<BeneficiaireAideDTO> beneficiaires;
/**
* Historique des changements de statut
*/
private List<HistoriqueStatutDTO> historiqueStatuts;
/**
* Commentaires et échanges
*/
private List<CommentaireAideDTO> commentaires;
/**
* Données personnalisées spécifiques au type d'aide
*/
private Map<String, Object> donneesPersonnalisees;
/**
* Tags pour catégorisation
*/
private List<String> tags;
// === MÉTADONNÉES ===
/**
* Indique si la demande est confidentielle
*/
@Builder.Default
private Boolean estConfidentielle = false;
/**
* Indique si la demande nécessite un suivi
*/
@Builder.Default
private Boolean necessiteSuivi = false;
/**
* Score de priorité calculé automatiquement
*/
private Double scorePriorite;
/**
* Nombre de vues de la demande
*/
@Builder.Default
private Integer nombreVues = 0;
/**
* Version du document (pour gestion des conflits)
*/
@Builder.Default
private Integer version = 1;
/**
* Informations de géolocalisation (si pertinent)
*/
private LocalisationDTO localisation;
/**
* Informations de contact d'urgence
*/
private ContactUrgenceDTO contactUrgence;
// === MÉTHODES UTILITAIRES ===
/**
* Vérifie si la demande est modifiable
*/
public boolean isModifiable() {
return statut != null && statut.permetModification();
}
/**
* Vérifie si la demande peut être annulée
*/
public boolean peutEtreAnnulee() {
return statut != null && statut.permetAnnulation();
}
/**
* Vérifie si la demande est urgente
*/
public boolean isUrgente() {
return priorite != null && priorite.isUrgente();
}
/**
* Vérifie si la demande est terminée
*/
public boolean isTerminee() {
return statut != null && statut.isEstFinal();
}
/**
* Vérifie si la demande est en succès
*/
public boolean isEnSucces() {
return statut != null && statut.isSucces();
}
/**
* Calcule le pourcentage d'avancement
*/
public double getPourcentageAvancement() {
if (statut == null) return 0.0;
return switch (statut) {
case BROUILLON -> 5.0;
case SOUMISE -> 10.0;
case EN_ATTENTE -> 20.0;
case EN_COURS_EVALUATION -> 40.0;
case INFORMATIONS_REQUISES -> 35.0;
case APPROUVEE, APPROUVEE_PARTIELLEMENT -> 60.0;
case EN_COURS_TRAITEMENT -> 70.0;
case EN_COURS_VERSEMENT -> 85.0;
case VERSEE, LIVREE, TERMINEE -> 100.0;
case REJETEE, ANNULEE, EXPIREE -> 100.0;
case SUSPENDUE -> 50.0;
case EN_SUIVI -> 95.0;
case CLOTUREE -> 100.0;
};
}
/**
* Retourne le délai restant en heures
*/
public long getDelaiRestantHeures() {
if (dateLimiteTraitement == null) return -1;
LocalDateTime maintenant = LocalDateTime.now();
if (maintenant.isAfter(dateLimiteTraitement)) return 0;
return java.time.Duration.between(maintenant, dateLimiteTraitement).toHours();
}
/**
* Vérifie si le délai est dépassé
*/
public boolean isDelaiDepasse() {
return getDelaiRestantHeures() == 0;
}
/**
* Retourne la durée de traitement en jours
*/
public long getDureeTraitementJours() {
if (dateCreation == null) return 0;
LocalDateTime dateFin = dateCloture != null ? dateCloture : LocalDateTime.now();
return java.time.Duration.between(dateCreation, dateFin).toDays();
}
}

View File

@@ -0,0 +1,347 @@
package dev.lions.unionflow.server.api.dto.solidarite;
import jakarta.validation.constraints.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.Builder;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
/**
* DTO pour l'évaluation d'une aide reçue ou fournie
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class EvaluationAideDTO {
/**
* Identifiant unique de l'évaluation
*/
private String id;
/**
* Identifiant de la demande d'aide évaluée
*/
@NotBlank(message = "L'identifiant de la demande d'aide est obligatoire")
private String demandeAideId;
/**
* Identifiant de la proposition d'aide évaluée (si applicable)
*/
private String propositionAideId;
/**
* Identifiant de l'évaluateur
*/
@NotBlank(message = "L'identifiant de l'évaluateur est obligatoire")
private String evaluateurId;
/**
* Nom de l'évaluateur
*/
private String evaluateurNom;
/**
* Rôle de l'évaluateur (beneficiaire, proposant, evaluateur_externe)
*/
@NotBlank(message = "Le rôle de l'évaluateur est obligatoire")
private String roleEvaluateur;
/**
* Type d'évaluation
*/
@NotNull(message = "Le type d'évaluation est obligatoire")
@Builder.Default
private TypeEvaluation typeEvaluation = TypeEvaluation.SATISFACTION_BENEFICIAIRE;
/**
* Note globale (1-5)
*/
@NotNull(message = "La note globale est obligatoire")
@DecimalMin(value = "1.0", message = "La note doit être au moins 1")
@DecimalMax(value = "5.0", message = "La note ne peut pas dépasser 5")
private Double noteGlobale;
/**
* Notes détaillées par critère
*/
private Map<String, Double> notesDetaillees;
/**
* Commentaire principal
*/
@Size(min = 10, max = 1000, message = "Le commentaire doit contenir entre 10 et 1000 caractères")
private String commentairePrincipal;
/**
* Points positifs
*/
@Size(max = 500, message = "Les points positifs ne peuvent pas dépasser 500 caractères")
private String pointsPositifs;
/**
* Points d'amélioration
*/
@Size(max = 500, message = "Les points d'amélioration ne peuvent pas dépasser 500 caractères")
private String pointsAmelioration;
/**
* Recommandations
*/
@Size(max = 500, message = "Les recommandations ne peuvent pas dépasser 500 caractères")
private String recommandations;
/**
* Indique si l'évaluateur recommande cette aide/proposant
*/
@Builder.Default
private Boolean recommande = true;
/**
* Indique si l'aide a été utile
*/
@Builder.Default
private Boolean aideUtile = true;
/**
* Indique si l'aide a résolu le problème
*/
@Builder.Default
private Boolean problemeResolu = true;
/**
* Délai de réponse perçu (1=très lent, 5=très rapide)
*/
@DecimalMin(value = "1.0", message = "La note délai doit être au moins 1")
@DecimalMax(value = "5.0", message = "La note délai ne peut pas dépasser 5")
private Double noteDelaiReponse;
/**
* Qualité de la communication (1=très mauvaise, 5=excellente)
*/
@DecimalMin(value = "1.0", message = "La note communication doit être au moins 1")
@DecimalMax(value = "5.0", message = "La note communication ne peut pas dépasser 5")
private Double noteCommunication;
/**
* Professionnalisme (1=très mauvais, 5=excellent)
*/
@DecimalMin(value = "1.0", message = "La note professionnalisme doit être au moins 1")
@DecimalMax(value = "5.0", message = "La note professionnalisme ne peut pas dépasser 5")
private Double noteProfessionnalisme;
/**
* Respect des engagements (1=très mauvais, 5=excellent)
*/
@DecimalMin(value = "1.0", message = "La note engagement doit être au moins 1")
@DecimalMax(value = "5.0", message = "La note engagement ne peut pas dépasser 5")
private Double noteRespectEngagements;
/**
* Date de création de l'évaluation
*/
@NotNull(message = "La date de création est obligatoire")
@Builder.Default
private LocalDateTime dateCreation = LocalDateTime.now();
/**
* Date de dernière modification
*/
@Builder.Default
private LocalDateTime dateModification = LocalDateTime.now();
/**
* Indique si l'évaluation est publique
*/
@Builder.Default
private Boolean estPublique = true;
/**
* Indique si l'évaluation est anonyme
*/
@Builder.Default
private Boolean estAnonyme = false;
/**
* Indique si l'évaluation a été vérifiée
*/
@Builder.Default
private Boolean estVerifiee = false;
/**
* Date de vérification
*/
private LocalDateTime dateVerification;
/**
* Identifiant du vérificateur
*/
private String verificateurId;
/**
* Pièces jointes à l'évaluation (photos, documents)
*/
private List<PieceJustificativeDTO> piecesJointes;
/**
* Tags associés à l'évaluation
*/
private List<String> tags;
/**
* Données additionnelles
*/
private Map<String, Object> donneesAdditionnelles;
/**
* Nombre de personnes qui ont trouvé cette évaluation utile
*/
@Builder.Default
private Integer nombreUtile = 0;
/**
* Nombre de signalements de cette évaluation
*/
@Builder.Default
private Integer nombreSignalements = 0;
/**
* Statut de l'évaluation
*/
@NotNull(message = "Le statut est obligatoire")
@Builder.Default
private StatutEvaluation statut = StatutEvaluation.ACTIVE;
/**
* Énumération des types d'évaluation
*/
public enum TypeEvaluation {
SATISFACTION_BENEFICIAIRE("Satisfaction du bénéficiaire"),
EVALUATION_PROPOSANT("Évaluation du proposant"),
EVALUATION_PROCESSUS("Évaluation du processus"),
SUIVI_POST_AIDE("Suivi post-aide"),
EVALUATION_IMPACT("Évaluation d'impact"),
RETOUR_EXPERIENCE("Retour d'expérience");
private final String libelle;
TypeEvaluation(String libelle) {
this.libelle = libelle;
}
public String getLibelle() {
return libelle;
}
}
/**
* Énumération des statuts d'évaluation
*/
public enum StatutEvaluation {
BROUILLON("Brouillon"),
ACTIVE("Active"),
MASQUEE("Masquée"),
SIGNALEE("Signalée"),
SUPPRIMEE("Supprimée");
private final String libelle;
StatutEvaluation(String libelle) {
this.libelle = libelle;
}
public String getLibelle() {
return libelle;
}
}
// === MÉTHODES UTILITAIRES ===
/**
* Calcule la note moyenne des critères détaillés
*/
public Double getNoteMoyenneDetaillees() {
if (notesDetaillees == null || notesDetaillees.isEmpty()) {
return noteGlobale;
}
return notesDetaillees.values().stream()
.mapToDouble(Double::doubleValue)
.average()
.orElse(noteGlobale);
}
/**
* Vérifie si l'évaluation est positive (note >= 4)
*/
public boolean isPositive() {
return noteGlobale != null && noteGlobale >= 4.0;
}
/**
* Vérifie si l'évaluation est négative (note <= 2)
*/
public boolean isNegative() {
return noteGlobale != null && noteGlobale <= 2.0;
}
/**
* Calcule un score de qualité global
*/
public double getScoreQualite() {
double score = noteGlobale != null ? noteGlobale : 0.0;
// Bonus pour les notes détaillées
if (noteDelaiReponse != null) score += noteDelaiReponse * 0.1;
if (noteCommunication != null) score += noteCommunication * 0.1;
if (noteProfessionnalisme != null) score += noteProfessionnalisme * 0.1;
if (noteRespectEngagements != null) score += noteRespectEngagements * 0.1;
// Bonus pour recommandation
if (recommande != null && recommande) score += 0.2;
// Bonus pour résolution du problème
if (problemeResolu != null && problemeResolu) score += 0.3;
// Malus pour signalements
if (nombreSignalements > 0) score -= nombreSignalements * 0.1;
return Math.min(5.0, Math.max(0.0, score));
}
/**
* Vérifie si l'évaluation est complète
*/
public boolean isComplete() {
return noteGlobale != null &&
commentairePrincipal != null && !commentairePrincipal.trim().isEmpty() &&
recommande != null &&
aideUtile != null &&
problemeResolu != null;
}
/**
* Retourne le niveau de satisfaction
*/
public String getNiveauSatisfaction() {
if (noteGlobale == null) return "Non évalué";
return switch (noteGlobale.intValue()) {
case 5 -> "Excellent";
case 4 -> "Très bien";
case 3 -> "Bien";
case 2 -> "Passable";
case 1 -> "Insuffisant";
default -> "Non évalué";
};
}
}

View File

@@ -0,0 +1,87 @@
package dev.lions.unionflow.server.api.dto.solidarite;
import dev.lions.unionflow.server.api.enums.solidarite.StatutAide;
import jakarta.validation.constraints.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.Builder;
import java.time.LocalDateTime;
/**
* DTO pour l'historique des changements de statut d'une demande d'aide
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class HistoriqueStatutDTO {
/**
* Identifiant unique de l'entrée d'historique
*/
private String id;
/**
* Ancien statut
*/
private StatutAide ancienStatut;
/**
* Nouveau statut
*/
@NotNull(message = "Le nouveau statut est obligatoire")
private StatutAide nouveauStatut;
/**
* Date du changement de statut
*/
@NotNull(message = "La date de changement est obligatoire")
@Builder.Default
private LocalDateTime dateChangement = LocalDateTime.now();
/**
* Identifiant de la personne qui a effectué le changement
*/
@NotBlank(message = "L'identifiant de l'auteur est obligatoire")
private String auteurId;
/**
* Nom de la personne qui a effectué le changement
*/
private String auteurNom;
/**
* Motif du changement de statut
*/
@Size(max = 500, message = "Le motif ne peut pas dépasser 500 caractères")
private String motif;
/**
* Commentaires additionnels
*/
@Size(max = 1000, message = "Les commentaires ne peuvent pas dépasser 1000 caractères")
private String commentaires;
/**
* Indique si le changement est automatique (système)
*/
@Builder.Default
private Boolean estAutomatique = false;
/**
* Durée en minutes depuis le statut précédent
*/
private Long dureeDepuisPrecedent;
/**
* Données additionnelles liées au changement
*/
private java.util.Map<String, Object> donneesAdditionnelles;
}

View File

@@ -0,0 +1,77 @@
package dev.lions.unionflow.server.api.dto.solidarite;
import jakarta.validation.constraints.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.Builder;
/**
* DTO pour les informations de géolocalisation
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class LocalisationDTO {
/**
* Latitude
*/
@DecimalMin(value = "-90.0", message = "La latitude doit être comprise entre -90 et 90")
@DecimalMax(value = "90.0", message = "La latitude doit être comprise entre -90 et 90")
private Double latitude;
/**
* Longitude
*/
@DecimalMin(value = "-180.0", message = "La longitude doit être comprise entre -180 et 180")
@DecimalMax(value = "180.0", message = "La longitude doit être comprise entre -180 et 180")
private Double longitude;
/**
* Adresse complète
*/
@Size(max = 300, message = "L'adresse ne peut pas dépasser 300 caractères")
private String adresseComplete;
/**
* Ville
*/
@Size(max = 100, message = "La ville ne peut pas dépasser 100 caractères")
private String ville;
/**
* Région/Province
*/
@Size(max = 100, message = "La région ne peut pas dépasser 100 caractères")
private String region;
/**
* Pays
*/
@Size(max = 100, message = "Le pays ne peut pas dépasser 100 caractères")
private String pays;
/**
* Code postal
*/
@Size(max = 20, message = "Le code postal ne peut pas dépasser 20 caractères")
private String codePostal;
/**
* Précision de la localisation en mètres
*/
@Min(value = 0, message = "La précision doit être positive")
private Double precision;
/**
* Indique si la localisation est approximative
*/
@Builder.Default
private Boolean estApproximative = false;
}

View File

@@ -0,0 +1,98 @@
package dev.lions.unionflow.server.api.dto.solidarite;
import jakarta.validation.constraints.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.Builder;
import java.time.LocalDateTime;
/**
* DTO pour les pièces justificatives d'une demande d'aide
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class PieceJustificativeDTO {
/**
* Identifiant unique de la pièce justificative
*/
private String id;
/**
* Nom du fichier
*/
@NotBlank(message = "Le nom du fichier est obligatoire")
@Size(max = 255, message = "Le nom du fichier ne peut pas dépasser 255 caractères")
private String nomFichier;
/**
* Type de pièce justificative
*/
@NotBlank(message = "Le type de pièce est obligatoire")
private String typePiece;
/**
* Description de la pièce
*/
@Size(max = 500, message = "La description ne peut pas dépasser 500 caractères")
private String description;
/**
* URL ou chemin d'accès au fichier
*/
@NotBlank(message = "L'URL du fichier est obligatoire")
private String urlFichier;
/**
* Type MIME du fichier
*/
private String typeMime;
/**
* Taille du fichier en octets
*/
@Min(value = 1, message = "La taille du fichier doit être positive")
private Long tailleFichier;
/**
* Indique si la pièce est obligatoire
*/
@Builder.Default
private Boolean estObligatoire = false;
/**
* Indique si la pièce a été vérifiée
*/
@Builder.Default
private Boolean estVerifiee = false;
/**
* Date d'ajout de la pièce
*/
@Builder.Default
private LocalDateTime dateAjout = LocalDateTime.now();
/**
* Date de vérification
*/
private LocalDateTime dateVerification;
/**
* Identifiant de la personne qui a vérifié
*/
private String verificateurId;
/**
* Commentaires sur la vérification
*/
@Size(max = 500, message = "Les commentaires ne peuvent pas dépasser 500 caractères")
private String commentairesVerification;
}

View File

@@ -0,0 +1,388 @@
package dev.lions.unionflow.server.api.dto.solidarite;
import dev.lions.unionflow.server.api.enums.solidarite.TypeAide;
import jakarta.validation.constraints.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.Builder;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
/**
* DTO pour les propositions d'aide dans le système de solidarité
*
* Ce DTO représente une proposition d'aide faite par un membre pour aider
* soit une demande spécifique, soit de manière générale.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class PropositionAideDTO {
// === IDENTIFICATION ===
/**
* Identifiant unique de la proposition d'aide
*/
private String id;
/**
* Numéro de référence de la proposition (généré automatiquement)
*/
@Pattern(regexp = "^PA-\\d{4}-\\d{6}$", message = "Le numéro de référence doit suivre le format PA-YYYY-NNNNNN")
private String numeroReference;
// === INFORMATIONS DE BASE ===
/**
* Type d'aide proposée
*/
@NotNull(message = "Le type d'aide proposée est obligatoire")
private TypeAide typeAide;
/**
* Titre de la proposition
*/
@NotBlank(message = "Le titre est obligatoire")
@Size(min = 10, max = 100, message = "Le titre doit contenir entre 10 et 100 caractères")
private String titre;
/**
* Description détaillée de l'aide proposée
*/
@NotBlank(message = "La description est obligatoire")
@Size(min = 20, max = 1000, message = "La description doit contenir entre 20 et 1000 caractères")
private String description;
/**
* Conditions ou critères pour bénéficier de l'aide
*/
@Size(max = 500, message = "Les conditions ne peuvent pas dépasser 500 caractères")
private String conditions;
// === MONTANT ET CAPACITÉ ===
/**
* Montant maximum que le proposant peut offrir (si applicable)
*/
@DecimalMin(value = "0.0", inclusive = false, message = "Le montant doit être positif")
@DecimalMax(value = "1000000.0", message = "Le montant ne peut pas dépasser 1 000 000 FCFA")
private Double montantMaximum;
/**
* Nombre maximum de bénéficiaires
*/
@Min(value = 1, message = "Le nombre de bénéficiaires doit être au moins 1")
@Max(value = 100, message = "Le nombre de bénéficiaires ne peut pas dépasser 100")
@Builder.Default
private Integer nombreMaxBeneficiaires = 1;
/**
* Devise du montant
*/
@Builder.Default
private String devise = "FCFA";
// === ACTEURS ===
/**
* Identifiant du proposant
*/
@NotBlank(message = "L'identifiant du proposant est obligatoire")
private String proposantId;
/**
* Nom complet du proposant
*/
private String proposantNom;
/**
* Identifiant de l'organisation du proposant
*/
@NotBlank(message = "L'identifiant de l'organisation est obligatoire")
private String organisationId;
/**
* Identifiant de la demande d'aide liée (si proposition spécifique)
*/
private String demandeAideId;
// === STATUT ET DISPONIBILITÉ ===
/**
* Statut de la proposition
*/
@NotNull(message = "Le statut est obligatoire")
@Builder.Default
private StatutProposition statut = StatutProposition.ACTIVE;
/**
* Indique si la proposition est disponible
*/
@Builder.Default
private Boolean estDisponible = true;
/**
* Indique si la proposition est récurrente
*/
@Builder.Default
private Boolean estRecurrente = false;
/**
* Fréquence de récurrence (si applicable)
*/
private String frequenceRecurrence;
// === DATES ET DÉLAIS ===
/**
* Date de création de la proposition
*/
@NotNull(message = "La date de création est obligatoire")
@Builder.Default
private LocalDateTime dateCreation = LocalDateTime.now();
/**
* Date d'expiration de la proposition
*/
private LocalDateTime dateExpiration;
/**
* Date de dernière modification
*/
@Builder.Default
private LocalDateTime dateModification = LocalDateTime.now();
/**
* Délai de réponse souhaité en heures
*/
@Min(value = 1, message = "Le délai de réponse doit être au moins 1 heure")
@Max(value = 8760, message = "Le délai de réponse ne peut pas dépasser 1 an")
@Builder.Default
private Integer delaiReponseHeures = 72;
// === CRITÈRES ET PRÉFÉRENCES ===
/**
* Critères de sélection des bénéficiaires
*/
private List<CritereSelectionDTO> criteresSelection;
/**
* Zones géographiques couvertes
*/
private List<String> zonesGeographiques;
/**
* Groupes cibles (âge, situation, etc.)
*/
private List<String> groupesCibles;
/**
* Compétences ou ressources disponibles
*/
private List<String> competencesRessources;
// === CONTACT ET DISPONIBILITÉ ===
/**
* Informations de contact préférées
*/
private ContactProposantDTO contactProposant;
/**
* Créneaux de disponibilité
*/
private List<CreneauDisponibiliteDTO> creneauxDisponibilite;
/**
* Mode de contact préféré
*/
private String modeContactPrefere;
// === HISTORIQUE ET SUIVI ===
/**
* Nombre de demandes traitées avec cette proposition
*/
@Builder.Default
private Integer nombreDemandesTraitees = 0;
/**
* Nombre de bénéficiaires aidés
*/
@Builder.Default
private Integer nombreBeneficiairesAides = 0;
/**
* Montant total versé
*/
@Builder.Default
private Double montantTotalVerse = 0.0;
/**
* Note moyenne des bénéficiaires
*/
@DecimalMin(value = "0.0", message = "La note doit être positive")
@DecimalMax(value = "5.0", message = "La note ne peut pas dépasser 5")
private Double noteMoyenne;
/**
* Nombre d'évaluations reçues
*/
@Builder.Default
private Integer nombreEvaluations = 0;
// === MÉTADONNÉES ===
/**
* Tags pour catégorisation
*/
private List<String> tags;
/**
* Données personnalisées
*/
private Map<String, Object> donneesPersonnalisees;
/**
* Indique si la proposition est mise en avant
*/
@Builder.Default
private Boolean estMiseEnAvant = false;
/**
* Score de pertinence calculé automatiquement
*/
private Double scorePertinence;
/**
* Nombre de vues de la proposition
*/
@Builder.Default
private Integer nombreVues = 0;
/**
* Nombre de candidatures reçues
*/
@Builder.Default
private Integer nombreCandidatures = 0;
// === MÉTHODES UTILITAIRES ===
/**
* Vérifie si la proposition est active et disponible
*/
public boolean isActiveEtDisponible() {
return statut == StatutProposition.ACTIVE && estDisponible && !isExpiree();
}
/**
* Vérifie si la proposition est expirée
*/
public boolean isExpiree() {
return dateExpiration != null && LocalDateTime.now().isAfter(dateExpiration);
}
/**
* Vérifie si la proposition peut encore accepter des bénéficiaires
*/
public boolean peutAccepterBeneficiaires() {
return isActiveEtDisponible() && nombreBeneficiairesAides < nombreMaxBeneficiaires;
}
/**
* Calcule le pourcentage de capacité utilisée
*/
public double getPourcentageCapaciteUtilisee() {
if (nombreMaxBeneficiaires == 0) return 100.0;
return (nombreBeneficiairesAides * 100.0) / nombreMaxBeneficiaires;
}
/**
* Retourne le nombre de places restantes
*/
public int getPlacesRestantes() {
return Math.max(0, nombreMaxBeneficiaires - nombreBeneficiairesAides);
}
/**
* Vérifie si la proposition correspond à un type d'aide
*/
public boolean correspondAuType(TypeAide type) {
return typeAide == type ||
(typeAide.getCategorie().equals(type.getCategorie()) && typeAide != TypeAide.AUTRE);
}
/**
* Calcule le score de compatibilité avec une demande
*/
public double getScoreCompatibilite(DemandeAideDTO demande) {
double score = 0.0;
// Correspondance exacte du type
if (typeAide == demande.getTypeAide()) {
score += 50.0;
} else if (typeAide.getCategorie().equals(demande.getTypeAide().getCategorie())) {
score += 30.0;
}
// Montant compatible
if (montantMaximum != null && demande.getMontantDemande() != null) {
if (demande.getMontantDemande() <= montantMaximum) {
score += 20.0;
} else {
score -= 10.0;
}
}
// Disponibilité
if (peutAccepterBeneficiaires()) {
score += 15.0;
}
// Réputation
if (noteMoyenne != null && noteMoyenne >= 4.0) {
score += 10.0;
}
// Récence
long joursDepuisCreation = java.time.Duration.between(dateCreation, LocalDateTime.now()).toDays();
if (joursDepuisCreation <= 30) {
score += 5.0;
}
return Math.min(100.0, Math.max(0.0, score));
}
/**
* Énumération des statuts de proposition
*/
public enum StatutProposition {
BROUILLON("Brouillon"),
ACTIVE("Active"),
SUSPENDUE("Suspendue"),
EXPIREE("Expirée"),
TERMINEE("Terminée"),
ANNULEE("Annulée");
private final String libelle;
StatutProposition(String libelle) {
this.libelle = libelle;
}
public String getLibelle() {
return libelle;
}
}
}

View File

@@ -0,0 +1,233 @@
package dev.lions.unionflow.server.api.enums.analytics;
/**
* Énumération des formats d'export disponibles pour les rapports et données analytics
*
* Cette énumération définit les différents formats dans lesquels les données
* peuvent être exportées depuis l'application UnionFlow.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
public enum FormatExport {
// === FORMATS DOCUMENTS ===
PDF("PDF", "pdf", "application/pdf", "Portable Document Format", true, true),
WORD("Word", "docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "Microsoft Word", true, false),
// === FORMATS TABLEURS ===
EXCEL("Excel", "xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "Microsoft Excel", true, true),
CSV("CSV", "csv", "text/csv", "Comma Separated Values", false, true),
// === FORMATS DONNÉES ===
JSON("JSON", "json", "application/json", "JavaScript Object Notation", false, true),
XML("XML", "xml", "application/xml", "eXtensible Markup Language", false, false),
// === FORMATS IMAGES ===
PNG("PNG", "png", "image/png", "Portable Network Graphics", true, false),
JPEG("JPEG", "jpg", "image/jpeg", "Joint Photographic Experts Group", true, false),
SVG("SVG", "svg", "image/svg+xml", "Scalable Vector Graphics", true, false),
// === FORMATS SPÉCIALISÉS ===
POWERPOINT("PowerPoint", "pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation", "Microsoft PowerPoint", true, false),
HTML("HTML", "html", "text/html", "HyperText Markup Language", true, false);
private final String libelle;
private final String extension;
private final String mimeType;
private final String description;
private final boolean supporteGraphiques;
private final boolean supporteGrandesQuantitesDonnees;
/**
* Constructeur de l'énumération FormatExport
*
* @param libelle Le libellé affiché à l'utilisateur
* @param extension L'extension de fichier
* @param mimeType Le type MIME du format
* @param description La description du format
* @param supporteGraphiques true si le format supporte les graphiques
* @param supporteGrandesQuantitesDonnees true si le format supporte de grandes quantités de données
*/
FormatExport(String libelle, String extension, String mimeType, String description,
boolean supporteGraphiques, boolean supporteGrandesQuantitesDonnees) {
this.libelle = libelle;
this.extension = extension;
this.mimeType = mimeType;
this.description = description;
this.supporteGraphiques = supporteGraphiques;
this.supporteGrandesQuantitesDonnees = supporteGrandesQuantitesDonnees;
}
/**
* Retourne le libellé du format
*
* @return Le libellé affiché à l'utilisateur
*/
public String getLibelle() {
return libelle;
}
/**
* Retourne l'extension de fichier
*
* @return L'extension sans le point (ex: "pdf", "xlsx")
*/
public String getExtension() {
return extension;
}
/**
* Retourne le type MIME du format
*
* @return Le type MIME complet
*/
public String getMimeType() {
return mimeType;
}
/**
* Retourne la description du format
*
* @return La description complète du format
*/
public String getDescription() {
return description;
}
/**
* Vérifie si le format supporte les graphiques
*
* @return true si le format peut inclure des graphiques
*/
public boolean supporteGraphiques() {
return supporteGraphiques;
}
/**
* Vérifie si le format supporte de grandes quantités de données
*
* @return true si le format peut gérer de gros volumes de données
*/
public boolean supporteGrandesQuantitesDonnees() {
return supporteGrandesQuantitesDonnees;
}
/**
* Vérifie si le format est adapté aux rapports exécutifs
*
* @return true si le format convient aux rapports de direction
*/
public boolean isFormatExecutif() {
return this == PDF || this == POWERPOINT || this == WORD;
}
/**
* Vérifie si le format est adapté à l'analyse de données
*
* @return true si le format convient à l'analyse de données
*/
public boolean isFormatAnalyse() {
return this == EXCEL || this == CSV || this == JSON;
}
/**
* Vérifie si le format est adapté au partage web
*
* @return true si le format convient au partage sur le web
*/
public boolean isFormatWeb() {
return this == HTML || this == PNG || this == SVG || this == JSON;
}
/**
* Retourne l'icône appropriée pour le format
*
* @return L'icône Material Design
*/
public String getIcone() {
return switch (this) {
case PDF -> "picture_as_pdf";
case WORD -> "description";
case EXCEL -> "table_chart";
case CSV -> "grid_on";
case JSON -> "code";
case XML -> "code";
case PNG, JPEG -> "image";
case SVG -> "vector_image";
case POWERPOINT -> "slideshow";
case HTML -> "web";
};
}
/**
* Retourne la couleur appropriée pour le format
*
* @return Le code couleur hexadécimal
*/
public String getCouleur() {
return switch (this) {
case PDF -> "#FF5722"; // Rouge-orange
case WORD -> "#2196F3"; // Bleu
case EXCEL -> "#4CAF50"; // Vert
case CSV -> "#607D8B"; // Bleu gris
case JSON -> "#FF9800"; // Orange
case XML -> "#795548"; // Marron
case PNG, JPEG -> "#E91E63"; // Rose
case SVG -> "#9C27B0"; // Violet
case POWERPOINT -> "#FF5722"; // Rouge-orange
case HTML -> "#00BCD4"; // Cyan
};
}
/**
* Génère un nom de fichier avec l'extension appropriée
*
* @param nomBase Le nom de base du fichier
* @return Le nom de fichier complet avec extension
*/
public String genererNomFichier(String nomBase) {
return nomBase + "." + extension;
}
/**
* Retourne la taille maximale recommandée pour ce format (en MB)
*
* @return La taille maximale en mégaoctets
*/
public int getTailleMaximaleRecommandee() {
return switch (this) {
case PDF, WORD, POWERPOINT -> 50; // 50 MB pour les documents
case EXCEL -> 100; // 100 MB pour Excel
case CSV, JSON, XML -> 200; // 200 MB pour les données
case PNG, JPEG -> 10; // 10 MB pour les images
case SVG, HTML -> 5; // 5 MB pour les formats légers
};
}
/**
* Vérifie si le format nécessite un traitement spécial
*
* @return true si le format nécessite un traitement particulier
*/
public boolean necessiteTraitementSpecial() {
return this == PDF || this == EXCEL || this == POWERPOINT || this == WORD;
}
/**
* Retourne les formats recommandés pour un type de rapport donné
*
* @param typeRapport Le type de rapport (executif, analytique, technique)
* @return Un tableau des formats recommandés
*/
public static FormatExport[] getFormatsRecommandes(String typeRapport) {
return switch (typeRapport.toLowerCase()) {
case "executif" -> new FormatExport[]{PDF, POWERPOINT, WORD};
case "analytique" -> new FormatExport[]{EXCEL, CSV, JSON, PDF};
case "technique" -> new FormatExport[]{JSON, XML, CSV, HTML};
case "partage" -> new FormatExport[]{PDF, PNG, HTML};
default -> new FormatExport[]{PDF, EXCEL, CSV};
};
}
}

View File

@@ -0,0 +1,207 @@
package dev.lions.unionflow.server.api.enums.analytics;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
/**
* Énumération des périodes d'analyse disponibles pour les métriques et rapports
*
* Cette énumération définit les différentes périodes temporelles qui peuvent être
* utilisées pour analyser les données et générer des rapports.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
public enum PeriodeAnalyse {
// === PÉRIODES COURTES ===
AUJOURD_HUI("Aujourd'hui", "today", 1, ChronoUnit.DAYS),
HIER("Hier", "yesterday", 1, ChronoUnit.DAYS),
CETTE_SEMAINE("Cette semaine", "this_week", 7, ChronoUnit.DAYS),
SEMAINE_DERNIERE("Semaine dernière", "last_week", 7, ChronoUnit.DAYS),
// === PÉRIODES MENSUELLES ===
CE_MOIS("Ce mois", "this_month", 1, ChronoUnit.MONTHS),
MOIS_DERNIER("Mois dernier", "last_month", 1, ChronoUnit.MONTHS),
TROIS_DERNIERS_MOIS("3 derniers mois", "last_3_months", 3, ChronoUnit.MONTHS),
SIX_DERNIERS_MOIS("6 derniers mois", "last_6_months", 6, ChronoUnit.MONTHS),
// === PÉRIODES ANNUELLES ===
CETTE_ANNEE("Cette année", "this_year", 1, ChronoUnit.YEARS),
ANNEE_DERNIERE("Année dernière", "last_year", 1, ChronoUnit.YEARS),
DEUX_DERNIERES_ANNEES("2 dernières années", "last_2_years", 2, ChronoUnit.YEARS),
// === PÉRIODES PERSONNALISÉES ===
SEPT_DERNIERS_JOURS("7 derniers jours", "last_7_days", 7, ChronoUnit.DAYS),
TRENTE_DERNIERS_JOURS("30 derniers jours", "last_30_days", 30, ChronoUnit.DAYS),
QUATRE_VINGT_DIX_DERNIERS_JOURS("90 derniers jours", "last_90_days", 90, ChronoUnit.DAYS),
// === PÉRIODES SPÉCIALES ===
DEPUIS_CREATION("Depuis la création", "since_creation", 0, ChronoUnit.FOREVER),
PERIODE_PERSONNALISEE("Période personnalisée", "custom", 0, ChronoUnit.DAYS);
private final String libelle;
private final String code;
private final int duree;
private final ChronoUnit unite;
/**
* Constructeur de l'énumération PeriodeAnalyse
*
* @param libelle Le libellé affiché à l'utilisateur
* @param code Le code technique de la période
* @param duree La durée de la période
* @param unite L'unité de temps (jours, mois, années)
*/
PeriodeAnalyse(String libelle, String code, int duree, ChronoUnit unite) {
this.libelle = libelle;
this.code = code;
this.duree = duree;
this.unite = unite;
}
/**
* Retourne le libellé de la période
*
* @return Le libellé affiché à l'utilisateur
*/
public String getLibelle() {
return libelle;
}
/**
* Retourne le code technique de la période
*
* @return Le code technique
*/
public String getCode() {
return code;
}
/**
* Retourne la durée de la période
*
* @return La durée numérique
*/
public int getDuree() {
return duree;
}
/**
* Retourne l'unité de temps de la période
*
* @return L'unité de temps (ChronoUnit)
*/
public ChronoUnit getUnite() {
return unite;
}
/**
* Calcule la date de début pour cette période
*
* @return La date de début de la période
*/
public LocalDateTime getDateDebut() {
LocalDateTime maintenant = LocalDateTime.now();
return switch (this) {
case AUJOURD_HUI -> maintenant.toLocalDate().atStartOfDay();
case HIER -> maintenant.minusDays(1).toLocalDate().atStartOfDay();
case CETTE_SEMAINE -> maintenant.minusDays(maintenant.getDayOfWeek().getValue() - 1).toLocalDate().atStartOfDay();
case SEMAINE_DERNIERE -> maintenant.minusDays(maintenant.getDayOfWeek().getValue() + 6).toLocalDate().atStartOfDay();
case CE_MOIS -> maintenant.withDayOfMonth(1).toLocalDate().atStartOfDay();
case MOIS_DERNIER -> maintenant.minusMonths(1).withDayOfMonth(1).toLocalDate().atStartOfDay();
case CETTE_ANNEE -> maintenant.withDayOfYear(1).toLocalDate().atStartOfDay();
case ANNEE_DERNIERE -> maintenant.minusYears(1).withDayOfYear(1).toLocalDate().atStartOfDay();
case DEPUIS_CREATION -> LocalDateTime.of(2020, 1, 1, 0, 0); // Date de création d'UnionFlow
case PERIODE_PERSONNALISEE -> maintenant; // À définir par l'utilisateur
default -> maintenant.minus(duree, unite).toLocalDate().atStartOfDay();
};
}
/**
* Calcule la date de fin pour cette période
*
* @return La date de fin de la période
*/
public LocalDateTime getDateFin() {
LocalDateTime maintenant = LocalDateTime.now();
return switch (this) {
case AUJOURD_HUI -> maintenant.toLocalDate().atTime(23, 59, 59);
case HIER -> maintenant.minusDays(1).toLocalDate().atTime(23, 59, 59);
case CETTE_SEMAINE -> maintenant.toLocalDate().atTime(23, 59, 59);
case SEMAINE_DERNIERE -> maintenant.minusDays(maintenant.getDayOfWeek().getValue()).toLocalDate().atTime(23, 59, 59);
case CE_MOIS -> maintenant.toLocalDate().atTime(23, 59, 59);
case MOIS_DERNIER -> maintenant.withDayOfMonth(1).minusDays(1).toLocalDate().atTime(23, 59, 59);
case CETTE_ANNEE -> maintenant.toLocalDate().atTime(23, 59, 59);
case ANNEE_DERNIERE -> maintenant.withDayOfYear(1).minusDays(1).toLocalDate().atTime(23, 59, 59);
case DEPUIS_CREATION, PERIODE_PERSONNALISEE -> maintenant;
default -> maintenant.toLocalDate().atTime(23, 59, 59);
};
}
/**
* Vérifie si la période est une période courte (moins d'un mois)
*
* @return true si la période est courte
*/
public boolean isPeriodeCourte() {
return this == AUJOURD_HUI || this == HIER || this == CETTE_SEMAINE ||
this == SEMAINE_DERNIERE || this == SEPT_DERNIERS_JOURS;
}
/**
* Vérifie si la période est une période longue (plus d'un an)
*
* @return true si la période est longue
*/
public boolean isPeriodeLongue() {
return this == CETTE_ANNEE || this == ANNEE_DERNIERE ||
this == DEUX_DERNIERES_ANNEES || this == DEPUIS_CREATION;
}
/**
* Vérifie si la période est personnalisable
*
* @return true si la période peut être personnalisée
*/
public boolean isPersonnalisable() {
return this == PERIODE_PERSONNALISEE;
}
/**
* Retourne l'intervalle de regroupement recommandé pour cette période
*
* @return L'intervalle de regroupement (jour, semaine, mois)
*/
public String getIntervalleRegroupement() {
return switch (this) {
case AUJOURD_HUI, HIER -> "heure";
case CETTE_SEMAINE, SEMAINE_DERNIERE, SEPT_DERNIERS_JOURS -> "jour";
case CE_MOIS, MOIS_DERNIER, TRENTE_DERNIERS_JOURS -> "jour";
case TROIS_DERNIERS_MOIS, SIX_DERNIERS_MOIS, QUATRE_VINGT_DIX_DERNIERS_JOURS -> "semaine";
case CETTE_ANNEE, ANNEE_DERNIERE, DEUX_DERNIERES_ANNEES -> "mois";
case DEPUIS_CREATION -> "annee";
default -> "jour";
};
}
/**
* Retourne le format de date approprié pour cette période
*
* @return Le format de date (dd/MM, MM/yyyy, etc.)
*/
public String getFormatDate() {
return switch (this) {
case AUJOURD_HUI, HIER -> "HH:mm";
case CETTE_SEMAINE, SEMAINE_DERNIERE, SEPT_DERNIERS_JOURS -> "dd/MM";
case CE_MOIS, MOIS_DERNIER, TRENTE_DERNIERS_JOURS -> "dd/MM";
case TROIS_DERNIERS_MOIS, SIX_DERNIERS_MOIS, QUATRE_VINGT_DIX_DERNIERS_JOURS -> "dd/MM";
case CETTE_ANNEE, ANNEE_DERNIERE -> "MM/yyyy";
case DEUX_DERNIERES_ANNEES, DEPUIS_CREATION -> "yyyy";
default -> "dd/MM/yyyy";
};
}
}

View File

@@ -0,0 +1,187 @@
package dev.lions.unionflow.server.api.enums.analytics;
/**
* Énumération des types de métriques disponibles dans le système analytics UnionFlow
*
* Cette énumération définit les différents types de métriques qui peuvent être
* calculées et affichées dans les tableaux de bord et rapports.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
public enum TypeMetrique {
// === MÉTRIQUES MEMBRES ===
NOMBRE_MEMBRES_ACTIFS("Nombre de membres actifs", "membres", "count"),
NOMBRE_MEMBRES_INACTIFS("Nombre de membres inactifs", "membres", "count"),
TAUX_CROISSANCE_MEMBRES("Taux de croissance des membres", "membres", "percentage"),
MOYENNE_AGE_MEMBRES("Âge moyen des membres", "membres", "average"),
REPARTITION_GENRE_MEMBRES("Répartition par genre", "membres", "distribution"),
// === MÉTRIQUES FINANCIÈRES ===
TOTAL_COTISATIONS_COLLECTEES("Total des cotisations collectées", "finance", "amount"),
COTISATIONS_EN_ATTENTE("Cotisations en attente", "finance", "amount"),
TAUX_RECOUVREMENT_COTISATIONS("Taux de recouvrement", "finance", "percentage"),
MOYENNE_COTISATION_MEMBRE("Cotisation moyenne par membre", "finance", "average"),
EVOLUTION_REVENUS_MENSUELLE("Évolution des revenus mensuels", "finance", "trend"),
// === MÉTRIQUES ÉVÉNEMENTS ===
NOMBRE_EVENEMENTS_ORGANISES("Nombre d'événements organisés", "evenements", "count"),
TAUX_PARTICIPATION_EVENEMENTS("Taux de participation aux événements", "evenements", "percentage"),
MOYENNE_PARTICIPANTS_EVENEMENT("Moyenne de participants par événement", "evenements", "average"),
EVENEMENTS_ANNULES("Événements annulés", "evenements", "count"),
SATISFACTION_EVENEMENTS("Satisfaction des événements", "evenements", "rating"),
// === MÉTRIQUES SOLIDARITÉ ===
NOMBRE_DEMANDES_AIDE("Nombre de demandes d'aide", "solidarite", "count"),
MONTANT_AIDES_ACCORDEES("Montant des aides accordées", "solidarite", "amount"),
TAUX_APPROBATION_AIDES("Taux d'approbation des aides", "solidarite", "percentage"),
DELAI_TRAITEMENT_DEMANDES("Délai moyen de traitement", "solidarite", "duration"),
IMPACT_SOCIAL_MESURE("Impact social mesuré", "solidarite", "score"),
// === MÉTRIQUES ENGAGEMENT ===
TAUX_CONNEXION_MOBILE("Taux de connexion mobile", "engagement", "percentage"),
FREQUENCE_UTILISATION_APP("Fréquence d'utilisation de l'app", "engagement", "frequency"),
ACTIONS_UTILISATEUR_JOUR("Actions utilisateur par jour", "engagement", "count"),
RETENTION_UTILISATEURS("Rétention des utilisateurs", "engagement", "percentage"),
NPS_SATISFACTION("Net Promoter Score", "engagement", "score"),
// === MÉTRIQUES ORGANISATIONNELLES ===
NOMBRE_ORGANISATIONS_ACTIVES("Organisations actives", "organisation", "count"),
TAUX_CROISSANCE_ORGANISATIONS("Croissance des organisations", "organisation", "percentage"),
MOYENNE_MEMBRES_PAR_ORGANISATION("Membres moyens par organisation", "organisation", "average"),
ORGANISATIONS_PREMIUM("Organisations premium", "organisation", "count"),
CHURN_RATE_ORGANISATIONS("Taux de désabonnement", "organisation", "percentage"),
// === MÉTRIQUES TECHNIQUES ===
TEMPS_REPONSE_API("Temps de réponse API", "technique", "duration"),
TAUX_DISPONIBILITE_SYSTEME("Taux de disponibilité", "technique", "percentage"),
NOMBRE_ERREURS_SYSTEME("Nombre d'erreurs système", "technique", "count"),
UTILISATION_STOCKAGE("Utilisation du stockage", "technique", "size"),
PERFORMANCE_MOBILE("Performance mobile", "technique", "score");
private final String libelle;
private final String categorie;
private final String typeValeur;
/**
* Constructeur de l'énumération TypeMetrique
*
* @param libelle Le libellé affiché à l'utilisateur
* @param categorie La catégorie de la métrique
* @param typeValeur Le type de valeur (count, percentage, amount, etc.)
*/
TypeMetrique(String libelle, String categorie, String typeValeur) {
this.libelle = libelle;
this.categorie = categorie;
this.typeValeur = typeValeur;
}
/**
* Retourne le libellé de la métrique
*
* @return Le libellé affiché à l'utilisateur
*/
public String getLibelle() {
return libelle;
}
/**
* Retourne la catégorie de la métrique
*
* @return La catégorie (membres, finance, evenements, etc.)
*/
public String getCategorie() {
return categorie;
}
/**
* Retourne le type de valeur de la métrique
*
* @return Le type de valeur (count, percentage, amount, etc.)
*/
public String getTypeValeur() {
return typeValeur;
}
/**
* Vérifie si la métrique est de type financier
*
* @return true si la métrique concerne les finances
*/
public boolean isFinanciere() {
return "finance".equals(this.categorie);
}
/**
* Vérifie si la métrique est de type pourcentage
*
* @return true si la métrique est un pourcentage
*/
public boolean isPourcentage() {
return "percentage".equals(this.typeValeur);
}
/**
* Vérifie si la métrique est de type compteur
*
* @return true si la métrique est un compteur
*/
public boolean isCompteur() {
return "count".equals(this.typeValeur);
}
/**
* Retourne l'unité de mesure appropriée pour la métrique
*
* @return L'unité de mesure (%, XOF, jours, etc.)
*/
public String getUnite() {
return switch (this.typeValeur) {
case "percentage" -> "%";
case "amount" -> "XOF";
case "duration" -> "jours";
case "size" -> "MB";
case "frequency" -> "/jour";
case "rating", "score" -> "/10";
default -> "";
};
}
/**
* Retourne l'icône appropriée pour la métrique
*
* @return L'icône Material Design
*/
public String getIcone() {
return switch (this.categorie) {
case "membres" -> "people";
case "finance" -> "attach_money";
case "evenements" -> "event";
case "solidarite" -> "favorite";
case "engagement" -> "trending_up";
case "organisation" -> "business";
case "technique" -> "settings";
default -> "analytics";
};
}
/**
* Retourne la couleur appropriée pour la métrique
*
* @return Le code couleur hexadécimal
*/
public String getCouleur() {
return switch (this.categorie) {
case "membres" -> "#2196F3"; // Bleu
case "finance" -> "#4CAF50"; // Vert
case "evenements" -> "#FF9800"; // Orange
case "solidarite" -> "#E91E63"; // Rose
case "engagement" -> "#9C27B0"; // Violet
case "organisation" -> "#607D8B"; // Bleu gris
case "technique" -> "#795548"; // Marron
default -> "#757575"; // Gris
};
}
}

View File

@@ -0,0 +1,321 @@
package dev.lions.unionflow.server.api.enums.notification;
/**
* Énumération des canaux de notification pour Android et iOS
*
* Cette énumération définit les différents canaux de notification utilisés
* pour organiser et prioriser les notifications push dans UnionFlow.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
public enum CanalNotification {
// === CANAUX PAR PRIORITÉ ===
URGENT_CHANNEL("urgent", "Notifications urgentes", "Alertes critiques nécessitant une action immédiate",
5, true, true, true, "urgent", "#F44336"),
ERROR_CHANNEL("error", "Erreurs système", "Notifications d'erreurs et de problèmes techniques",
4, true, true, false, "error", "#F44336"),
WARNING_CHANNEL("warning", "Avertissements", "Notifications d'avertissement et d'attention",
4, true, true, false, "warning", "#FF9800"),
IMPORTANT_CHANNEL("important", "Notifications importantes", "Informations importantes à ne pas manquer",
4, true, true, false, "important", "#FF5722"),
REMINDER_CHANNEL("reminder", "Rappels", "Rappels d'événements, cotisations et échéances",
3, true, true, false, "reminder", "#2196F3"),
SUCCESS_CHANNEL("success", "Confirmations", "Notifications de succès et confirmations",
2, false, false, false, "success", "#4CAF50"),
CELEBRATION_CHANNEL("celebration", "Célébrations", "Anniversaires, félicitations et événements joyeux",
2, false, false, false, "celebration", "#FF9800"),
DEFAULT_CHANNEL("default", "Notifications générales", "Notifications d'information générale",
2, false, false, false, "info", "#2196F3"),
// === CANAUX PAR CATÉGORIE ===
EVENTS_CHANNEL("events", "Événements", "Notifications liées aux événements et activités",
3, true, false, false, "event", "#2196F3"),
PAYMENTS_CHANNEL("payments", "Paiements", "Notifications de cotisations et paiements",
4, true, true, false, "payment", "#4CAF50"),
SOLIDARITY_CHANNEL("solidarity", "Solidarité", "Notifications d'aide et de solidarité",
3, true, false, false, "help", "#E91E63"),
MEMBERS_CHANNEL("members", "Membres", "Notifications concernant les membres",
2, false, false, false, "people", "#2196F3"),
ORGANIZATION_CHANNEL("organization", "Organisation", "Annonces et informations organisationnelles",
3, true, false, false, "business", "#2196F3"),
SYSTEM_CHANNEL("system", "Système", "Notifications système et maintenance",
2, false, false, false, "settings", "#607D8B"),
MESSAGES_CHANNEL("messages", "Messages", "Messages privés et communications",
3, true, false, false, "message", "#2196F3"),
LOCATION_CHANNEL("location", "Géolocalisation", "Notifications basées sur la localisation",
2, false, false, false, "location_on", "#4CAF50");
private final String id;
private final String nom;
private final String description;
private final int importance;
private final boolean sonActive;
private final boolean vibrationActive;
private final boolean lumiereLED;
private final String typeDefaut;
private final String couleur;
/**
* Constructeur de l'énumération CanalNotification
*
* @param id L'identifiant unique du canal
* @param nom Le nom affiché du canal
* @param description La description du canal
* @param importance Le niveau d'importance (1=Min, 2=Low, 3=Default, 4=High, 5=Max)
* @param sonActive true si le son est activé par défaut
* @param vibrationActive true si la vibration est activée par défaut
* @param lumiereLED true si la lumière LED est activée par défaut
* @param typeDefaut Le type de notification par défaut pour ce canal
* @param couleur La couleur hexadécimale du canal
*/
CanalNotification(String id, String nom, String description, int importance,
boolean sonActive, boolean vibrationActive, boolean lumiereLED,
String typeDefaut, String couleur) {
this.id = id;
this.nom = nom;
this.description = description;
this.importance = importance;
this.sonActive = sonActive;
this.vibrationActive = vibrationActive;
this.lumiereLED = lumiereLED;
this.typeDefaut = typeDefaut;
this.couleur = couleur;
}
/**
* Retourne l'identifiant du canal
*
* @return L'ID unique du canal
*/
public String getId() {
return id;
}
/**
* Retourne le nom du canal
*
* @return Le nom affiché du canal
*/
public String getNom() {
return nom;
}
/**
* Retourne la description du canal
*
* @return La description détaillée du canal
*/
public String getDescription() {
return description;
}
/**
* Retourne le niveau d'importance
*
* @return Le niveau d'importance (1-5)
*/
public int getImportance() {
return importance;
}
/**
* Vérifie si le son est activé par défaut
*
* @return true si le son est activé
*/
public boolean isSonActive() {
return sonActive;
}
/**
* Vérifie si la vibration est activée par défaut
*
* @return true si la vibration est activée
*/
public boolean isVibrationActive() {
return vibrationActive;
}
/**
* Vérifie si la lumière LED est activée par défaut
*
* @return true si la lumière LED est activée
*/
public boolean isLumiereLED() {
return lumiereLED;
}
/**
* Retourne le type de notification par défaut
*
* @return Le type par défaut pour ce canal
*/
public String getTypeDefaut() {
return typeDefaut;
}
/**
* Retourne la couleur du canal
*
* @return Le code couleur hexadécimal
*/
public String getCouleur() {
return couleur;
}
/**
* Vérifie si le canal est critique
*
* @return true si le canal a une importance élevée (4-5)
*/
public boolean isCritique() {
return importance >= 4;
}
/**
* Vérifie si le canal est silencieux par défaut
*
* @return true si le canal n'émet ni son ni vibration
*/
public boolean isSilencieux() {
return !sonActive && !vibrationActive;
}
/**
* Retourne le niveau d'importance Android
*
* @return Le niveau d'importance pour Android (IMPORTANCE_MIN à IMPORTANCE_MAX)
*/
public String getImportanceAndroid() {
return switch (importance) {
case 1 -> "IMPORTANCE_MIN";
case 2 -> "IMPORTANCE_LOW";
case 3 -> "IMPORTANCE_DEFAULT";
case 4 -> "IMPORTANCE_HIGH";
case 5 -> "IMPORTANCE_MAX";
default -> "IMPORTANCE_DEFAULT";
};
}
/**
* Retourne la priorité iOS
*
* @return La priorité pour iOS (low ou high)
*/
public String getPrioriteIOS() {
return importance >= 4 ? "high" : "low";
}
/**
* Retourne le son par défaut pour le canal
*
* @return Le nom du fichier son ou "default"
*/
public String getSonDefaut() {
return switch (this) {
case URGENT_CHANNEL -> "urgent_sound.mp3";
case ERROR_CHANNEL -> "error_sound.mp3";
case WARNING_CHANNEL -> "warning_sound.mp3";
case IMPORTANT_CHANNEL -> "important_sound.mp3";
case REMINDER_CHANNEL -> "reminder_sound.mp3";
case SUCCESS_CHANNEL -> "success_sound.mp3";
case CELEBRATION_CHANNEL -> "celebration_sound.mp3";
default -> "default";
};
}
/**
* Retourne le pattern de vibration
*
* @return Le pattern de vibration en millisecondes
*/
public long[] getPatternVibration() {
return switch (this) {
case URGENT_CHANNEL -> new long[]{0, 500, 200, 500, 200, 500}; // Triple vibration
case ERROR_CHANNEL -> new long[]{0, 1000, 500, 1000}; // Double vibration longue
case WARNING_CHANNEL -> new long[]{0, 300, 200, 300}; // Double vibration courte
case IMPORTANT_CHANNEL -> new long[]{0, 500, 100, 200}; // Vibration distinctive
case REMINDER_CHANNEL -> new long[]{0, 200, 100, 200}; // Vibration douce
default -> new long[]{0, 250}; // Vibration simple
};
}
/**
* Vérifie si le canal peut être désactivé par l'utilisateur
*
* @return true si l'utilisateur peut désactiver ce canal
*/
public boolean peutEtreDesactive() {
return this != URGENT_CHANNEL && this != ERROR_CHANNEL;
}
/**
* Retourne la durée de vie par défaut des notifications de ce canal
*
* @return La durée de vie en millisecondes
*/
public long getDureeVieMs() {
return switch (this) {
case URGENT_CHANNEL -> 3600000L; // 1 heure
case ERROR_CHANNEL -> 86400000L; // 24 heures
case WARNING_CHANNEL -> 172800000L; // 48 heures
case IMPORTANT_CHANNEL -> 259200000L; // 72 heures
case REMINDER_CHANNEL -> 86400000L; // 24 heures
case SUCCESS_CHANNEL -> 172800000L; // 48 heures
case CELEBRATION_CHANNEL -> 259200000L; // 72 heures
default -> 604800000L; // 1 semaine
};
}
/**
* Trouve un canal par son ID
*
* @param id L'identifiant du canal
* @return Le canal correspondant ou DEFAULT_CHANNEL si non trouvé
*/
public static CanalNotification parId(String id) {
for (CanalNotification canal : values()) {
if (canal.getId().equals(id)) {
return canal;
}
}
return DEFAULT_CHANNEL;
}
/**
* Retourne tous les canaux critiques
*
* @return Un tableau des canaux critiques
*/
public static CanalNotification[] getCanauxCritiques() {
return new CanalNotification[]{URGENT_CHANNEL, ERROR_CHANNEL, WARNING_CHANNEL, IMPORTANT_CHANNEL};
}
/**
* Retourne tous les canaux par catégorie
*
* @return Un tableau des canaux catégoriels
*/
public static CanalNotification[] getCanauxCategories() {
return new CanalNotification[]{EVENTS_CHANNEL, PAYMENTS_CHANNEL, SOLIDARITY_CHANNEL,
MEMBERS_CHANNEL, ORGANIZATION_CHANNEL, SYSTEM_CHANNEL,
MESSAGES_CHANNEL, LOCATION_CHANNEL};
}
}

View File

@@ -0,0 +1,310 @@
package dev.lions.unionflow.server.api.enums.notification;
/**
* Énumération des statuts de notification dans UnionFlow
*
* Cette énumération définit les différents états qu'une notification peut avoir
* tout au long de son cycle de vie, de la création à l'archivage.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
public enum StatutNotification {
// === STATUTS DE CRÉATION ===
BROUILLON("Brouillon", "draft", "La notification est en cours de création",
"edit", "#9E9E9E", false, false),
PROGRAMMEE("Programmée", "scheduled", "La notification est programmée pour envoi ultérieur",
"schedule", "#FF9800", false, false),
EN_ATTENTE("En attente", "pending", "La notification est en attente d'envoi",
"hourglass_empty", "#FF9800", false, false),
// === STATUTS D'ENVOI ===
EN_COURS_ENVOI("En cours d'envoi", "sending", "La notification est en cours d'envoi",
"send", "#2196F3", false, false),
ENVOYEE("Envoyée", "sent", "La notification a été envoyée avec succès",
"check_circle", "#4CAF50", true, false),
ECHEC_ENVOI("Échec d'envoi", "failed", "L'envoi de la notification a échoué",
"error", "#F44336", true, true),
PARTIELLEMENT_ENVOYEE("Partiellement envoyée", "partial", "La notification a été envoyée à certains destinataires seulement",
"warning", "#FF9800", true, true),
// === STATUTS DE RÉCEPTION ===
RECUE("Reçue", "received", "La notification a été reçue par l'appareil",
"download_done", "#4CAF50", true, false),
AFFICHEE("Affichée", "displayed", "La notification a été affichée à l'utilisateur",
"visibility", "#2196F3", true, false),
OUVERTE("Ouverte", "opened", "L'utilisateur a ouvert la notification",
"open_in_new", "#4CAF50", true, false),
IGNOREE("Ignorée", "ignored", "La notification a été ignorée par l'utilisateur",
"visibility_off", "#9E9E9E", true, false),
// === STATUTS D'INTERACTION ===
LUE("Lue", "read", "La notification a été lue par l'utilisateur",
"mark_email_read", "#4CAF50", true, false),
NON_LUE("Non lue", "unread", "La notification n'a pas encore été lue",
"mark_email_unread", "#FF9800", true, false),
MARQUEE_IMPORTANTE("Marquée importante", "starred", "L'utilisateur a marqué la notification comme importante",
"star", "#FF9800", true, false),
ACTION_EXECUTEE("Action exécutée", "action_done", "L'utilisateur a exécuté l'action demandée",
"task_alt", "#4CAF50", true, false),
// === STATUTS DE GESTION ===
SUPPRIMEE("Supprimée", "deleted", "La notification a été supprimée par l'utilisateur",
"delete", "#F44336", false, false),
ARCHIVEE("Archivée", "archived", "La notification a été archivée",
"archive", "#9E9E9E", false, false),
EXPIREE("Expirée", "expired", "La notification a dépassé sa durée de vie",
"schedule", "#9E9E9E", false, false),
ANNULEE("Annulée", "cancelled", "L'envoi de la notification a été annulé",
"cancel", "#F44336", false, true),
// === STATUTS D'ERREUR ===
ERREUR_TECHNIQUE("Erreur technique", "error", "Une erreur technique a empêché le traitement",
"bug_report", "#F44336", false, true),
DESTINATAIRE_INVALIDE("Destinataire invalide", "invalid_recipient", "Le destinataire n'est pas valide",
"person_off", "#F44336", false, true),
TOKEN_INVALIDE("Token invalide", "invalid_token", "Le token FCM du destinataire est invalide",
"key_off", "#F44336", false, true),
QUOTA_DEPASSE("Quota dépassé", "quota_exceeded", "Le quota d'envoi a été dépassé",
"block", "#F44336", false, true);
private final String libelle;
private final String code;
private final String description;
private final String icone;
private final String couleur;
private final boolean visibleUtilisateur;
private final boolean necessiteAttention;
/**
* Constructeur de l'énumération StatutNotification
*
* @param libelle Le libellé affiché à l'utilisateur
* @param code Le code technique du statut
* @param description La description détaillée du statut
* @param icone L'icône Material Design
* @param couleur La couleur hexadécimale
* @param visibleUtilisateur true si visible à l'utilisateur final
* @param necessiteAttention true si le statut nécessite une attention particulière
*/
StatutNotification(String libelle, String code, String description, String icone, String couleur,
boolean visibleUtilisateur, boolean necessiteAttention) {
this.libelle = libelle;
this.code = code;
this.description = description;
this.icone = icone;
this.couleur = couleur;
this.visibleUtilisateur = visibleUtilisateur;
this.necessiteAttention = necessiteAttention;
}
/**
* Retourne le libellé du statut
*
* @return Le libellé affiché à l'utilisateur
*/
public String getLibelle() {
return libelle;
}
/**
* Retourne le code technique du statut
*
* @return Le code technique
*/
public String getCode() {
return code;
}
/**
* Retourne la description du statut
*
* @return La description détaillée
*/
public String getDescription() {
return description;
}
/**
* Retourne l'icône du statut
*
* @return L'icône Material Design
*/
public String getIcone() {
return icone;
}
/**
* Retourne la couleur du statut
*
* @return Le code couleur hexadécimal
*/
public String getCouleur() {
return couleur;
}
/**
* Vérifie si le statut est visible à l'utilisateur final
*
* @return true si visible à l'utilisateur
*/
public boolean isVisibleUtilisateur() {
return visibleUtilisateur;
}
/**
* Vérifie si le statut nécessite une attention particulière
*
* @return true si le statut nécessite attention
*/
public boolean isNecessiteAttention() {
return necessiteAttention;
}
/**
* Vérifie si le statut indique un succès
*
* @return true si le statut indique un succès
*/
public boolean isSucces() {
return this == ENVOYEE || this == RECUE || this == AFFICHEE ||
this == OUVERTE || this == LUE || this == ACTION_EXECUTEE;
}
/**
* Vérifie si le statut indique une erreur
*
* @return true si le statut indique une erreur
*/
public boolean isErreur() {
return this == ECHEC_ENVOI || this == ERREUR_TECHNIQUE ||
this == DESTINATAIRE_INVALIDE || this == TOKEN_INVALIDE || this == QUOTA_DEPASSE;
}
/**
* Vérifie si le statut indique un état en cours
*
* @return true si le statut indique un traitement en cours
*/
public boolean isEnCours() {
return this == PROGRAMMEE || this == EN_ATTENTE || this == EN_COURS_ENVOI;
}
/**
* Vérifie si le statut indique un état final
*
* @return true si le statut est final (pas de transition possible)
*/
public boolean isFinal() {
return this == SUPPRIMEE || this == ARCHIVEE || this == EXPIREE ||
this == ANNULEE || isErreur();
}
/**
* Vérifie si le statut permet la modification
*
* @return true si la notification peut encore être modifiée
*/
public boolean permetModification() {
return this == BROUILLON || this == PROGRAMMEE;
}
/**
* Vérifie si le statut permet l'annulation
*
* @return true si la notification peut être annulée
*/
public boolean permetAnnulation() {
return this == PROGRAMMEE || this == EN_ATTENTE;
}
/**
* Retourne la priorité d'affichage du statut
*
* @return La priorité (1=haute, 5=basse)
*/
public int getPrioriteAffichage() {
if (isErreur()) return 1;
if (necessiteAttention) return 2;
if (isEnCours()) return 3;
if (isSucces()) return 4;
return 5;
}
/**
* Retourne les statuts suivants possibles
*
* @return Un tableau des statuts de transition possibles
*/
public StatutNotification[] getStatutsSuivantsPossibles() {
return switch (this) {
case BROUILLON -> new StatutNotification[]{PROGRAMMEE, EN_ATTENTE, ANNULEE};
case PROGRAMMEE -> new StatutNotification[]{EN_ATTENTE, EN_COURS_ENVOI, ANNULEE};
case EN_ATTENTE -> new StatutNotification[]{EN_COURS_ENVOI, ECHEC_ENVOI, ANNULEE};
case EN_COURS_ENVOI -> new StatutNotification[]{ENVOYEE, PARTIELLEMENT_ENVOYEE, ECHEC_ENVOI};
case ENVOYEE -> new StatutNotification[]{RECUE, ECHEC_ENVOI};
case RECUE -> new StatutNotification[]{AFFICHEE, IGNOREE};
case AFFICHEE -> new StatutNotification[]{OUVERTE, LUE, NON_LUE, IGNOREE};
case OUVERTE -> new StatutNotification[]{LUE, ACTION_EXECUTEE, MARQUEE_IMPORTANTE};
case NON_LUE -> new StatutNotification[]{LUE, OUVERTE, SUPPRIMEE, ARCHIVEE};
case LUE -> new StatutNotification[]{ACTION_EXECUTEE, MARQUEE_IMPORTANTE, SUPPRIMEE, ARCHIVEE};
default -> new StatutNotification[]{};
};
}
/**
* Trouve un statut par son code
*
* @param code Le code du statut
* @return Le statut correspondant ou null si non trouvé
*/
public static StatutNotification parCode(String code) {
for (StatutNotification statut : values()) {
if (statut.getCode().equals(code)) {
return statut;
}
}
return null;
}
/**
* Retourne tous les statuts visibles à l'utilisateur
*
* @return Un tableau des statuts visibles
*/
public static StatutNotification[] getStatutsVisibles() {
return java.util.Arrays.stream(values())
.filter(StatutNotification::isVisibleUtilisateur)
.toArray(StatutNotification[]::new);
}
/**
* Retourne tous les statuts d'erreur
*
* @return Un tableau des statuts d'erreur
*/
public static StatutNotification[] getStatutsErreur() {
return java.util.Arrays.stream(values())
.filter(StatutNotification::isErreur)
.toArray(StatutNotification[]::new);
}
}

View File

@@ -0,0 +1,261 @@
package dev.lions.unionflow.server.api.enums.notification;
/**
* Énumération des types de notifications disponibles dans UnionFlow
*
* Cette énumération définit les différents types de notifications qui peuvent être
* envoyées aux utilisateurs de l'application UnionFlow.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
public enum TypeNotification {
// === NOTIFICATIONS ÉVÉNEMENTS ===
NOUVEL_EVENEMENT("Nouvel événement", "evenements", "info", "event", "#FF9800", true, true),
RAPPEL_EVENEMENT("Rappel d'événement", "evenements", "reminder", "schedule", "#2196F3", true, true),
EVENEMENT_ANNULE("Événement annulé", "evenements", "warning", "event_busy", "#F44336", true, true),
EVENEMENT_MODIFIE("Événement modifié", "evenements", "info", "edit", "#FF9800", true, false),
INSCRIPTION_CONFIRMEE("Inscription confirmée", "evenements", "success", "check_circle", "#4CAF50", true, false),
INSCRIPTION_REFUSEE("Inscription refusée", "evenements", "error", "cancel", "#F44336", true, false),
LISTE_ATTENTE("Mis en liste d'attente", "evenements", "info", "hourglass_empty", "#FF9800", true, false),
// === NOTIFICATIONS COTISATIONS ===
COTISATION_DUE("Cotisation due", "cotisations", "reminder", "payment", "#FF5722", true, true),
COTISATION_PAYEE("Cotisation payée", "cotisations", "success", "paid", "#4CAF50", true, false),
COTISATION_RETARD("Cotisation en retard", "cotisations", "warning", "schedule", "#F44336", true, true),
RAPPEL_COTISATION("Rappel de cotisation", "cotisations", "reminder", "notifications", "#FF9800", true, true),
PAIEMENT_CONFIRME("Paiement confirmé", "cotisations", "success", "check_circle", "#4CAF50", true, false),
PAIEMENT_ECHOUE("Paiement échoué", "cotisations", "error", "error", "#F44336", true, true),
// === NOTIFICATIONS SOLIDARITÉ ===
NOUVELLE_DEMANDE_AIDE("Nouvelle demande d'aide", "solidarite", "info", "help", "#E91E63", false, true),
DEMANDE_AIDE_APPROUVEE("Demande d'aide approuvée", "solidarite", "success", "thumb_up", "#4CAF50", true, false),
DEMANDE_AIDE_REFUSEE("Demande d'aide refusée", "solidarite", "error", "thumb_down", "#F44336", true, false),
AIDE_DISPONIBLE("Aide disponible", "solidarite", "info", "volunteer_activism", "#E91E63", true, false),
APPEL_SOLIDARITE("Appel à la solidarité", "solidarite", "urgent", "campaign", "#E91E63", true, true),
// === NOTIFICATIONS MEMBRES ===
NOUVEAU_MEMBRE("Nouveau membre", "membres", "info", "person_add", "#2196F3", false, false),
ANNIVERSAIRE_MEMBRE("Anniversaire de membre", "membres", "celebration", "cake", "#FF9800", true, false),
MEMBRE_INACTIF("Membre inactif", "membres", "warning", "person_off", "#FF5722", false, false),
REACTIVATION_MEMBRE("Réactivation de membre", "membres", "success", "person", "#4CAF50", false, false),
// === NOTIFICATIONS ORGANISATION ===
ANNONCE_GENERALE("Annonce générale", "organisation", "info", "campaign", "#2196F3", true, true),
REUNION_PROGRAMMEE("Réunion programmée", "organisation", "info", "groups", "#2196F3", true, true),
CHANGEMENT_REGLEMENT("Changement de règlement", "organisation", "important", "gavel", "#FF5722", true, true),
ELECTION_OUVERTE("Élection ouverte", "organisation", "info", "how_to_vote", "#2196F3", true, true),
RESULTAT_ELECTION("Résultat d'élection", "organisation", "info", "poll", "#4CAF50", true, false),
// === NOTIFICATIONS SYSTÈME ===
MISE_A_JOUR_APP("Mise à jour disponible", "systeme", "info", "system_update", "#2196F3", true, false),
MAINTENANCE_PROGRAMMEE("Maintenance programmée", "systeme", "warning", "build", "#FF9800", true, true),
PROBLEME_TECHNIQUE("Problème technique", "systeme", "error", "error", "#F44336", true, true),
SAUVEGARDE_REUSSIE("Sauvegarde réussie", "systeme", "success", "backup", "#4CAF50", false, false),
// === NOTIFICATIONS PERSONNALISÉES ===
MESSAGE_PRIVE("Message privé", "messages", "info", "mail", "#2196F3", true, false),
MENTION("Mention", "messages", "info", "alternate_email", "#FF9800", true, false),
COMMENTAIRE("Nouveau commentaire", "messages", "info", "comment", "#2196F3", true, false),
// === NOTIFICATIONS GÉOLOCALISÉES ===
EVENEMENT_PROXIMITE("Événement à proximité", "geolocalisation", "info", "location_on", "#4CAF50", true, false),
MEMBRE_PROXIMITE("Membre à proximité", "geolocalisation", "info", "people", "#2196F3", true, false),
URGENCE_LOCALE("Urgence locale", "geolocalisation", "urgent", "warning", "#F44336", true, true);
private final String libelle;
private final String categorie;
private final String priorite;
private final String icone;
private final String couleur;
private final boolean visibleUtilisateur;
private final boolean activeeParDefaut;
/**
* Constructeur de l'énumération TypeNotification
*
* @param libelle Le libellé affiché à l'utilisateur
* @param categorie La catégorie de la notification
* @param priorite Le niveau de priorité (info, reminder, warning, error, success, urgent, important, celebration)
* @param icone L'icône Material Design
* @param couleur La couleur hexadécimale
* @param visibleUtilisateur true si visible dans les préférences utilisateur
* @param activeeParDefaut true si activée par défaut
*/
TypeNotification(String libelle, String categorie, String priorite, String icone, String couleur,
boolean visibleUtilisateur, boolean activeeParDefaut) {
this.libelle = libelle;
this.categorie = categorie;
this.priorite = priorite;
this.icone = icone;
this.couleur = couleur;
this.visibleUtilisateur = visibleUtilisateur;
this.activeeParDefaut = activeeParDefaut;
}
/**
* Retourne le libellé de la notification
*
* @return Le libellé affiché à l'utilisateur
*/
public String getLibelle() {
return libelle;
}
/**
* Retourne la catégorie de la notification
*
* @return La catégorie (evenements, cotisations, solidarite, etc.)
*/
public String getCategorie() {
return categorie;
}
/**
* Retourne la priorité de la notification
*
* @return Le niveau de priorité
*/
public String getPriorite() {
return priorite;
}
/**
* Retourne l'icône de la notification
*
* @return L'icône Material Design
*/
public String getIcone() {
return icone;
}
/**
* Retourne la couleur de la notification
*
* @return Le code couleur hexadécimal
*/
public String getCouleur() {
return couleur;
}
/**
* Vérifie si la notification est visible dans les préférences utilisateur
*
* @return true si visible dans les préférences
*/
public boolean isVisibleUtilisateur() {
return visibleUtilisateur;
}
/**
* Vérifie si la notification est activée par défaut
*
* @return true si activée par défaut
*/
public boolean isActiveeParDefaut() {
return activeeParDefaut;
}
/**
* Vérifie si la notification est critique (urgent ou error)
*
* @return true si la notification est critique
*/
public boolean isCritique() {
return "urgent".equals(priorite) || "error".equals(priorite);
}
/**
* Vérifie si la notification est un rappel
*
* @return true si c'est un rappel
*/
public boolean isRappel() {
return "reminder".equals(priorite);
}
/**
* Vérifie si la notification est positive (success ou celebration)
*
* @return true si la notification est positive
*/
public boolean isPositive() {
return "success".equals(priorite) || "celebration".equals(priorite);
}
/**
* Retourne le niveau de priorité numérique pour le tri
*
* @return Niveau de priorité (1=urgent, 2=error, 3=warning, 4=important, 5=reminder, 6=info, 7=success, 8=celebration)
*/
public int getNiveauPriorite() {
return switch (priorite) {
case "urgent" -> 1;
case "error" -> 2;
case "warning" -> 3;
case "important" -> 4;
case "reminder" -> 5;
case "info" -> 6;
case "success" -> 7;
case "celebration" -> 8;
default -> 6;
};
}
/**
* Retourne le délai d'expiration par défaut en heures
*
* @return Délai d'expiration en heures
*/
public int getDelaiExpirationHeures() {
return switch (priorite) {
case "urgent" -> 1; // 1 heure
case "error" -> 24; // 24 heures
case "warning" -> 48; // 48 heures
case "important" -> 72; // 72 heures
case "reminder" -> 24; // 24 heures
case "info" -> 168; // 1 semaine
case "success" -> 48; // 48 heures
case "celebration" -> 72; // 72 heures
default -> 168;
};
}
/**
* Vérifie si la notification doit vibrer
*
* @return true si la notification doit faire vibrer l'appareil
*/
public boolean doitVibrer() {
return isCritique() || isRappel();
}
/**
* Vérifie si la notification doit émettre un son
*
* @return true si la notification doit émettre un son
*/
public boolean doitEmettreSon() {
return isCritique() || isRappel() || "important".equals(priorite);
}
/**
* Retourne le canal de notification Android approprié
*
* @return L'ID du canal de notification
*/
public String getCanalNotification() {
return switch (priorite) {
case "urgent" -> "URGENT_CHANNEL";
case "error" -> "ERROR_CHANNEL";
case "warning" -> "WARNING_CHANNEL";
case "important" -> "IMPORTANT_CHANNEL";
case "reminder" -> "REMINDER_CHANNEL";
case "success" -> "SUCCESS_CHANNEL";
case "celebration" -> "CELEBRATION_CHANNEL";
default -> "DEFAULT_CHANNEL";
};
}
}

View File

@@ -0,0 +1,215 @@
package dev.lions.unionflow.server.api.enums.solidarite;
/**
* Énumération des priorités d'aide dans le système de solidarité
*
* Cette énumération définit les niveaux de priorité pour les demandes d'aide,
* permettant de prioriser le traitement selon l'urgence de la situation.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
public enum PrioriteAide {
CRITIQUE("Critique", "critical", 1, "Situation critique nécessitant une intervention immédiate",
"#F44336", "emergency", 24, true, true),
URGENTE("Urgente", "urgent", 2, "Situation urgente nécessitant une réponse rapide",
"#FF5722", "priority_high", 72, true, false),
ELEVEE("Élevée", "high", 3, "Priorité élevée, traitement dans les meilleurs délais",
"#FF9800", "keyboard_arrow_up", 168, false, false),
NORMALE("Normale", "normal", 4, "Priorité normale, traitement selon les délais standards",
"#2196F3", "remove", 336, false, false),
FAIBLE("Faible", "low", 5, "Priorité faible, traitement quand les ressources le permettent",
"#4CAF50", "keyboard_arrow_down", 720, false, false);
private final String libelle;
private final String code;
private final int niveau;
private final String description;
private final String couleur;
private final String icone;
private final int delaiTraitementHeures;
private final boolean notificationImmediate;
private final boolean escaladeAutomatique;
PrioriteAide(String libelle, String code, int niveau, String description, String couleur,
String icone, int delaiTraitementHeures, boolean notificationImmediate,
boolean escaladeAutomatique) {
this.libelle = libelle;
this.code = code;
this.niveau = niveau;
this.description = description;
this.couleur = couleur;
this.icone = icone;
this.delaiTraitementHeures = delaiTraitementHeures;
this.notificationImmediate = notificationImmediate;
this.escaladeAutomatique = escaladeAutomatique;
}
// === GETTERS ===
public String getLibelle() { return libelle; }
public String getCode() { return code; }
public int getNiveau() { return niveau; }
public String getDescription() { return description; }
public String getCouleur() { return couleur; }
public String getIcone() { return icone; }
public int getDelaiTraitementHeures() { return delaiTraitementHeures; }
public boolean isNotificationImmediate() { return notificationImmediate; }
public boolean isEscaladeAutomatique() { return escaladeAutomatique; }
// === MÉTHODES UTILITAIRES ===
/**
* Vérifie si la priorité est critique ou urgente
*/
public boolean isUrgente() {
return this == CRITIQUE || this == URGENTE;
}
/**
* Vérifie si la priorité nécessite un traitement immédiat
*/
public boolean necessiteTraitementImmediat() {
return niveau <= 2;
}
/**
* Retourne la date limite de traitement
*/
public java.time.LocalDateTime getDateLimiteTraitement() {
return java.time.LocalDateTime.now().plusHours(delaiTraitementHeures);
}
/**
* Retourne la priorité suivante (escalade)
*/
public PrioriteAide getPrioriteEscalade() {
return switch (this) {
case FAIBLE -> NORMALE;
case NORMALE -> ELEVEE;
case ELEVEE -> URGENTE;
case URGENTE -> CRITIQUE;
case CRITIQUE -> CRITIQUE; // Déjà au maximum
};
}
/**
* Détermine la priorité basée sur le type d'aide
*/
public static PrioriteAide determinerPriorite(TypeAide typeAide) {
if (typeAide.isUrgent()) {
return switch (typeAide) {
case AIDE_FINANCIERE_URGENTE, AIDE_FRAIS_MEDICAUX -> CRITIQUE;
case HEBERGEMENT_URGENCE, AIDE_ALIMENTAIRE -> URGENTE;
default -> ELEVEE;
};
}
if (typeAide.getPriorite().equals("important")) {
return ELEVEE;
}
return NORMALE;
}
/**
* Retourne les priorités urgentes
*/
public static java.util.List<PrioriteAide> getPrioritesUrgentes() {
return java.util.Arrays.stream(values())
.filter(PrioriteAide::isUrgente)
.collect(java.util.stream.Collectors.toList());
}
/**
* Retourne les priorités par niveau croissant
*/
public static java.util.List<PrioriteAide> getParNiveauCroissant() {
return java.util.Arrays.stream(values())
.sorted(java.util.Comparator.comparingInt(PrioriteAide::getNiveau))
.collect(java.util.stream.Collectors.toList());
}
/**
* Retourne les priorités par niveau décroissant
*/
public static java.util.List<PrioriteAide> getParNiveauDecroissant() {
return java.util.Arrays.stream(values())
.sorted(java.util.Comparator.comparingInt(PrioriteAide::getNiveau).reversed())
.collect(java.util.stream.Collectors.toList());
}
/**
* Trouve la priorité par code
*/
public static PrioriteAide parCode(String code) {
return java.util.Arrays.stream(values())
.filter(p -> p.getCode().equals(code))
.findFirst()
.orElse(NORMALE);
}
/**
* Calcule le score de priorité (plus bas = plus prioritaire)
*/
public double getScorePriorite() {
double score = niveau;
// Bonus pour notification immédiate
if (notificationImmediate) score -= 0.5;
// Bonus pour escalade automatique
if (escaladeAutomatique) score -= 0.3;
// Malus pour délai long
if (delaiTraitementHeures > 168) score += 0.2;
return score;
}
/**
* Vérifie si le délai de traitement est dépassé
*/
public boolean isDelaiDepasse(java.time.LocalDateTime dateCreation) {
java.time.LocalDateTime dateLimite = dateCreation.plusHours(delaiTraitementHeures);
return java.time.LocalDateTime.now().isAfter(dateLimite);
}
/**
* Calcule le pourcentage de temps écoulé
*/
public double getPourcentageTempsEcoule(java.time.LocalDateTime dateCreation) {
java.time.LocalDateTime maintenant = java.time.LocalDateTime.now();
java.time.LocalDateTime dateLimite = dateCreation.plusHours(delaiTraitementHeures);
long dureeTotal = java.time.Duration.between(dateCreation, dateLimite).toMinutes();
long dureeEcoulee = java.time.Duration.between(dateCreation, maintenant).toMinutes();
if (dureeTotal <= 0) return 100.0;
return Math.min(100.0, (dureeEcoulee * 100.0) / dureeTotal);
}
/**
* Retourne le message d'alerte selon le temps écoulé
*/
public String getMessageAlerte(java.time.LocalDateTime dateCreation) {
double pourcentage = getPourcentageTempsEcoule(dateCreation);
if (pourcentage >= 100) {
return "Délai de traitement dépassé !";
} else if (pourcentage >= 80) {
return "Délai de traitement bientôt dépassé";
} else if (pourcentage >= 60) {
return "Plus de la moitié du délai écoulé";
}
return null;
}
}

View File

@@ -1,29 +1,181 @@
package dev.lions.unionflow.server.api.enums.solidarite;
/**
* Énumération des statuts d'aide dans le système de solidarité UnionFlow
* Énumération des statuts d'aide dans le système de solidarité
*
* Cette énumération définit les différents statuts qu'une demande d'aide
* peut avoir tout au long de son cycle de vie.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-10
* @since 2025-01-16
*/
public enum StatutAide {
EN_ATTENTE("En attente"),
EN_COURS("En cours d'évaluation"),
APPROUVEE("Approuvée"),
REJETEE("Rejetée"),
EN_COURS_VERSEMENT("En cours de versement"),
VERSEE("Versée"),
ANNULEE("Annulée"),
SUSPENDUE("Suspendue");
private final String libelle;
// === STATUTS INITIAUX ===
BROUILLON("Brouillon", "draft", "La demande est en cours de rédaction", "#9E9E9E", "edit", false, false),
SOUMISE("Soumise", "submitted", "La demande a été soumise et attend validation", "#FF9800", "send", false, false),
StatutAide(String libelle) {
this.libelle = libelle;
}
// === STATUTS D'ÉVALUATION ===
EN_ATTENTE("En attente", "pending", "La demande est en attente d'évaluation", "#2196F3", "hourglass_empty", false, false),
EN_COURS_EVALUATION("En cours d'évaluation", "under_review", "La demande est en cours d'évaluation", "#FF9800", "rate_review", false, false),
INFORMATIONS_REQUISES("Informations requises", "info_required", "Des informations complémentaires sont requises", "#FF5722", "info", false, false),
public String getLibelle() {
return libelle;
}
// === STATUTS DE DÉCISION ===
APPROUVEE("Approuvée", "approved", "La demande a été approuvée", "#4CAF50", "check_circle", true, false),
APPROUVEE_PARTIELLEMENT("Approuvée partiellement", "partially_approved", "La demande a été approuvée partiellement", "#8BC34A", "check_circle_outline", true, false),
REJETEE("Rejetée", "rejected", "La demande a été rejetée", "#F44336", "cancel", true, true),
// === STATUTS DE TRAITEMENT ===
EN_COURS_TRAITEMENT("En cours de traitement", "processing", "La demande approuvée est en cours de traitement", "#9C27B0", "settings", false, false),
EN_COURS_VERSEMENT("En cours de versement", "payment_processing", "Le versement est en cours", "#3F51B5", "payment", false, false),
// === STATUTS FINAUX ===
VERSEE("Versée", "paid", "L'aide a été versée avec succès", "#4CAF50", "paid", true, false),
LIVREE("Livrée", "delivered", "L'aide matérielle a été livrée", "#4CAF50", "local_shipping", true, false),
TERMINEE("Terminée", "completed", "L'aide a été fournie avec succès", "#4CAF50", "done_all", true, false),
// === STATUTS D'EXCEPTION ===
ANNULEE("Annulée", "cancelled", "La demande a été annulée", "#9E9E9E", "cancel", true, true),
SUSPENDUE("Suspendue", "suspended", "La demande a été suspendue temporairement", "#FF5722", "pause_circle", false, false),
EXPIREE("Expirée", "expired", "La demande a expiré", "#795548", "schedule", true, true),
// === STATUTS DE SUIVI ===
EN_SUIVI("En suivi", "follow_up", "L'aide fait l'objet d'un suivi", "#607D8B", "track_changes", false, false),
CLOTUREE("Clôturée", "closed", "Le dossier d'aide est clôturé", "#9E9E9E", "folder", true, false);
private final String libelle;
private final String code;
private final String description;
private final String couleur;
private final String icone;
private final boolean estFinal;
private final boolean estEchec;
StatutAide(String libelle, String code, String description, String couleur, String icone, boolean estFinal, boolean estEchec) {
this.libelle = libelle;
this.code = code;
this.description = description;
this.couleur = couleur;
this.icone = icone;
this.estFinal = estFinal;
this.estEchec = estEchec;
}
// === GETTERS ===
public String getLibelle() { return libelle; }
public String getCode() { return code; }
public String getDescription() { return description; }
public String getCouleur() { return couleur; }
public String getIcone() { return icone; }
public boolean isEstFinal() { return estFinal; }
public boolean isEstEchec() { return estEchec; }
// === MÉTHODES UTILITAIRES ===
/**
* Vérifie si le statut indique un succès
*/
public boolean isSucces() {
return this == VERSEE || this == LIVREE || this == TERMINEE;
}
/**
* Vérifie si le statut est en cours de traitement
*/
public boolean isEnCours() {
return this == EN_COURS_EVALUATION || this == EN_COURS_TRAITEMENT || this == EN_COURS_VERSEMENT;
}
/**
* Vérifie si le statut permet la modification
*/
public boolean permetModification() {
return this == BROUILLON || this == INFORMATIONS_REQUISES;
}
/**
* Vérifie si le statut permet l'annulation
*/
public boolean permetAnnulation() {
return !estFinal && this != ANNULEE;
}
/**
* Retourne les statuts finaux
*/
public static java.util.List<StatutAide> getStatutsFinaux() {
return java.util.Arrays.stream(values())
.filter(StatutAide::isEstFinal)
.collect(java.util.stream.Collectors.toList());
}
/**
* Retourne les statuts d'échec
*/
public static java.util.List<StatutAide> getStatutsEchec() {
return java.util.Arrays.stream(values())
.filter(StatutAide::isEstEchec)
.collect(java.util.stream.Collectors.toList());
}
/**
* Retourne les statuts de succès
*/
public static java.util.List<StatutAide> getStatutsSucces() {
return java.util.Arrays.stream(values())
.filter(StatutAide::isSucces)
.collect(java.util.stream.Collectors.toList());
}
/**
* Retourne les statuts en cours
*/
public static java.util.List<StatutAide> getStatutsEnCours() {
return java.util.Arrays.stream(values())
.filter(StatutAide::isEnCours)
.collect(java.util.stream.Collectors.toList());
}
/**
* Vérifie si la transition vers un autre statut est valide
*/
public boolean peutTransitionnerVers(StatutAide nouveauStatut) {
// Règles de transition simplifiées
if (this == nouveauStatut) return false;
if (estFinal && nouveauStatut != EN_SUIVI) return false;
return switch (this) {
case BROUILLON -> nouveauStatut == SOUMISE || nouveauStatut == ANNULEE;
case SOUMISE -> nouveauStatut == EN_ATTENTE || nouveauStatut == ANNULEE;
case EN_ATTENTE -> nouveauStatut == EN_COURS_EVALUATION || nouveauStatut == ANNULEE;
case EN_COURS_EVALUATION -> nouveauStatut == APPROUVEE || nouveauStatut == APPROUVEE_PARTIELLEMENT ||
nouveauStatut == REJETEE || nouveauStatut == INFORMATIONS_REQUISES ||
nouveauStatut == SUSPENDUE;
case INFORMATIONS_REQUISES -> nouveauStatut == EN_COURS_EVALUATION || nouveauStatut == ANNULEE;
case APPROUVEE, APPROUVEE_PARTIELLEMENT -> nouveauStatut == EN_COURS_TRAITEMENT || nouveauStatut == SUSPENDUE;
case EN_COURS_TRAITEMENT -> nouveauStatut == EN_COURS_VERSEMENT || nouveauStatut == LIVREE ||
nouveauStatut == TERMINEE || nouveauStatut == SUSPENDUE;
case EN_COURS_VERSEMENT -> nouveauStatut == VERSEE || nouveauStatut == SUSPENDUE;
case SUSPENDUE -> nouveauStatut == EN_COURS_EVALUATION || nouveauStatut == ANNULEE;
default -> false;
};
}
/**
* Retourne le niveau de priorité pour l'affichage
*/
public int getNiveauPriorite() {
return switch (this) {
case INFORMATIONS_REQUISES -> 1;
case EN_COURS_EVALUATION, EN_COURS_TRAITEMENT, EN_COURS_VERSEMENT -> 2;
case APPROUVEE, APPROUVEE_PARTIELLEMENT -> 3;
case EN_ATTENTE, SOUMISE -> 4;
case SUSPENDUE -> 5;
case BROUILLON -> 6;
case EN_SUIVI -> 7;
default -> 8; // Statuts finaux
};
}
}

View File

@@ -1,30 +1,284 @@
package dev.lions.unionflow.server.api.enums.solidarite;
/**
* Énumération des types d'aide dans le système de solidarité UnionFlow
* Énumération des types d'aide disponibles dans le système de solidarité
*
* Cette énumération définit les différents types d'aide que les membres
* peuvent demander ou proposer dans le cadre du système de solidarité.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-10
* @since 2025-01-16
*/
public enum TypeAide {
AIDE_FINANCIERE("Aide Financière"),
AIDE_MEDICALE("Aide Médicale"),
AIDE_EDUCATIVE("Aide Éducative"),
AIDE_LOGEMENT("Aide au Logement"),
AIDE_ALIMENTAIRE("Aide Alimentaire"),
AIDE_JURIDIQUE("Aide Juridique"),
AIDE_PROFESSIONNELLE("Aide Professionnelle"),
AIDE_URGENCE("Aide d'Urgence"),
AUTRE("Autre");
private final String libelle;
// === AIDE FINANCIÈRE ===
AIDE_FINANCIERE_URGENTE("Aide financière urgente", "financiere", "urgent",
"Aide financière pour situation d'urgence", "emergency_fund", "#F44336",
true, true, 5000.0, 50000.0, 7),
TypeAide(String libelle) {
this.libelle = libelle;
}
PRET_SANS_INTERET("Prêt sans intérêt", "financiere", "important",
"Prêt sans intérêt entre membres", "account_balance", "#FF9800",
true, true, 10000.0, 100000.0, 30),
public String getLibelle() {
return libelle;
}
AIDE_COTISATION("Aide pour cotisation", "financiere", "normal",
"Aide pour payer les cotisations", "payment", "#2196F3",
true, false, 1000.0, 10000.0, 14),
AIDE_FRAIS_MEDICAUX("Aide frais médicaux", "financiere", "urgent",
"Aide pour frais médicaux et hospitaliers", "medical_services", "#E91E63",
true, true, 5000.0, 200000.0, 7),
AIDE_FRAIS_SCOLARITE("Aide frais de scolarité", "financiere", "important",
"Aide pour frais de scolarité des enfants", "school", "#9C27B0",
true, true, 10000.0, 100000.0, 21),
// === AIDE MATÉRIELLE ===
DON_MATERIEL("Don de matériel", "materielle", "normal",
"Don d'objets, équipements ou matériel", "inventory", "#4CAF50",
false, false, null, null, 14),
PRET_MATERIEL("Prêt de matériel", "materielle", "normal",
"Prêt temporaire d'objets ou équipements", "build", "#607D8B",
false, false, null, null, 30),
AIDE_DEMENAGEMENT("Aide déménagement", "materielle", "normal",
"Aide pour déménagement (transport, main d'œuvre)", "local_shipping", "#795548",
false, false, null, null, 7),
AIDE_TRAVAUX("Aide travaux", "materielle", "normal",
"Aide pour travaux de rénovation ou construction", "construction", "#FF5722",
false, false, null, null, 21),
// === AIDE PROFESSIONNELLE ===
AIDE_RECHERCHE_EMPLOI("Aide recherche d'emploi", "professionnelle", "important",
"Aide pour recherche d'emploi et CV", "work", "#3F51B5",
false, false, null, null, 30),
FORMATION_PROFESSIONNELLE("Formation professionnelle", "professionnelle", "normal",
"Formation et développement des compétences", "school", "#009688",
false, false, null, null, 60),
CONSEIL_JURIDIQUE("Conseil juridique", "professionnelle", "important",
"Conseil et assistance juridique", "gavel", "#8BC34A",
false, false, null, null, 14),
AIDE_CREATION_ENTREPRISE("Aide création d'entreprise", "professionnelle", "normal",
"Accompagnement création d'entreprise", "business", "#CDDC39",
false, false, null, null, 90),
// === AIDE SOCIALE ===
GARDE_ENFANTS("Garde d'enfants", "sociale", "normal",
"Garde d'enfants ponctuelle ou régulière", "child_care", "#FFC107",
false, false, null, null, 7),
AIDE_PERSONNES_AGEES("Aide personnes âgées", "sociale", "important",
"Aide et accompagnement personnes âgées", "elderly", "#FF9800",
false, false, null, null, 30),
TRANSPORT("Transport", "sociale", "normal",
"Aide au transport (covoiturage, accompagnement)", "directions_car", "#2196F3",
false, false, null, null, 7),
AIDE_ADMINISTRATIVE("Aide administrative", "sociale", "normal",
"Aide pour démarches administratives", "description", "#9E9E9E",
false, false, null, null, 14),
// === AIDE D'URGENCE ===
HEBERGEMENT_URGENCE("Hébergement d'urgence", "urgence", "urgent",
"Hébergement temporaire d'urgence", "home", "#F44336",
false, true, null, null, 7),
AIDE_ALIMENTAIRE("Aide alimentaire", "urgence", "urgent",
"Aide alimentaire d'urgence", "restaurant", "#FF5722",
false, true, null, null, 3),
AIDE_VESTIMENTAIRE("Aide vestimentaire", "urgence", "normal",
"Don de vêtements et accessoires", "checkroom", "#795548",
false, false, null, null, 14),
// === AIDE SPÉCIALISÉE ===
SOUTIEN_PSYCHOLOGIQUE("Soutien psychologique", "specialisee", "important",
"Soutien et écoute psychologique", "psychology", "#E91E63",
false, true, null, null, 30),
AIDE_NUMERIQUE("Aide numérique", "specialisee", "normal",
"Aide pour utilisation outils numériques", "computer", "#607D8B",
false, false, null, null, 14),
TRADUCTION("Traduction", "specialisee", "normal",
"Services de traduction et interprétariat", "translate", "#9C27B0",
false, false, null, null, 7),
AUTRE("Autre", "autre", "normal",
"Autre type d'aide non catégorisé", "help", "#9E9E9E",
false, false, null, null, 14);
private final String libelle;
private final String categorie;
private final String priorite;
private final String description;
private final String icone;
private final String couleur;
private final boolean necessiteMontant;
private final boolean necessiteValidation;
private final Double montantMin;
private final Double montantMax;
private final int delaiReponseJours;
TypeAide(String libelle, String categorie, String priorite, String description,
String icone, String couleur, boolean necessiteMontant, boolean necessiteValidation,
Double montantMin, Double montantMax, int delaiReponseJours) {
this.libelle = libelle;
this.categorie = categorie;
this.priorite = priorite;
this.description = description;
this.icone = icone;
this.couleur = couleur;
this.necessiteMontant = necessiteMontant;
this.necessiteValidation = necessiteValidation;
this.montantMin = montantMin;
this.montantMax = montantMax;
this.delaiReponseJours = delaiReponseJours;
}
// === GETTERS ===
public String getLibelle() { return libelle; }
public String getCategorie() { return categorie; }
public String getPriorite() { return priorite; }
public String getDescription() { return description; }
public String getIcone() { return icone; }
public String getCouleur() { return couleur; }
public boolean isNecessiteMontant() { return necessiteMontant; }
public boolean isNecessiteValidation() { return necessiteValidation; }
public Double getMontantMin() { return montantMin; }
public Double getMontantMax() { return montantMax; }
public int getDelaiReponseJours() { return delaiReponseJours; }
// === MÉTHODES UTILITAIRES ===
/**
* Vérifie si le type d'aide est urgent
*/
public boolean isUrgent() {
return "urgent".equals(priorite);
}
/**
* Vérifie si le type d'aide est financier
*/
public boolean isFinancier() {
return "financiere".equals(categorie);
}
/**
* Vérifie si le type d'aide est matériel
*/
public boolean isMateriel() {
return "materielle".equals(categorie);
}
/**
* Vérifie si le montant est dans la fourchette autorisée
*/
public boolean isMontantValide(Double montant) {
if (!necessiteMontant || montant == null) return true;
if (montantMin != null && montant < montantMin) return false;
if (montantMax != null && montant > montantMax) return false;
return true;
}
/**
* Retourne le niveau de priorité numérique
*/
public int getNiveauPriorite() {
return switch (priorite) {
case "urgent" -> 1;
case "important" -> 2;
case "normal" -> 3;
default -> 3;
};
}
/**
* Retourne la date limite de réponse
*/
public java.time.LocalDateTime getDateLimiteReponse() {
return java.time.LocalDateTime.now().plusDays(delaiReponseJours);
}
/**
* Retourne les types d'aide par catégorie
*/
public static java.util.List<TypeAide> getParCategorie(String categorie) {
return java.util.Arrays.stream(values())
.filter(type -> type.getCategorie().equals(categorie))
.collect(java.util.stream.Collectors.toList());
}
/**
* Retourne les types d'aide urgents
*/
public static java.util.List<TypeAide> getUrgents() {
return java.util.Arrays.stream(values())
.filter(TypeAide::isUrgent)
.collect(java.util.stream.Collectors.toList());
}
/**
* Retourne les types d'aide financiers
*/
public static java.util.List<TypeAide> getFinanciers() {
return java.util.Arrays.stream(values())
.filter(TypeAide::isFinancier)
.collect(java.util.stream.Collectors.toList());
}
/**
* Retourne les catégories disponibles
*/
public static java.util.Set<String> getCategories() {
return java.util.Arrays.stream(values())
.map(TypeAide::getCategorie)
.collect(java.util.stream.Collectors.toSet());
}
/**
* Retourne le libellé de la catégorie
*/
public String getLibelleCategorie() {
return switch (categorie) {
case "financiere" -> "Aide financière";
case "materielle" -> "Aide matérielle";
case "professionnelle" -> "Aide professionnelle";
case "sociale" -> "Aide sociale";
case "urgence" -> "Aide d'urgence";
case "specialisee" -> "Aide spécialisée";
case "autre" -> "Autre";
default -> categorie;
};
}
/**
* Retourne l'unité du montant si applicable
*/
public String getUniteMontant() {
return necessiteMontant ? "FCFA" : null;
}
/**
* Retourne le message de validation du montant
*/
public String getMessageValidationMontant(Double montant) {
if (!necessiteMontant) return null;
if (montant == null) return "Le montant est obligatoire";
if (montantMin != null && montant < montantMin) {
return String.format("Le montant minimum est de %.0f FCFA", montantMin);
}
if (montantMax != null && montant > montantMax) {
return String.format("Le montant maximum est de %.0f FCFA", montantMax);
}
return null;
}
}