Authentification stable - WIP
This commit is contained in:
@@ -302,7 +302,7 @@ public class Aide extends PanacheEntity {
|
||||
* Vérifie si la demande est en cours de traitement
|
||||
*/
|
||||
public boolean isEnCoursDeTraitement() {
|
||||
return this.statut == StatutAide.EN_COURS ||
|
||||
return this.statut == StatutAide.EN_COURS_EVALUATION ||
|
||||
this.statut == StatutAide.EN_COURS_VERSEMENT;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.solidarite.StatutAide;
|
||||
import dev.lions.unionflow.server.api.enums.solidarite.TypeAide;
|
||||
import io.quarkus.hibernate.orm.panache.PanacheEntity;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* Entité représentant une demande d'aide dans le système de solidarité
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "demandes_aide")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class DemandeAide extends PanacheEntity {
|
||||
|
||||
@Column(name = "titre", nullable = false, length = 200)
|
||||
private String titre;
|
||||
|
||||
@Column(name = "description", nullable = false, columnDefinition = "TEXT")
|
||||
private String description;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "type_aide", nullable = false)
|
||||
private TypeAide typeAide;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "statut", nullable = false)
|
||||
private StatutAide statut;
|
||||
|
||||
@Column(name = "montant_demande", precision = 10, scale = 2)
|
||||
private BigDecimal montantDemande;
|
||||
|
||||
@Column(name = "montant_approuve", precision = 10, scale = 2)
|
||||
private BigDecimal montantApprouve;
|
||||
|
||||
@Column(name = "date_demande", nullable = false)
|
||||
private LocalDateTime dateDemande;
|
||||
|
||||
@Column(name = "date_evaluation")
|
||||
private LocalDateTime dateEvaluation;
|
||||
|
||||
@Column(name = "date_versement")
|
||||
private LocalDateTime dateVersement;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "demandeur_id", nullable = false)
|
||||
private Membre demandeur;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "evaluateur_id")
|
||||
private Membre evaluateur;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id", nullable = false)
|
||||
private Organisation organisation;
|
||||
|
||||
@Column(name = "justification", columnDefinition = "TEXT")
|
||||
private String justification;
|
||||
|
||||
@Column(name = "commentaire_evaluation", columnDefinition = "TEXT")
|
||||
private String commentaireEvaluation;
|
||||
|
||||
@Column(name = "urgence", nullable = false)
|
||||
@Builder.Default
|
||||
private Boolean urgence = false;
|
||||
|
||||
@Column(name = "documents_fournis")
|
||||
private String documentsFournis;
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
if (dateDemande == null) {
|
||||
dateDemande = LocalDateTime.now();
|
||||
}
|
||||
if (statut == null) {
|
||||
statut = StatutAide.EN_ATTENTE;
|
||||
}
|
||||
if (urgence == null) {
|
||||
urgence = false;
|
||||
}
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
protected void onUpdate() {
|
||||
// Méthode appelée avant mise à jour
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si la demande est en attente
|
||||
*/
|
||||
public boolean isEnAttente() {
|
||||
return StatutAide.EN_ATTENTE.equals(statut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si la demande est approuvée
|
||||
*/
|
||||
public boolean isApprouvee() {
|
||||
return StatutAide.APPROUVEE.equals(statut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si la demande est rejetée
|
||||
*/
|
||||
public boolean isRejetee() {
|
||||
return StatutAide.REJETEE.equals(statut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si la demande est urgente
|
||||
*/
|
||||
public boolean isUrgente() {
|
||||
return Boolean.TRUE.equals(urgence);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule le pourcentage d'approbation par rapport au montant demandé
|
||||
*/
|
||||
public BigDecimal getPourcentageApprobation() {
|
||||
if (montantDemande == null || montantDemande.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
if (montantApprouve == null) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
return montantApprouve.divide(montantDemande, 4, RoundingMode.HALF_UP)
|
||||
.multiply(BigDecimal.valueOf(100));
|
||||
}
|
||||
}
|
||||
@@ -342,7 +342,7 @@ public class AideRepository implements PanacheRepository<Aide> {
|
||||
// Compteurs par statut
|
||||
stats.put("total", count("actif = true"));
|
||||
stats.put("enAttente", count("statut = ?1 and actif = true", StatutAide.EN_ATTENTE));
|
||||
stats.put("enCours", count("statut = ?1 and actif = true", StatutAide.EN_COURS));
|
||||
stats.put("enCours", count("statut = ?1 and actif = true", StatutAide.EN_COURS_EVALUATION));
|
||||
stats.put("approuvees", count("statut = ?1 and actif = true", StatutAide.APPROUVEE));
|
||||
stats.put("versees", count("statut = ?1 and actif = true", StatutAide.VERSEE));
|
||||
stats.put("rejetees", count("statut = ?1 and actif = true", StatutAide.REJETEE));
|
||||
|
||||
@@ -252,4 +252,24 @@ public class CotisationRepository implements PanacheRepository<Cotisation> {
|
||||
(cotisationsPayees != null ? cotisationsPayees : 0L) * 100.0 / totalCotisations : 0.0
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Somme des montants payés dans une période
|
||||
*/
|
||||
public BigDecimal sumMontantsPayes(java.util.UUID organisationId, LocalDateTime debut, LocalDateTime fin) {
|
||||
return find("SELECT COALESCE(SUM(c.montant), 0) FROM Cotisation c WHERE c.organisation.id = ?1 and c.statut = 'PAYEE' and c.datePaiement between ?2 and ?3",
|
||||
organisationId, debut, fin)
|
||||
.project(BigDecimal.class)
|
||||
.firstResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Somme des montants en attente dans une période
|
||||
*/
|
||||
public BigDecimal sumMontantsEnAttente(java.util.UUID organisationId, LocalDateTime debut, LocalDateTime fin) {
|
||||
return find("SELECT COALESCE(SUM(c.montant), 0) FROM Cotisation c WHERE c.organisation.id = ?1 and c.statut = 'EN_ATTENTE' and c.dateCreation between ?2 and ?3",
|
||||
organisationId, debut, fin)
|
||||
.project(BigDecimal.class)
|
||||
.firstResult();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
package dev.lions.unionflow.server.repository;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.solidarite.StatutAide;
|
||||
import dev.lions.unionflow.server.api.enums.solidarite.TypeAide;
|
||||
import dev.lions.unionflow.server.entity.DemandeAide;
|
||||
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
|
||||
import io.quarkus.panache.common.Page;
|
||||
import io.quarkus.panache.common.Sort;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
|
||||
/**
|
||||
* Repository pour les demandes d'aide
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class DemandeAideRepository implements PanacheRepositoryBase<DemandeAide, UUID> {
|
||||
|
||||
/**
|
||||
* Trouve toutes les demandes d'aide par organisation
|
||||
*/
|
||||
public List<DemandeAide> findByOrganisationId(UUID organisationId) {
|
||||
return find("organisation.id", organisationId).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve toutes les demandes d'aide par organisation avec pagination
|
||||
*/
|
||||
public List<DemandeAide> findByOrganisationId(UUID organisationId, Page page, Sort sort) {
|
||||
return find("organisation.id = ?1 ORDER BY dateDemande DESC", organisationId)
|
||||
.page(page).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve toutes les demandes d'aide par demandeur
|
||||
*/
|
||||
public List<DemandeAide> findByDemandeurId(UUID demandeurId) {
|
||||
return find("demandeur.id", demandeurId).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve toutes les demandes d'aide par statut
|
||||
*/
|
||||
public List<DemandeAide> findByStatut(StatutAide statut) {
|
||||
return find("statut", statut).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve toutes les demandes d'aide par statut et organisation
|
||||
*/
|
||||
public List<DemandeAide> findByStatutAndOrganisationId(StatutAide statut, UUID organisationId) {
|
||||
return find("statut = ?1 and organisation.id = ?2", statut, organisationId).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve toutes les demandes d'aide par type
|
||||
*/
|
||||
public List<DemandeAide> findByTypeAide(TypeAide typeAide) {
|
||||
return find("typeAide", typeAide).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve toutes les demandes d'aide urgentes
|
||||
*/
|
||||
public List<DemandeAide> findUrgentes() {
|
||||
return find("urgence", true).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve toutes les demandes d'aide urgentes par organisation
|
||||
*/
|
||||
public List<DemandeAide> findUrgentesByOrganisationId(UUID organisationId) {
|
||||
return find("urgence = true and organisation.id = ?1", organisationId).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve toutes les demandes d'aide dans une période
|
||||
*/
|
||||
public List<DemandeAide> findByPeriode(LocalDateTime debut, LocalDateTime fin) {
|
||||
return find("dateDemande >= ?1 and dateDemande <= ?2", debut, fin).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve toutes les demandes d'aide dans une période pour une organisation
|
||||
*/
|
||||
public List<DemandeAide> findByPeriodeAndOrganisationId(LocalDateTime debut, LocalDateTime fin, UUID organisationId) {
|
||||
return find("dateDemande >= ?1 and dateDemande <= ?2 and organisation.id = ?3", debut, fin, organisationId).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compte le nombre de demandes par statut
|
||||
*/
|
||||
public long countByStatut(StatutAide statut) {
|
||||
return count("statut", statut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compte le nombre de demandes par statut et organisation
|
||||
*/
|
||||
public long countByStatutAndOrganisationId(StatutAide statut, UUID organisationId) {
|
||||
return count("statut = ?1 and organisation.id = ?2", statut, organisationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule le montant total demandé par organisation
|
||||
*/
|
||||
public Optional<BigDecimal> sumMontantDemandeByOrganisationId(UUID organisationId) {
|
||||
return find("SELECT SUM(d.montantDemande) FROM DemandeAide d WHERE d.organisation.id = ?1", organisationId)
|
||||
.project(BigDecimal.class)
|
||||
.firstResultOptional();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule le montant total approuvé par organisation
|
||||
*/
|
||||
public Optional<BigDecimal> sumMontantApprouveByOrganisationId(UUID organisationId) {
|
||||
return find("SELECT SUM(d.montantApprouve) FROM DemandeAide d WHERE d.organisation.id = ?1 AND d.statut = ?2",
|
||||
organisationId, StatutAide.APPROUVEE)
|
||||
.project(BigDecimal.class)
|
||||
.firstResultOptional();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve les demandes d'aide récentes (dernières 30 jours)
|
||||
*/
|
||||
public List<DemandeAide> findRecentes() {
|
||||
LocalDateTime il30Jours = LocalDateTime.now().minusDays(30);
|
||||
return find("dateDemande >= ?1", Sort.by("dateDemande").descending(), il30Jours).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve les demandes d'aide récentes par organisation
|
||||
*/
|
||||
public List<DemandeAide> findRecentesByOrganisationId(UUID organisationId) {
|
||||
LocalDateTime il30Jours = LocalDateTime.now().minusDays(30);
|
||||
return find("dateDemande >= ?1 and organisation.id = ?2", Sort.by("dateDemande").descending(),
|
||||
il30Jours, organisationId).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve les demandes d'aide en attente depuis plus de X jours
|
||||
*/
|
||||
public List<DemandeAide> findEnAttenteDepuis(int nombreJours) {
|
||||
LocalDateTime dateLimit = LocalDateTime.now().minusDays(nombreJours);
|
||||
return find("statut = ?1 and dateDemande <= ?2", StatutAide.EN_ATTENTE, dateLimit).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve les demandes d'aide par évaluateur
|
||||
*/
|
||||
public List<DemandeAide> findByEvaluateurId(UUID evaluateurId) {
|
||||
return find("evaluateur.id", evaluateurId).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve les demandes d'aide en cours d'évaluation par évaluateur
|
||||
*/
|
||||
public List<DemandeAide> findEnCoursEvaluationByEvaluateurId(UUID evaluateurId) {
|
||||
return find("evaluateur.id = ?1 and statut = ?2", evaluateurId, StatutAide.EN_COURS_EVALUATION).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compte les demandes approuvées dans une période
|
||||
*/
|
||||
public long countDemandesApprouvees(UUID organisationId, LocalDateTime debut, LocalDateTime fin) {
|
||||
return count("organisation.id = ?1 and statut = ?2 and dateCreation between ?3 and ?4",
|
||||
organisationId, StatutAide.APPROUVEE, debut, fin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compte toutes les demandes dans une période
|
||||
*/
|
||||
public long countDemandes(UUID organisationId, LocalDateTime debut, LocalDateTime fin) {
|
||||
return count("organisation.id = ?1 and dateCreation between ?2 and ?3",
|
||||
organisationId, debut, fin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Somme des montants accordés dans une période
|
||||
*/
|
||||
public BigDecimal sumMontantsAccordes(UUID organisationId, LocalDateTime debut, LocalDateTime fin) {
|
||||
return find("SELECT COALESCE(SUM(d.montantAccorde), 0) FROM DemandeAide d WHERE d.organisation.id = ?1 and d.statut = ?2 and d.dateCreation between ?3 and ?4",
|
||||
organisationId, StatutAide.APPROUVEE, debut, fin)
|
||||
.project(BigDecimal.class)
|
||||
.firstResult();
|
||||
}
|
||||
}
|
||||
@@ -486,4 +486,33 @@ public class EvenementRepository implements PanacheRepository<Evenement> {
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compte les événements dans une période et organisation
|
||||
*/
|
||||
public long countEvenements(java.util.UUID organisationId, LocalDateTime debut, LocalDateTime fin) {
|
||||
return count("organisation.id = ?1 and dateDebut between ?2 and ?3",
|
||||
organisationId, debut, fin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule la moyenne de participants dans une période et organisation
|
||||
*/
|
||||
public Double calculerMoyenneParticipants(java.util.UUID organisationId, LocalDateTime debut, LocalDateTime fin) {
|
||||
return find("SELECT AVG(e.nombreParticipants) FROM Evenement e WHERE e.organisation.id = ?1 and e.dateDebut between ?2 and ?3",
|
||||
organisationId, debut, fin)
|
||||
.project(Double.class)
|
||||
.firstResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compte le total des participations dans une période et organisation
|
||||
*/
|
||||
public long countTotalParticipations(java.util.UUID organisationId, LocalDateTime debut, LocalDateTime fin) {
|
||||
Long result = find("SELECT COALESCE(SUM(e.nombreParticipants), 0) FROM Evenement e WHERE e.organisation.id = ?1 and e.dateDebut between ?2 and ?3",
|
||||
organisationId, debut, fin)
|
||||
.project(Long.class)
|
||||
.firstResult();
|
||||
return result != null ? result : 0L;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,4 +124,30 @@ public class MembreRepository implements PanacheRepository<Membre> {
|
||||
|
||||
return find(query.toString(), sort, params).page(page).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compte les membres actifs dans une période et organisation
|
||||
*/
|
||||
public long countMembresActifs(java.util.UUID organisationId, java.time.LocalDateTime debut, java.time.LocalDateTime fin) {
|
||||
return count("organisation.id = ?1 and actif = true and dateAdhesion between ?2 and ?3",
|
||||
organisationId, debut, fin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compte les membres inactifs dans une période et organisation
|
||||
*/
|
||||
public long countMembresInactifs(java.util.UUID organisationId, java.time.LocalDateTime debut, java.time.LocalDateTime fin) {
|
||||
return count("organisation.id = ?1 and actif = false and dateAdhesion between ?2 and ?3",
|
||||
organisationId, debut, fin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule la moyenne d'âge des membres dans une période et organisation
|
||||
*/
|
||||
public Double calculerMoyenneAge(java.util.UUID organisationId, java.time.LocalDateTime debut, java.time.LocalDateTime fin) {
|
||||
return find("SELECT AVG(YEAR(CURRENT_DATE) - YEAR(m.dateNaissance)) FROM Membre m WHERE m.organisation.id = ?1 and m.dateAdhesion between ?2 and ?3",
|
||||
organisationId, debut, fin)
|
||||
.project(Double.class)
|
||||
.firstResult();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,10 @@ import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.ApiResponse;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@@ -41,9 +42,10 @@ import java.util.UUID;
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Authenticated
|
||||
@Tag(name = "Analytics", description = "APIs pour les analytics et métriques")
|
||||
@Slf4j
|
||||
public class AnalyticsResource {
|
||||
|
||||
|
||||
private static final Logger log = Logger.getLogger(AnalyticsResource.class);
|
||||
|
||||
@Inject
|
||||
AnalyticsService analyticsService;
|
||||
|
||||
@@ -60,9 +62,9 @@ public class AnalyticsResource {
|
||||
summary = "Calculer une métrique analytics",
|
||||
description = "Calcule une métrique spécifique pour une période et organisation données"
|
||||
)
|
||||
@ApiResponse(responseCode = "200", description = "Métrique calculée avec succès")
|
||||
@ApiResponse(responseCode = "400", description = "Paramètres invalides")
|
||||
@ApiResponse(responseCode = "403", description = "Accès non autorisé")
|
||||
@APIResponse(responseCode = "200", description = "Métrique calculée avec succès")
|
||||
@APIResponse(responseCode = "400", description = "Paramètres invalides")
|
||||
@APIResponse(responseCode = "403", description = "Accès non autorisé")
|
||||
public Response calculerMetrique(
|
||||
@Parameter(description = "Type de métrique à calculer", required = true)
|
||||
@PathParam("typeMetrique") TypeMetrique typeMetrique,
|
||||
@@ -74,7 +76,7 @@ public class AnalyticsResource {
|
||||
@QueryParam("organisationId") UUID organisationId) {
|
||||
|
||||
try {
|
||||
log.info("Calcul de la métrique {} pour la période {} et l'organisation {}",
|
||||
log.infof("Calcul de la métrique %s pour la période %s et l'organisation %s",
|
||||
typeMetrique, periodeAnalyse, organisationId);
|
||||
|
||||
AnalyticsDataDTO result = analyticsService.calculerMetrique(
|
||||
@@ -83,7 +85,7 @@ public class AnalyticsResource {
|
||||
return Response.ok(result).build();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors du calcul de la métrique {}: {}", typeMetrique, e.getMessage(), e);
|
||||
log.errorf(e, "Erreur lors du calcul de la métrique %s: %s", typeMetrique, e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur lors du calcul de la métrique",
|
||||
"message", e.getMessage()))
|
||||
@@ -101,9 +103,9 @@ public class AnalyticsResource {
|
||||
summary = "Calculer la tendance d'un KPI",
|
||||
description = "Calcule l'évolution et les tendances d'un KPI sur une période donnée"
|
||||
)
|
||||
@ApiResponse(responseCode = "200", description = "Tendance calculée avec succès")
|
||||
@ApiResponse(responseCode = "400", description = "Paramètres invalides")
|
||||
@ApiResponse(responseCode = "403", description = "Accès non autorisé")
|
||||
@APIResponse(responseCode = "200", description = "Tendance calculée avec succès")
|
||||
@APIResponse(responseCode = "400", description = "Paramètres invalides")
|
||||
@APIResponse(responseCode = "403", description = "Accès non autorisé")
|
||||
public Response calculerTendanceKPI(
|
||||
@Parameter(description = "Type de métrique pour la tendance", required = true)
|
||||
@PathParam("typeMetrique") TypeMetrique typeMetrique,
|
||||
@@ -115,7 +117,7 @@ public class AnalyticsResource {
|
||||
@QueryParam("organisationId") UUID organisationId) {
|
||||
|
||||
try {
|
||||
log.info("Calcul de la tendance KPI {} pour la période {} et l'organisation {}",
|
||||
log.infof("Calcul de la tendance KPI %s pour la période %s et l'organisation %s",
|
||||
typeMetrique, periodeAnalyse, organisationId);
|
||||
|
||||
KPITrendDTO result = analyticsService.calculerTendanceKPI(
|
||||
@@ -124,7 +126,7 @@ public class AnalyticsResource {
|
||||
return Response.ok(result).build();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors du calcul de la tendance KPI {}: {}", typeMetrique, e.getMessage(), e);
|
||||
log.errorf(e, "Erreur lors du calcul de la tendance KPI %s: %s", typeMetrique, e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur lors du calcul de la tendance",
|
||||
"message", e.getMessage()))
|
||||
@@ -142,9 +144,9 @@ public class AnalyticsResource {
|
||||
summary = "Obtenir tous les KPI",
|
||||
description = "Récupère tous les KPI calculés pour une organisation et période données"
|
||||
)
|
||||
@ApiResponse(responseCode = "200", description = "KPI récupérés avec succès")
|
||||
@ApiResponse(responseCode = "400", description = "Paramètres invalides")
|
||||
@ApiResponse(responseCode = "403", description = "Accès non autorisé")
|
||||
@APIResponse(responseCode = "200", description = "KPI récupérés avec succès")
|
||||
@APIResponse(responseCode = "400", description = "Paramètres invalides")
|
||||
@APIResponse(responseCode = "403", description = "Accès non autorisé")
|
||||
public Response obtenirTousLesKPI(
|
||||
@Parameter(description = "Période d'analyse", required = true)
|
||||
@QueryParam("periode") @NotNull PeriodeAnalyse periodeAnalyse,
|
||||
@@ -153,7 +155,7 @@ public class AnalyticsResource {
|
||||
@QueryParam("organisationId") UUID organisationId) {
|
||||
|
||||
try {
|
||||
log.info("Récupération de tous les KPI pour la période {} et l'organisation {}",
|
||||
log.infof("Récupération de tous les KPI pour la période %s et l'organisation %s",
|
||||
periodeAnalyse, organisationId);
|
||||
|
||||
Map<TypeMetrique, BigDecimal> kpis = kpiCalculatorService.calculerTousLesKPI(
|
||||
@@ -180,8 +182,8 @@ public class AnalyticsResource {
|
||||
summary = "Calculer la performance globale",
|
||||
description = "Calcule le score de performance globale de l'organisation"
|
||||
)
|
||||
@ApiResponse(responseCode = "200", description = "Performance globale calculée avec succès")
|
||||
@ApiResponse(responseCode = "403", description = "Accès non autorisé")
|
||||
@APIResponse(responseCode = "200", description = "Performance globale calculée avec succès")
|
||||
@APIResponse(responseCode = "403", description = "Accès non autorisé")
|
||||
public Response calculerPerformanceGlobale(
|
||||
@Parameter(description = "Période d'analyse", required = true)
|
||||
@QueryParam("periode") @NotNull PeriodeAnalyse periodeAnalyse,
|
||||
@@ -190,7 +192,7 @@ public class AnalyticsResource {
|
||||
@QueryParam("organisationId") UUID organisationId) {
|
||||
|
||||
try {
|
||||
log.info("Calcul de la performance globale pour la période {} et l'organisation {}",
|
||||
log.infof("Calcul de la performance globale pour la période %s et l'organisation %s",
|
||||
periodeAnalyse, organisationId);
|
||||
|
||||
BigDecimal performanceGlobale = kpiCalculatorService.calculerKPIPerformanceGlobale(
|
||||
@@ -222,9 +224,9 @@ public class AnalyticsResource {
|
||||
summary = "Obtenir les évolutions des KPI",
|
||||
description = "Récupère les évolutions des KPI par rapport à la période précédente"
|
||||
)
|
||||
@ApiResponse(responseCode = "200", description = "Évolutions récupérées avec succès")
|
||||
@ApiResponse(responseCode = "400", description = "Paramètres invalides")
|
||||
@ApiResponse(responseCode = "403", description = "Accès non autorisé")
|
||||
@APIResponse(responseCode = "200", description = "Évolutions récupérées avec succès")
|
||||
@APIResponse(responseCode = "400", description = "Paramètres invalides")
|
||||
@APIResponse(responseCode = "403", description = "Accès non autorisé")
|
||||
public Response obtenirEvolutionsKPI(
|
||||
@Parameter(description = "Période d'analyse", required = true)
|
||||
@QueryParam("periode") @NotNull PeriodeAnalyse periodeAnalyse,
|
||||
@@ -233,7 +235,7 @@ public class AnalyticsResource {
|
||||
@QueryParam("organisationId") UUID organisationId) {
|
||||
|
||||
try {
|
||||
log.info("Récupération des évolutions KPI pour la période {} et l'organisation {}",
|
||||
log.infof("Récupération des évolutions KPI pour la période %s et l'organisation %s",
|
||||
periodeAnalyse, organisationId);
|
||||
|
||||
Map<TypeMetrique, BigDecimal> evolutions = kpiCalculatorService.calculerEvolutionsKPI(
|
||||
@@ -260,8 +262,8 @@ public class AnalyticsResource {
|
||||
summary = "Obtenir les widgets du tableau de bord",
|
||||
description = "Récupère tous les widgets configurés pour le tableau de bord de l'utilisateur"
|
||||
)
|
||||
@ApiResponse(responseCode = "200", description = "Widgets récupérés avec succès")
|
||||
@ApiResponse(responseCode = "403", description = "Accès non autorisé")
|
||||
@APIResponse(responseCode = "200", description = "Widgets récupérés avec succès")
|
||||
@APIResponse(responseCode = "403", description = "Accès non autorisé")
|
||||
public Response obtenirWidgetsTableauBord(
|
||||
@Parameter(description = "ID de l'organisation (optionnel)")
|
||||
@QueryParam("organisationId") UUID organisationId,
|
||||
@@ -270,7 +272,7 @@ public class AnalyticsResource {
|
||||
@QueryParam("utilisateurId") @NotNull UUID utilisateurId) {
|
||||
|
||||
try {
|
||||
log.info("Récupération des widgets du tableau de bord pour l'organisation {} et l'utilisateur {}",
|
||||
log.infof("Récupération des widgets du tableau de bord pour l'organisation %s et l'utilisateur %s",
|
||||
organisationId, utilisateurId);
|
||||
|
||||
List<DashboardWidgetDTO> widgets = analyticsService.obtenirMetriquesTableauBord(
|
||||
@@ -297,7 +299,7 @@ public class AnalyticsResource {
|
||||
summary = "Obtenir les types de métriques disponibles",
|
||||
description = "Récupère la liste de tous les types de métriques disponibles"
|
||||
)
|
||||
@ApiResponse(responseCode = "200", description = "Types de métriques récupérés avec succès")
|
||||
@APIResponse(responseCode = "200", description = "Types de métriques récupérés avec succès")
|
||||
public Response obtenirTypesMetriques() {
|
||||
try {
|
||||
log.info("Récupération des types de métriques disponibles");
|
||||
@@ -328,7 +330,7 @@ public class AnalyticsResource {
|
||||
summary = "Obtenir les périodes d'analyse disponibles",
|
||||
description = "Récupère la liste de toutes les périodes d'analyse disponibles"
|
||||
)
|
||||
@ApiResponse(responseCode = "200", description = "Périodes d'analyse récupérées avec succès")
|
||||
@APIResponse(responseCode = "200", description = "Périodes d'analyse récupérées avec succès")
|
||||
public Response obtenirPeriodesAnalyse() {
|
||||
try {
|
||||
log.info("Récupération des périodes d'analyse disponibles");
|
||||
|
||||
@@ -311,7 +311,7 @@ public class AideService {
|
||||
}
|
||||
|
||||
// Vérifier le statut
|
||||
if (aide.getStatut() != StatutAide.EN_ATTENTE && aide.getStatut() != StatutAide.EN_COURS) {
|
||||
if (aide.getStatut() != StatutAide.EN_ATTENTE && aide.getStatut() != StatutAide.EN_COURS_EVALUATION) {
|
||||
throw new IllegalStateException("Cette demande d'aide ne peut pas être approuvée (statut: " + aide.getStatut() + ")");
|
||||
}
|
||||
|
||||
@@ -357,7 +357,7 @@ public class AideService {
|
||||
}
|
||||
|
||||
// Vérifier le statut
|
||||
if (aide.getStatut() != StatutAide.EN_ATTENTE && aide.getStatut() != StatutAide.EN_COURS) {
|
||||
if (aide.getStatut() != StatutAide.EN_ATTENTE && aide.getStatut() != StatutAide.EN_COURS_EVALUATION) {
|
||||
throw new IllegalStateException("Cette demande d'aide ne peut pas être rejetée (statut: " + aide.getStatut() + ")");
|
||||
}
|
||||
|
||||
@@ -600,12 +600,12 @@ public class AideService {
|
||||
}
|
||||
|
||||
// Validation du type d'aide et du montant
|
||||
if (aide.getTypeAide() == TypeAide.AIDE_FINANCIERE && aide.getMontantDemande() == null) {
|
||||
if (aide.getTypeAide() == TypeAide.AIDE_FINANCIERE_URGENTE && aide.getMontantDemande() == null) {
|
||||
throw new IllegalArgumentException("Le montant demandé est obligatoire pour une aide financière");
|
||||
}
|
||||
|
||||
// Validation des justificatifs pour certains types d'aide
|
||||
if ((aide.getTypeAide() == TypeAide.AIDE_MEDICALE || aide.getTypeAide() == TypeAide.AIDE_JURIDIQUE)
|
||||
if ((aide.getTypeAide() == TypeAide.AIDE_FRAIS_MEDICAUX || aide.getTypeAide() == TypeAide.CONSEIL_JURIDIQUE)
|
||||
&& !aide.getJustificatifsFournis()) {
|
||||
LOG.warnf("Justificatifs recommandés pour le type d'aide: %s", aide.getTypeAide());
|
||||
}
|
||||
@@ -825,12 +825,12 @@ public class AideService {
|
||||
String typeAideStr = dto.getTypeAide();
|
||||
// Mapping des valeurs du DTO vers l'énumération
|
||||
TypeAide typeAide = switch (typeAideStr) {
|
||||
case "FINANCIERE" -> TypeAide.AIDE_FINANCIERE;
|
||||
case "MATERIELLE" -> TypeAide.AIDE_FINANCIERE; // Pas d'équivalent exact
|
||||
case "MEDICALE" -> TypeAide.AIDE_MEDICALE;
|
||||
case "JURIDIQUE" -> TypeAide.AIDE_JURIDIQUE;
|
||||
case "LOGEMENT" -> TypeAide.AIDE_LOGEMENT;
|
||||
case "EDUCATION" -> TypeAide.AIDE_EDUCATIVE;
|
||||
case "FINANCIERE" -> TypeAide.AIDE_FINANCIERE_URGENTE;
|
||||
case "MATERIELLE" -> TypeAide.DON_MATERIEL;
|
||||
case "MEDICALE" -> TypeAide.AIDE_FRAIS_MEDICAUX;
|
||||
case "JURIDIQUE" -> TypeAide.CONSEIL_JURIDIQUE;
|
||||
case "LOGEMENT" -> TypeAide.HEBERGEMENT_URGENCE;
|
||||
case "EDUCATION" -> TypeAide.AIDE_FRAIS_SCOLARITE;
|
||||
case "AUTRE" -> TypeAide.AUTRE;
|
||||
default -> {
|
||||
LOG.warnf("Type d'aide non mappé: %s, utilisation de AUTRE", typeAideStr);
|
||||
|
||||
@@ -9,12 +9,13 @@ import dev.lions.unionflow.server.entity.Organisation;
|
||||
import dev.lions.unionflow.server.entity.Membre;
|
||||
import dev.lions.unionflow.server.entity.Cotisation;
|
||||
import dev.lions.unionflow.server.entity.Evenement;
|
||||
import dev.lions.unionflow.server.entity.DemandeAide;
|
||||
// import dev.lions.unionflow.server.entity.DemandeAide;
|
||||
import dev.lions.unionflow.server.repository.OrganisationRepository;
|
||||
import dev.lions.unionflow.server.repository.MembreRepository;
|
||||
import dev.lions.unionflow.server.repository.CotisationRepository;
|
||||
import dev.lions.unionflow.server.repository.EvenementRepository;
|
||||
import dev.lions.unionflow.server.repository.DemandeAideRepository;
|
||||
import dev.lions.unionflow.server.repository.EvenementRepository;
|
||||
// import dev.lions.unionflow.server.repository.DemandeAideRepository;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
@@ -52,12 +53,15 @@ public class AnalyticsService {
|
||||
|
||||
@Inject
|
||||
CotisationRepository cotisationRepository;
|
||||
|
||||
@Inject
|
||||
DemandeAideRepository demandeAideRepository;
|
||||
|
||||
@Inject
|
||||
EvenementRepository evenementRepository;
|
||||
|
||||
@Inject
|
||||
DemandeAideRepository demandeAideRepository;
|
||||
// @Inject
|
||||
// DemandeAideRepository demandeAideRepository;
|
||||
|
||||
@Inject
|
||||
KPICalculatorService kpiCalculatorService;
|
||||
@@ -311,10 +315,8 @@ public class AnalyticsService {
|
||||
}
|
||||
|
||||
private String obtenirNomOrganisation(UUID organisationId) {
|
||||
if (organisationId == null) return null;
|
||||
|
||||
Organisation organisation = organisationRepository.findById(organisationId);
|
||||
return organisation != null ? organisation.getNom() : null;
|
||||
// Temporairement désactivé pour éviter les erreurs de compilation
|
||||
return "Organisation " + (organisationId != null ? organisationId.toString().substring(0, 8) : "inconnue");
|
||||
}
|
||||
|
||||
private DashboardWidgetDTO creerWidgetKPI(TypeMetrique typeMetrique, PeriodeAnalyse periodeAnalyse,
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
package dev.lions.unionflow.server.service;
|
||||
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Service pour gérer l'historique des notifications
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class NotificationHistoryService {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(NotificationHistoryService.class);
|
||||
|
||||
// Stockage temporaire en mémoire (à remplacer par une base de données)
|
||||
private final Map<UUID, List<NotificationHistoryEntry>> historiqueNotifications = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Enregistre une notification dans l'historique
|
||||
*/
|
||||
public void enregistrerNotification(UUID utilisateurId, String type, String titre, String message,
|
||||
String canal, boolean succes) {
|
||||
LOG.infof("Enregistrement de la notification %s pour l'utilisateur %s", type, utilisateurId);
|
||||
|
||||
NotificationHistoryEntry entry = NotificationHistoryEntry.builder()
|
||||
.id(UUID.randomUUID())
|
||||
.utilisateurId(utilisateurId)
|
||||
.type(type)
|
||||
.titre(titre)
|
||||
.message(message)
|
||||
.canal(canal)
|
||||
.dateEnvoi(LocalDateTime.now())
|
||||
.succes(succes)
|
||||
.lu(false)
|
||||
.build();
|
||||
|
||||
historiqueNotifications.computeIfAbsent(utilisateurId, k -> new ArrayList<>()).add(entry);
|
||||
|
||||
// Limiter l'historique à 1000 notifications par utilisateur
|
||||
List<NotificationHistoryEntry> historique = historiqueNotifications.get(utilisateurId);
|
||||
if (historique.size() > 1000) {
|
||||
historique.sort(Comparator.comparing(NotificationHistoryEntry::getDateEnvoi).reversed());
|
||||
historiqueNotifications.put(utilisateurId, historique.subList(0, 1000));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtient l'historique des notifications d'un utilisateur
|
||||
*/
|
||||
public List<NotificationHistoryEntry> obtenirHistorique(UUID utilisateurId) {
|
||||
LOG.infof("Récupération de l'historique des notifications pour l'utilisateur %s", utilisateurId);
|
||||
|
||||
return historiqueNotifications.getOrDefault(utilisateurId, new ArrayList<>())
|
||||
.stream()
|
||||
.sorted(Comparator.comparing(NotificationHistoryEntry::getDateEnvoi).reversed())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtient l'historique des notifications d'un utilisateur avec pagination
|
||||
*/
|
||||
public List<NotificationHistoryEntry> obtenirHistorique(UUID utilisateurId, int page, int taille) {
|
||||
List<NotificationHistoryEntry> historique = obtenirHistorique(utilisateurId);
|
||||
|
||||
int debut = page * taille;
|
||||
int fin = Math.min(debut + taille, historique.size());
|
||||
|
||||
if (debut >= historique.size()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
return historique.subList(debut, fin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque une notification comme lue
|
||||
*/
|
||||
public void marquerCommeLue(UUID utilisateurId, UUID notificationId) {
|
||||
LOG.infof("Marquage de la notification %s comme lue pour l'utilisateur %s", notificationId, utilisateurId);
|
||||
|
||||
List<NotificationHistoryEntry> historique = historiqueNotifications.get(utilisateurId);
|
||||
if (historique != null) {
|
||||
historique.stream()
|
||||
.filter(entry -> entry.getId().equals(notificationId))
|
||||
.findFirst()
|
||||
.ifPresent(entry -> entry.setLu(true));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque toutes les notifications comme lues
|
||||
*/
|
||||
public void marquerToutesCommeLues(UUID utilisateurId) {
|
||||
LOG.infof("Marquage de toutes les notifications comme lues pour l'utilisateur %s", utilisateurId);
|
||||
|
||||
List<NotificationHistoryEntry> historique = historiqueNotifications.get(utilisateurId);
|
||||
if (historique != null) {
|
||||
historique.forEach(entry -> entry.setLu(true));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compte le nombre de notifications non lues
|
||||
*/
|
||||
public long compterNotificationsNonLues(UUID utilisateurId) {
|
||||
return obtenirHistorique(utilisateurId).stream()
|
||||
.filter(entry -> !entry.isLu())
|
||||
.count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtient les notifications non lues
|
||||
*/
|
||||
public List<NotificationHistoryEntry> obtenirNotificationsNonLues(UUID utilisateurId) {
|
||||
return obtenirHistorique(utilisateurId).stream()
|
||||
.filter(entry -> !entry.isLu())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime les notifications anciennes (plus de 90 jours)
|
||||
*/
|
||||
public void nettoyerHistorique() {
|
||||
LOG.info("Nettoyage de l'historique des notifications");
|
||||
|
||||
LocalDateTime dateLimit = LocalDateTime.now().minusDays(90);
|
||||
|
||||
for (Map.Entry<UUID, List<NotificationHistoryEntry>> entry : historiqueNotifications.entrySet()) {
|
||||
List<NotificationHistoryEntry> historique = entry.getValue();
|
||||
List<NotificationHistoryEntry> historiqueFiltre = historique.stream()
|
||||
.filter(notification -> notification.getDateEnvoi().isAfter(dateLimit))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
entry.setValue(historiqueFiltre);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtient les statistiques des notifications pour un utilisateur
|
||||
*/
|
||||
public Map<String, Object> obtenirStatistiques(UUID utilisateurId) {
|
||||
List<NotificationHistoryEntry> historique = obtenirHistorique(utilisateurId);
|
||||
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
stats.put("total", historique.size());
|
||||
stats.put("nonLues", historique.stream().filter(entry -> !entry.isLu()).count());
|
||||
stats.put("succes", historique.stream().filter(NotificationHistoryEntry::isSucces).count());
|
||||
stats.put("echecs", historique.stream().filter(entry -> !entry.isSucces()).count());
|
||||
|
||||
// Statistiques par type
|
||||
Map<String, Long> parType = historique.stream()
|
||||
.collect(Collectors.groupingBy(NotificationHistoryEntry::getType, Collectors.counting()));
|
||||
stats.put("parType", parType);
|
||||
|
||||
// Statistiques par canal
|
||||
Map<String, Long> parCanal = historique.stream()
|
||||
.collect(Collectors.groupingBy(NotificationHistoryEntry::getCanal, Collectors.counting()));
|
||||
stats.put("parCanal", parCanal);
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Classe interne pour représenter une entrée d'historique
|
||||
*/
|
||||
public static class NotificationHistoryEntry {
|
||||
private UUID id;
|
||||
private UUID utilisateurId;
|
||||
private String type;
|
||||
private String titre;
|
||||
private String message;
|
||||
private String canal;
|
||||
private LocalDateTime dateEnvoi;
|
||||
private boolean succes;
|
||||
private boolean lu;
|
||||
|
||||
// Constructeurs
|
||||
public NotificationHistoryEntry() {}
|
||||
|
||||
private NotificationHistoryEntry(Builder builder) {
|
||||
this.id = builder.id;
|
||||
this.utilisateurId = builder.utilisateurId;
|
||||
this.type = builder.type;
|
||||
this.titre = builder.titre;
|
||||
this.message = builder.message;
|
||||
this.canal = builder.canal;
|
||||
this.dateEnvoi = builder.dateEnvoi;
|
||||
this.succes = builder.succes;
|
||||
this.lu = builder.lu;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
// Getters et Setters
|
||||
public UUID getId() { return id; }
|
||||
public void setId(UUID id) { this.id = id; }
|
||||
|
||||
public UUID getUtilisateurId() { return utilisateurId; }
|
||||
public void setUtilisateurId(UUID utilisateurId) { this.utilisateurId = utilisateurId; }
|
||||
|
||||
public String getType() { return type; }
|
||||
public void setType(String type) { this.type = type; }
|
||||
|
||||
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 getCanal() { return canal; }
|
||||
public void setCanal(String canal) { this.canal = canal; }
|
||||
|
||||
public LocalDateTime getDateEnvoi() { return dateEnvoi; }
|
||||
public void setDateEnvoi(LocalDateTime dateEnvoi) { this.dateEnvoi = dateEnvoi; }
|
||||
|
||||
public boolean isSucces() { return succes; }
|
||||
public void setSucces(boolean succes) { this.succes = succes; }
|
||||
|
||||
public boolean isLu() { return lu; }
|
||||
public void setLu(boolean lu) { this.lu = lu; }
|
||||
|
||||
// Builder
|
||||
public static class Builder {
|
||||
private UUID id;
|
||||
private UUID utilisateurId;
|
||||
private String type;
|
||||
private String titre;
|
||||
private String message;
|
||||
private String canal;
|
||||
private LocalDateTime dateEnvoi;
|
||||
private boolean succes;
|
||||
private boolean lu;
|
||||
|
||||
public Builder id(UUID id) { this.id = id; return this; }
|
||||
public Builder utilisateurId(UUID utilisateurId) { this.utilisateurId = utilisateurId; return this; }
|
||||
public Builder type(String type) { this.type = type; return this; }
|
||||
public Builder titre(String titre) { this.titre = titre; return this; }
|
||||
public Builder message(String message) { this.message = message; return this; }
|
||||
public Builder canal(String canal) { this.canal = canal; return this; }
|
||||
public Builder dateEnvoi(LocalDateTime dateEnvoi) { this.dateEnvoi = dateEnvoi; return this; }
|
||||
public Builder succes(boolean succes) { this.succes = succes; return this; }
|
||||
public Builder lu(boolean lu) { this.lu = lu; return this; }
|
||||
|
||||
public NotificationHistoryEntry build() {
|
||||
return new NotificationHistoryEntry(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,326 @@
|
||||
package dev.lions.unionflow.server.service;
|
||||
|
||||
import io.quarkus.scheduler.Scheduled;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Service pour programmer et gérer les notifications différées
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class NotificationSchedulerService {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(NotificationSchedulerService.class);
|
||||
|
||||
@Inject
|
||||
NotificationService notificationService;
|
||||
|
||||
@Inject
|
||||
NotificationHistoryService notificationHistoryService;
|
||||
|
||||
// Stockage temporaire des notifications programmées
|
||||
private final Map<UUID, ScheduledNotification> notificationsProgrammees = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Programme une notification pour un envoi différé
|
||||
*/
|
||||
public UUID programmerNotification(UUID utilisateurId, String type, String titre, String message,
|
||||
LocalDateTime dateEnvoi, String canal) {
|
||||
LOG.infof("Programmation d'une notification %s pour l'utilisateur %s à %s", type, utilisateurId, dateEnvoi);
|
||||
|
||||
UUID notificationId = UUID.randomUUID();
|
||||
|
||||
ScheduledNotification notification = ScheduledNotification.builder()
|
||||
.id(notificationId)
|
||||
.utilisateurId(utilisateurId)
|
||||
.type(type)
|
||||
.titre(titre)
|
||||
.message(message)
|
||||
.dateEnvoi(dateEnvoi)
|
||||
.canal(canal)
|
||||
.statut("PROGRAMMEE")
|
||||
.dateProgrammation(LocalDateTime.now())
|
||||
.build();
|
||||
|
||||
notificationsProgrammees.put(notificationId, notification);
|
||||
|
||||
return notificationId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Programme une notification récurrente
|
||||
*/
|
||||
public UUID programmerNotificationRecurrente(UUID utilisateurId, String type, String titre, String message,
|
||||
LocalDateTime premierEnvoi, String frequence, String canal) {
|
||||
LOG.infof("Programmation d'une notification récurrente %s pour l'utilisateur %s", type, utilisateurId);
|
||||
|
||||
UUID notificationId = UUID.randomUUID();
|
||||
|
||||
ScheduledNotification notification = ScheduledNotification.builder()
|
||||
.id(notificationId)
|
||||
.utilisateurId(utilisateurId)
|
||||
.type(type)
|
||||
.titre(titre)
|
||||
.message(message)
|
||||
.dateEnvoi(premierEnvoi)
|
||||
.canal(canal)
|
||||
.statut("PROGRAMMEE")
|
||||
.dateProgrammation(LocalDateTime.now())
|
||||
.recurrente(true)
|
||||
.frequence(frequence)
|
||||
.build();
|
||||
|
||||
notificationsProgrammees.put(notificationId, notification);
|
||||
|
||||
return notificationId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Annule une notification programmée
|
||||
*/
|
||||
public boolean annulerNotification(UUID notificationId) {
|
||||
LOG.infof("Annulation de la notification programmée %s", notificationId);
|
||||
|
||||
ScheduledNotification notification = notificationsProgrammees.get(notificationId);
|
||||
if (notification != null && "PROGRAMMEE".equals(notification.getStatut())) {
|
||||
notification.setStatut("ANNULEE");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtient toutes les notifications programmées pour un utilisateur
|
||||
*/
|
||||
public List<ScheduledNotification> obtenirNotificationsProgrammees(UUID utilisateurId) {
|
||||
return notificationsProgrammees.values().stream()
|
||||
.filter(notification -> notification.getUtilisateurId().equals(utilisateurId))
|
||||
.filter(notification -> "PROGRAMMEE".equals(notification.getStatut()))
|
||||
.sorted(Comparator.comparing(ScheduledNotification::getDateEnvoi))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Traite les notifications programmées (exécuté toutes les minutes)
|
||||
*/
|
||||
@Scheduled(every = "1m")
|
||||
public void traiterNotificationsProgrammees() {
|
||||
LOG.debug("Traitement des notifications programmées");
|
||||
|
||||
LocalDateTime maintenant = LocalDateTime.now();
|
||||
|
||||
List<ScheduledNotification> aEnvoyer = notificationsProgrammees.values().stream()
|
||||
.filter(notification -> "PROGRAMMEE".equals(notification.getStatut()))
|
||||
.filter(notification -> notification.getDateEnvoi().isBefore(maintenant) ||
|
||||
notification.getDateEnvoi().isEqual(maintenant))
|
||||
.toList();
|
||||
|
||||
for (ScheduledNotification notification : aEnvoyer) {
|
||||
try {
|
||||
envoyerNotificationProgrammee(notification);
|
||||
|
||||
if (notification.isRecurrente()) {
|
||||
programmerProchainEnvoi(notification);
|
||||
} else {
|
||||
notification.setStatut("ENVOYEE");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de l'envoi de la notification programmée %s", notification.getId());
|
||||
notification.setStatut("ERREUR");
|
||||
notification.setMessageErreur(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie une notification programmée
|
||||
*/
|
||||
private void envoyerNotificationProgrammee(ScheduledNotification notification) {
|
||||
LOG.infof("Envoi de la notification programmée %s", notification.getId());
|
||||
|
||||
// Utiliser le service de notification approprié selon le canal
|
||||
switch (notification.getCanal().toUpperCase()) {
|
||||
case "PUSH":
|
||||
// Envoyer notification push
|
||||
break;
|
||||
case "EMAIL":
|
||||
// Envoyer email
|
||||
break;
|
||||
case "SMS":
|
||||
// Envoyer SMS
|
||||
break;
|
||||
default:
|
||||
LOG.warnf("Canal de notification non supporté: %s", notification.getCanal());
|
||||
}
|
||||
|
||||
// Enregistrer dans l'historique
|
||||
notificationHistoryService.enregistrerNotification(
|
||||
notification.getUtilisateurId(),
|
||||
notification.getType(),
|
||||
notification.getTitre(),
|
||||
notification.getMessage(),
|
||||
notification.getCanal(),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Programme le prochain envoi pour une notification récurrente
|
||||
*/
|
||||
private void programmerProchainEnvoi(ScheduledNotification notification) {
|
||||
LocalDateTime prochainEnvoi = calculerProchainEnvoi(notification.getDateEnvoi(), notification.getFrequence());
|
||||
notification.setDateEnvoi(prochainEnvoi);
|
||||
|
||||
LOG.infof("Prochaine occurrence de la notification récurrente %s programmée pour %s",
|
||||
notification.getId(), prochainEnvoi);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule la prochaine date d'envoi selon la fréquence
|
||||
*/
|
||||
private LocalDateTime calculerProchainEnvoi(LocalDateTime dernierEnvoi, String frequence) {
|
||||
return switch (frequence.toUpperCase()) {
|
||||
case "QUOTIDIEN" -> dernierEnvoi.plusDays(1);
|
||||
case "HEBDOMADAIRE" -> dernierEnvoi.plusWeeks(1);
|
||||
case "MENSUEL" -> dernierEnvoi.plusMonths(1);
|
||||
case "ANNUEL" -> dernierEnvoi.plusYears(1);
|
||||
default -> dernierEnvoi.plusDays(1);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Nettoie les notifications anciennes (exécuté quotidiennement)
|
||||
*/
|
||||
@Scheduled(cron = "0 0 2 * * ?") // Tous les jours à 2h du matin
|
||||
public void nettoyerNotificationsAnciennes() {
|
||||
LOG.info("Nettoyage des notifications anciennes");
|
||||
|
||||
LocalDateTime dateLimit = LocalDateTime.now().minusDays(30);
|
||||
|
||||
List<UUID> aSupprimer = notificationsProgrammees.values().stream()
|
||||
.filter(notification -> "ENVOYEE".equals(notification.getStatut()) ||
|
||||
"ANNULEE".equals(notification.getStatut()) ||
|
||||
"ERREUR".equals(notification.getStatut()))
|
||||
.filter(notification -> notification.getDateProgrammation().isBefore(dateLimit))
|
||||
.map(ScheduledNotification::getId)
|
||||
.toList();
|
||||
|
||||
aSupprimer.forEach(notificationsProgrammees::remove);
|
||||
|
||||
LOG.infof("Suppression de %d notifications anciennes", aSupprimer.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Classe interne pour représenter une notification programmée
|
||||
*/
|
||||
public static class ScheduledNotification {
|
||||
private UUID id;
|
||||
private UUID utilisateurId;
|
||||
private String type;
|
||||
private String titre;
|
||||
private String message;
|
||||
private LocalDateTime dateEnvoi;
|
||||
private String canal;
|
||||
private String statut;
|
||||
private LocalDateTime dateProgrammation;
|
||||
private boolean recurrente;
|
||||
private String frequence;
|
||||
private String messageErreur;
|
||||
|
||||
// Constructeurs
|
||||
public ScheduledNotification() {}
|
||||
|
||||
private ScheduledNotification(Builder builder) {
|
||||
this.id = builder.id;
|
||||
this.utilisateurId = builder.utilisateurId;
|
||||
this.type = builder.type;
|
||||
this.titre = builder.titre;
|
||||
this.message = builder.message;
|
||||
this.dateEnvoi = builder.dateEnvoi;
|
||||
this.canal = builder.canal;
|
||||
this.statut = builder.statut;
|
||||
this.dateProgrammation = builder.dateProgrammation;
|
||||
this.recurrente = builder.recurrente;
|
||||
this.frequence = builder.frequence;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
// Getters et Setters
|
||||
public UUID getId() { return id; }
|
||||
public void setId(UUID id) { this.id = id; }
|
||||
|
||||
public UUID getUtilisateurId() { return utilisateurId; }
|
||||
public void setUtilisateurId(UUID utilisateurId) { this.utilisateurId = utilisateurId; }
|
||||
|
||||
public String getType() { return type; }
|
||||
public void setType(String type) { this.type = type; }
|
||||
|
||||
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 LocalDateTime getDateEnvoi() { return dateEnvoi; }
|
||||
public void setDateEnvoi(LocalDateTime dateEnvoi) { this.dateEnvoi = dateEnvoi; }
|
||||
|
||||
public String getCanal() { return canal; }
|
||||
public void setCanal(String canal) { this.canal = canal; }
|
||||
|
||||
public String getStatut() { return statut; }
|
||||
public void setStatut(String statut) { this.statut = statut; }
|
||||
|
||||
public LocalDateTime getDateProgrammation() { return dateProgrammation; }
|
||||
public void setDateProgrammation(LocalDateTime dateProgrammation) { this.dateProgrammation = dateProgrammation; }
|
||||
|
||||
public boolean isRecurrente() { return recurrente; }
|
||||
public void setRecurrente(boolean recurrente) { this.recurrente = recurrente; }
|
||||
|
||||
public String getFrequence() { return frequence; }
|
||||
public void setFrequence(String frequence) { this.frequence = frequence; }
|
||||
|
||||
public String getMessageErreur() { return messageErreur; }
|
||||
public void setMessageErreur(String messageErreur) { this.messageErreur = messageErreur; }
|
||||
|
||||
// Builder
|
||||
public static class Builder {
|
||||
private UUID id;
|
||||
private UUID utilisateurId;
|
||||
private String type;
|
||||
private String titre;
|
||||
private String message;
|
||||
private LocalDateTime dateEnvoi;
|
||||
private String canal;
|
||||
private String statut;
|
||||
private LocalDateTime dateProgrammation;
|
||||
private boolean recurrente;
|
||||
private String frequence;
|
||||
|
||||
public Builder id(UUID id) { this.id = id; return this; }
|
||||
public Builder utilisateurId(UUID utilisateurId) { this.utilisateurId = utilisateurId; return this; }
|
||||
public Builder type(String type) { this.type = type; return this; }
|
||||
public Builder titre(String titre) { this.titre = titre; return this; }
|
||||
public Builder message(String message) { this.message = message; return this; }
|
||||
public Builder dateEnvoi(LocalDateTime dateEnvoi) { this.dateEnvoi = dateEnvoi; return this; }
|
||||
public Builder canal(String canal) { this.canal = canal; return this; }
|
||||
public Builder statut(String statut) { this.statut = statut; return this; }
|
||||
public Builder dateProgrammation(LocalDateTime dateProgrammation) { this.dateProgrammation = dateProgrammation; return this; }
|
||||
public Builder recurrente(boolean recurrente) { this.recurrente = recurrente; return this; }
|
||||
public Builder frequence(String frequence) { this.frequence = frequence; return this; }
|
||||
|
||||
public ScheduledNotification build() {
|
||||
return new ScheduledNotification(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,20 +32,20 @@ public class NotificationService {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(NotificationService.class);
|
||||
|
||||
@Inject
|
||||
FirebaseNotificationService firebaseService;
|
||||
// @Inject
|
||||
// FirebaseNotificationService firebaseService;
|
||||
|
||||
@Inject
|
||||
NotificationTemplateService templateService;
|
||||
// @Inject
|
||||
// NotificationTemplateService templateService;
|
||||
|
||||
@Inject
|
||||
PreferencesNotificationService preferencesService;
|
||||
|
||||
@Inject
|
||||
NotificationHistoryService historyService;
|
||||
|
||||
@Inject
|
||||
NotificationSchedulerService schedulerService;
|
||||
// @Inject
|
||||
// PreferencesNotificationService preferencesService;
|
||||
|
||||
// @Inject
|
||||
// NotificationHistoryService historyService;
|
||||
|
||||
// @Inject
|
||||
// NotificationSchedulerService schedulerService;
|
||||
|
||||
@ConfigProperty(name = "unionflow.notifications.enabled", defaultValue = "true")
|
||||
boolean notificationsEnabled;
|
||||
@@ -87,13 +87,15 @@ public class NotificationService {
|
||||
}
|
||||
|
||||
// Application des templates
|
||||
notification = templateService.appliquerTemplate(notification);
|
||||
// notification = templateService.appliquerTemplate(notification);
|
||||
|
||||
// Envoi via Firebase
|
||||
notification.setStatut(StatutNotification.EN_COURS_ENVOI);
|
||||
notification.setDateEnvoi(LocalDateTime.now());
|
||||
|
||||
boolean succes = firebaseService.envoyerNotificationPush(notification);
|
||||
// TODO: Réactiver quand Firebase sera configuré
|
||||
// boolean succes = firebaseService.envoyerNotificationPush(notification);
|
||||
boolean succes = true; // Mode démo
|
||||
|
||||
if (succes) {
|
||||
notification.setStatut(StatutNotification.ENVOYEE);
|
||||
@@ -104,7 +106,7 @@ public class NotificationService {
|
||||
}
|
||||
|
||||
// Sauvegarde dans l'historique
|
||||
historyService.sauvegarderNotification(notification);
|
||||
// historyService.sauvegarderNotification(notification);
|
||||
|
||||
return notification;
|
||||
|
||||
@@ -197,10 +199,10 @@ public class NotificationService {
|
||||
validerNotification(notification);
|
||||
|
||||
// Sauvegarde
|
||||
historyService.sauvegarderNotification(notification);
|
||||
|
||||
// historyService.sauvegarderNotification(notification);
|
||||
|
||||
// Programmation dans le scheduler
|
||||
schedulerService.programmerNotification(notification);
|
||||
// schedulerService.programmerNotification(notification);
|
||||
|
||||
incrementerStatistique("notifications_programmees");
|
||||
return notification;
|
||||
@@ -217,19 +219,21 @@ public class NotificationService {
|
||||
LOG.infof("Annulation de notification programmée: %s", notificationId);
|
||||
|
||||
try {
|
||||
NotificationDTO notification = historyService.obtenirNotification(notificationId);
|
||||
|
||||
if (notification != null && notification.getStatut().permetAnnulation()) {
|
||||
notification.setStatut(StatutNotification.ANNULEE);
|
||||
historyService.mettreAJourNotification(notification);
|
||||
|
||||
schedulerService.annulerNotificationProgrammee(notificationId);
|
||||
|
||||
incrementerStatistique("notifications_annulees");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
// TODO: Réactiver quand les services seront configurés
|
||||
// NotificationDTO notification = historyService.obtenirNotification(notificationId);
|
||||
//
|
||||
// if (notification != null && notification.getStatut().permetAnnulation()) {
|
||||
// notification.setStatut(StatutNotification.ANNULEE);
|
||||
// historyService.mettreAJourNotification(notification);
|
||||
//
|
||||
// schedulerService.annulerNotificationProgrammee(notificationId);
|
||||
// incrementerStatistique("notifications_annulees");
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// Mode démo : toujours retourner true
|
||||
incrementerStatistique("notifications_annulees");
|
||||
return true;
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de l'annulation de la notification %s", notificationId);
|
||||
@@ -249,20 +253,22 @@ public class NotificationService {
|
||||
LOG.debugf("Marquage comme lue: notification=%s, utilisateur=%s", notificationId, utilisateurId);
|
||||
|
||||
try {
|
||||
NotificationDTO notification = historyService.obtenirNotification(notificationId);
|
||||
|
||||
if (notification != null && notification.getDestinatairesIds().contains(utilisateurId)) {
|
||||
notification.setEstLue(true);
|
||||
notification.setDateDerniereLecture(LocalDateTime.now());
|
||||
notification.setStatut(StatutNotification.LUE);
|
||||
|
||||
historyService.mettreAJourNotification(notification);
|
||||
|
||||
incrementerStatistique("notifications_lues");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
// TODO: Réactiver quand les services seront configurés
|
||||
// NotificationDTO notification = historyService.obtenirNotification(notificationId);
|
||||
//
|
||||
// if (notification != null && notification.getDestinatairesIds().contains(utilisateurId)) {
|
||||
// notification.setEstLue(true);
|
||||
// notification.setDateDerniereLecture(LocalDateTime.now());
|
||||
// notification.setStatut(StatutNotification.LUE);
|
||||
//
|
||||
// historyService.mettreAJourNotification(notification);
|
||||
// incrementerStatistique("notifications_lues");
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// Mode démo : toujours retourner true
|
||||
incrementerStatistique("notifications_lues");
|
||||
return true;
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors du marquage comme lue: %s", notificationId);
|
||||
@@ -282,19 +288,21 @@ public class NotificationService {
|
||||
LOG.debugf("Archivage: notification=%s, utilisateur=%s", notificationId, utilisateurId);
|
||||
|
||||
try {
|
||||
NotificationDTO notification = historyService.obtenirNotification(notificationId);
|
||||
|
||||
if (notification != null && notification.getDestinatairesIds().contains(utilisateurId)) {
|
||||
notification.setEstArchivee(true);
|
||||
notification.setStatut(StatutNotification.ARCHIVEE);
|
||||
|
||||
historyService.mettreAJourNotification(notification);
|
||||
|
||||
incrementerStatistique("notifications_archivees");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
// TODO: Réactiver quand les services seront configurés
|
||||
// NotificationDTO notification = historyService.obtenirNotification(notificationId);
|
||||
//
|
||||
// if (notification != null && notification.getDestinatairesIds().contains(utilisateurId)) {
|
||||
// notification.setEstArchivee(true);
|
||||
// notification.setStatut(StatutNotification.ARCHIVEE);
|
||||
//
|
||||
// historyService.mettreAJourNotification(notification);
|
||||
// incrementerStatistique("notifications_archivees");
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// Mode démo : toujours retourner true
|
||||
incrementerStatistique("notifications_archivees");
|
||||
return true;
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de l'archivage: %s", notificationId);
|
||||
@@ -316,9 +324,13 @@ public class NotificationService {
|
||||
LOG.debugf("Récupération notifications utilisateur: %s", utilisateurId);
|
||||
|
||||
try {
|
||||
return historyService.obtenirNotificationsUtilisateur(
|
||||
utilisateurId, includeArchivees, limite
|
||||
);
|
||||
// TODO: Réactiver quand les services seront configurés
|
||||
// return historyService.obtenirNotificationsUtilisateur(
|
||||
// utilisateurId, includeArchivees, limite
|
||||
// );
|
||||
|
||||
// Mode démo : retourner une liste vide
|
||||
return new ArrayList<>();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la récupération des notifications pour %s", utilisateurId);
|
||||
return new ArrayList<>();
|
||||
@@ -443,7 +455,9 @@ public class NotificationService {
|
||||
private PreferencesNotificationDTO obtenirPreferencesUtilisateur(String utilisateurId) {
|
||||
return preferencesCache.computeIfAbsent(utilisateurId, id -> {
|
||||
try {
|
||||
return preferencesService.obtenirPreferences(id);
|
||||
// TODO: Réactiver quand les services seront configurés
|
||||
// return preferencesService.obtenirPreferences(id);
|
||||
return new PreferencesNotificationDTO(id); // Mode démo
|
||||
} catch (Exception e) {
|
||||
LOG.warnf("Impossible de récupérer les préférences pour %s, utilisation des défauts", id);
|
||||
return new PreferencesNotificationDTO(id);
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
package dev.lions.unionflow.server.service;
|
||||
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Service pour gérer les préférences de notification des utilisateurs
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class PreferencesNotificationService {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(PreferencesNotificationService.class);
|
||||
|
||||
// Stockage temporaire en mémoire (à remplacer par une base de données)
|
||||
private final Map<UUID, Map<String, Boolean>> preferencesUtilisateurs = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Obtient les préférences de notification d'un utilisateur
|
||||
*/
|
||||
public Map<String, Boolean> obtenirPreferences(UUID utilisateurId) {
|
||||
LOG.infof("Récupération des préférences de notification pour l'utilisateur %s", utilisateurId);
|
||||
|
||||
return preferencesUtilisateurs.getOrDefault(utilisateurId, getPreferencesParDefaut());
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour les préférences de notification d'un utilisateur
|
||||
*/
|
||||
public void mettreAJourPreferences(UUID utilisateurId, Map<String, Boolean> preferences) {
|
||||
LOG.infof("Mise à jour des préférences de notification pour l'utilisateur %s", utilisateurId);
|
||||
|
||||
preferencesUtilisateurs.put(utilisateurId, new HashMap<>(preferences));
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si un utilisateur souhaite recevoir un type de notification
|
||||
*/
|
||||
public boolean accepteNotification(UUID utilisateurId, String typeNotification) {
|
||||
Map<String, Boolean> preferences = obtenirPreferences(utilisateurId);
|
||||
return preferences.getOrDefault(typeNotification, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Active un type de notification pour un utilisateur
|
||||
*/
|
||||
public void activerNotification(UUID utilisateurId, String typeNotification) {
|
||||
LOG.infof("Activation de la notification %s pour l'utilisateur %s", typeNotification, utilisateurId);
|
||||
|
||||
Map<String, Boolean> preferences = obtenirPreferences(utilisateurId);
|
||||
preferences.put(typeNotification, true);
|
||||
mettreAJourPreferences(utilisateurId, preferences);
|
||||
}
|
||||
|
||||
/**
|
||||
* Désactive un type de notification pour un utilisateur
|
||||
*/
|
||||
public void desactiverNotification(UUID utilisateurId, String typeNotification) {
|
||||
LOG.infof("Désactivation de la notification %s pour l'utilisateur %s", typeNotification, utilisateurId);
|
||||
|
||||
Map<String, Boolean> preferences = obtenirPreferences(utilisateurId);
|
||||
preferences.put(typeNotification, false);
|
||||
mettreAJourPreferences(utilisateurId, preferences);
|
||||
}
|
||||
|
||||
/**
|
||||
* Réinitialise les préférences d'un utilisateur aux valeurs par défaut
|
||||
*/
|
||||
public void reinitialiserPreferences(UUID utilisateurId) {
|
||||
LOG.infof("Réinitialisation des préférences pour l'utilisateur %s", utilisateurId);
|
||||
|
||||
mettreAJourPreferences(utilisateurId, getPreferencesParDefaut());
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtient les préférences par défaut
|
||||
*/
|
||||
private Map<String, Boolean> getPreferencesParDefaut() {
|
||||
Map<String, Boolean> preferences = new HashMap<>();
|
||||
|
||||
// Notifications générales
|
||||
preferences.put("NOUVELLE_COTISATION", true);
|
||||
preferences.put("RAPPEL_COTISATION", true);
|
||||
preferences.put("COTISATION_RETARD", true);
|
||||
|
||||
// Notifications d'événements
|
||||
preferences.put("NOUVEL_EVENEMENT", true);
|
||||
preferences.put("RAPPEL_EVENEMENT", true);
|
||||
preferences.put("MODIFICATION_EVENEMENT", true);
|
||||
preferences.put("ANNULATION_EVENEMENT", true);
|
||||
|
||||
// Notifications de solidarité
|
||||
preferences.put("NOUVELLE_DEMANDE_AIDE", true);
|
||||
preferences.put("DEMANDE_AIDE_APPROUVEE", true);
|
||||
preferences.put("DEMANDE_AIDE_REJETEE", true);
|
||||
preferences.put("NOUVELLE_PROPOSITION_AIDE", true);
|
||||
|
||||
// Notifications administratives
|
||||
preferences.put("NOUVEAU_MEMBRE", false);
|
||||
preferences.put("MODIFICATION_PROFIL", false);
|
||||
preferences.put("RAPPORT_MENSUEL", true);
|
||||
|
||||
// Notifications push
|
||||
preferences.put("PUSH_MOBILE", true);
|
||||
preferences.put("EMAIL", true);
|
||||
preferences.put("SMS", false);
|
||||
|
||||
return preferences;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtient tous les utilisateurs qui acceptent un type de notification
|
||||
*/
|
||||
public Map<UUID, Boolean> obtenirUtilisateursAcceptantNotification(String typeNotification) {
|
||||
LOG.infof("Recherche des utilisateurs acceptant la notification %s", typeNotification);
|
||||
|
||||
Map<UUID, Boolean> utilisateursAcceptant = new HashMap<>();
|
||||
|
||||
for (Map.Entry<UUID, Map<String, Boolean>> entry : preferencesUtilisateurs.entrySet()) {
|
||||
UUID utilisateurId = entry.getKey();
|
||||
Map<String, Boolean> preferences = entry.getValue();
|
||||
|
||||
if (preferences.getOrDefault(typeNotification, true)) {
|
||||
utilisateursAcceptant.put(utilisateurId, true);
|
||||
}
|
||||
}
|
||||
|
||||
return utilisateursAcceptant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exporte les préférences d'un utilisateur
|
||||
*/
|
||||
public Map<String, Object> exporterPreferences(UUID utilisateurId) {
|
||||
LOG.infof("Export des préférences pour l'utilisateur %s", utilisateurId);
|
||||
|
||||
Map<String, Object> export = new HashMap<>();
|
||||
export.put("utilisateurId", utilisateurId);
|
||||
export.put("preferences", obtenirPreferences(utilisateurId));
|
||||
export.put("dateExport", java.time.LocalDateTime.now());
|
||||
|
||||
return export;
|
||||
}
|
||||
|
||||
/**
|
||||
* Importe les préférences d'un utilisateur
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void importerPreferences(UUID utilisateurId, Map<String, Object> donnees) {
|
||||
LOG.infof("Import des préférences pour l'utilisateur %s", utilisateurId);
|
||||
|
||||
if (donnees.containsKey("preferences")) {
|
||||
Map<String, Boolean> preferences = (Map<String, Boolean>) donnees.get("preferences");
|
||||
mettreAJourPreferences(utilisateurId, preferences);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,11 +45,11 @@ public class SolidariteService {
|
||||
@Inject
|
||||
MatchingService matchingService;
|
||||
|
||||
@Inject
|
||||
EvaluationService evaluationService;
|
||||
|
||||
@Inject
|
||||
NotificationSolidariteService notificationService;
|
||||
// @Inject
|
||||
// EvaluationService evaluationService;
|
||||
|
||||
// @Inject
|
||||
// NotificationSolidariteService notificationService;
|
||||
|
||||
@Inject
|
||||
SolidariteAnalyticsService analyticsService;
|
||||
@@ -81,11 +81,11 @@ public class SolidariteService {
|
||||
DemandeAideDTO demandeCree = demandeAideService.creerDemande(demandeDTO);
|
||||
|
||||
// 2. Calcul automatique de la priorité si non définie
|
||||
if (demandeCree.getPriorite() == null) {
|
||||
PrioriteAide prioriteCalculee = PrioriteAide.determinerPriorite(demandeCree.getTypeAide());
|
||||
demandeCree.setPriorite(prioriteCalculee);
|
||||
demandeCree = demandeAideService.mettreAJour(demandeCree);
|
||||
}
|
||||
// if (demandeCree.getPriorite() == null) {
|
||||
// PrioriteAide prioriteCalculee = PrioriteAide.determinerPriorite(demandeCree.getTypeAide());
|
||||
// demandeCree.setPriorite(prioriteCalculee);
|
||||
// demandeCree = demandeAideService.mettreAJour(demandeCree);
|
||||
// }
|
||||
|
||||
// 3. Matching automatique si activé
|
||||
if (autoMatchingEnabled) {
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="https://jakarta.ee/xml/ns/jakartaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
|
||||
https://jakarta.ee/xml/ns/jakartaee/beans_4_0.xsd"
|
||||
version="4.0"
|
||||
bean-discovery-mode="all">
|
||||
</beans>
|
||||
@@ -0,0 +1,56 @@
|
||||
# Configuration UnionFlow Server - Mode Minimal
|
||||
quarkus.application.name=unionflow-server-minimal
|
||||
quarkus.application.version=1.0.0
|
||||
|
||||
# Configuration HTTP
|
||||
quarkus.http.port=8080
|
||||
quarkus.http.host=0.0.0.0
|
||||
|
||||
# Configuration CORS
|
||||
quarkus.http.cors=true
|
||||
quarkus.http.cors.origins=*
|
||||
quarkus.http.cors.methods=GET,POST,PUT,DELETE,OPTIONS
|
||||
quarkus.http.cors.headers=Content-Type,Authorization
|
||||
|
||||
# Configuration Base de données H2 (en mémoire)
|
||||
quarkus.datasource.db-kind=h2
|
||||
quarkus.datasource.username=sa
|
||||
quarkus.datasource.password=
|
||||
quarkus.datasource.jdbc.url=jdbc:h2:mem:unionflow_minimal;DB_CLOSE_DELAY=-1;MODE=PostgreSQL
|
||||
|
||||
# Configuration Hibernate
|
||||
quarkus.hibernate-orm.database.generation=drop-and-create
|
||||
quarkus.hibernate-orm.log.sql=true
|
||||
quarkus.hibernate-orm.jdbc.timezone=UTC
|
||||
quarkus.hibernate-orm.packages=dev.lions.unionflow.server.entity
|
||||
|
||||
# Désactiver Flyway
|
||||
quarkus.flyway.migrate-at-start=false
|
||||
|
||||
# Désactiver Keycloak temporairement
|
||||
quarkus.oidc.tenant-enabled=false
|
||||
|
||||
# Chemins publics (tous publics en mode minimal)
|
||||
quarkus.http.auth.permission.public.paths=/*
|
||||
quarkus.http.auth.permission.public.policy=permit
|
||||
|
||||
# Configuration OpenAPI
|
||||
quarkus.smallrye-openapi.info-title=UnionFlow Server API - Minimal
|
||||
quarkus.smallrye-openapi.info-version=1.0.0
|
||||
quarkus.smallrye-openapi.info-description=API REST pour la gestion d'union (mode minimal)
|
||||
quarkus.smallrye-openapi.servers=http://localhost:8080
|
||||
|
||||
# Configuration Swagger UI
|
||||
quarkus.swagger-ui.always-include=true
|
||||
quarkus.swagger-ui.path=/swagger-ui
|
||||
|
||||
# Configuration santé
|
||||
quarkus.smallrye-health.root-path=/health
|
||||
|
||||
# Configuration logging
|
||||
quarkus.log.console.enable=true
|
||||
quarkus.log.console.level=INFO
|
||||
quarkus.log.console.format=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c{2.}] (%t) %s%e%n
|
||||
quarkus.log.category."dev.lions.unionflow".level=DEBUG
|
||||
quarkus.log.category."org.hibernate".level=WARN
|
||||
quarkus.log.category."io.quarkus".level=INFO
|
||||
@@ -118,3 +118,7 @@ quarkus.log.category."io.quarkus".level=INFO
|
||||
%prod.quarkus.oidc.client-id=${KEYCLOAK_CLIENT_ID:unionflow-server}
|
||||
%prod.quarkus.oidc.credentials.secret=${KEYCLOAK_CLIENT_SECRET}
|
||||
%prod.quarkus.oidc.tls.verification=required
|
||||
|
||||
# Configuration Jandex pour résoudre les warnings de réflexion
|
||||
quarkus.index-dependency.unionflow-server-api.group-id=dev.lions.unionflow
|
||||
quarkus.index-dependency.unionflow-server-api.artifact-id=unionflow-server-api
|
||||
|
||||
Reference in New Issue
Block a user