Sync: code local unifié

Synchronisation du code source local (fait foi).

Signed-off-by: lions dev Team
This commit is contained in:
dahoud
2026-03-15 16:25:40 +00:00
parent e82dc356f3
commit 75a19988b0
730 changed files with 53599 additions and 13145 deletions

View File

@@ -1,13 +1,22 @@
package dev.lions.unionflow.server.service;
import dev.lions.unionflow.server.api.dto.solidarite.DemandeAideDTO;
import dev.lions.unionflow.server.api.dto.solidarite.request.CreateDemandeAideRequest;
import dev.lions.unionflow.server.api.dto.solidarite.request.UpdateDemandeAideRequest;
import dev.lions.unionflow.server.api.dto.solidarite.response.DemandeAideResponse;
import dev.lions.unionflow.server.api.dto.solidarite.HistoriqueStatutDTO;
import dev.lions.unionflow.server.api.enums.solidarite.PrioriteAide;
import dev.lions.unionflow.server.api.enums.solidarite.StatutAide;
import dev.lions.unionflow.server.entity.DemandeAide;
import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.entity.Organisation;
import dev.lions.unionflow.server.mapper.DemandeAideMapper;
import dev.lions.unionflow.server.repository.DemandeAideRepository;
import dev.lions.unionflow.server.repository.MembreRepository;
import dev.lions.unionflow.server.repository.OrganisationRepository;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@@ -18,8 +27,11 @@ import org.jboss.logging.Logger;
/**
* Service spécialisé pour la gestion des demandes d'aide
*
* <p>Ce service gère le cycle de vie complet des demandes d'aide : création, validation,
* changements de statut, recherche et suivi.
* <p>
* Ce service gère le cycle de vie complet des demandes d'aide : création,
* validation,
* changements de statut, recherche et suivi. Persistance via
* DemandeAideRepository.
*
* @author UnionFlow Team
* @version 1.0
@@ -30,8 +42,17 @@ public class DemandeAideService {
private static final Logger LOG = Logger.getLogger(DemandeAideService.class);
@Inject
DemandeAideRepository demandeAideRepository;
@Inject
DemandeAideMapper demandeAideMapper;
@Inject
MembreRepository membreRepository;
@Inject
OrganisationRepository organisationRepository;
// Cache en mémoire pour les demandes fréquemment consultées
private final Map<UUID, DemandeAideDTO> cacheDemandesRecentes = new HashMap<>();
private final Map<UUID, DemandeAideResponse> cacheDemandesRecentes = new HashMap<>();
private final Map<UUID, LocalDateTime> cacheTimestamps = new HashMap<>();
private static final long CACHE_DURATION_MINUTES = 15;
@@ -40,83 +61,80 @@ public class DemandeAideService {
/**
* Crée une nouvelle demande d'aide
*
* @param demandeDTO La demande à créer
* @return La demande créée avec ID généré
* @param request La requête de création
* @return La demande créée
*/
@Transactional
public DemandeAideDTO creerDemande(@Valid DemandeAideDTO demandeDTO) {
LOG.infof("Création d'une nouvelle demande d'aide: %s", demandeDTO.getTitre());
public DemandeAideResponse creerDemande(@Valid CreateDemandeAideRequest request) {
LOG.infof("Création d'une nouvelle demande d'aide: %s", request.titre());
// Génération des identifiants
demandeDTO.setId(UUID.randomUUID());
demandeDTO.setNumeroReference(genererNumeroReference());
Membre demandeur = null;
if (request.membreDemandeurId() != null) {
demandeur = membreRepository.findById(request.membreDemandeurId());
if (demandeur == null) {
throw new IllegalArgumentException("Membre demandeur non trouvé: " + request.membreDemandeurId());
}
}
Organisation organisation = null;
if (request.associationId() != null) {
organisation = organisationRepository.findById(request.associationId());
if (organisation == null) {
throw new IllegalArgumentException("Organisation non trouvée: " + request.associationId());
}
}
DemandeAide entity = demandeAideMapper.toEntity(request, demandeur, null, organisation);
demandeAideRepository.persist(entity);
DemandeAideResponse response = demandeAideMapper.toDTO(entity);
response.setNumeroReference(genererNumeroReference());
response.setScorePriorite(calculerScorePriorite(response));
// Initialisation des dates
LocalDateTime maintenant = LocalDateTime.now();
demandeDTO.setDateCreation(maintenant);
demandeDTO.setDateModification(maintenant);
HistoriqueStatutDTO historiqueInitial = HistoriqueStatutDTO.builder()
.id(UUID.randomUUID().toString())
.ancienStatut(null)
.nouveauStatut(response.getStatut())
.dateChangement(maintenant)
.auteurId(response.getMembreDemandeurId() != null ? response.getMembreDemandeurId().toString() : null)
.motif("Création de la demande")
.estAutomatique(true)
.build();
response.setHistoriqueStatuts(List.of(historiqueInitial));
// Statut initial
if (demandeDTO.getStatut() == null) {
demandeDTO.setStatut(StatutAide.BROUILLON);
}
// Priorité par défaut si non définie
if (demandeDTO.getPriorite() == null) {
demandeDTO.setPriorite(PrioriteAide.NORMALE);
}
// Initialisation de l'historique
HistoriqueStatutDTO historiqueInitial =
HistoriqueStatutDTO.builder()
.id(UUID.randomUUID().toString())
.ancienStatut(null)
.nouveauStatut(demandeDTO.getStatut())
.dateChangement(maintenant)
.auteurId(demandeDTO.getMembreDemandeurId() != null ? demandeDTO.getMembreDemandeurId().toString() : null)
.motif("Création de la demande")
.estAutomatique(true)
.build();
demandeDTO.setHistoriqueStatuts(List.of(historiqueInitial));
// Calcul du score de priorité
demandeDTO.setScorePriorite(calculerScorePriorite(demandeDTO));
// Sauvegarde en cache
ajouterAuCache(demandeDTO);
LOG.infof("Demande d'aide créée avec succès: %s", demandeDTO.getId());
return demandeDTO;
ajouterAuCache(response);
LOG.infof("Demande d'aide créée avec succès: %s", response.getId());
return response;
}
/**
* Met à jour une demande d'aide existante
*
* @param demandeDTO La demande à mettre à jour
* @param id Identifiant de la demande
* @param request La requête de mise à jour
* @return La demande mise à jour
*/
@Transactional
public DemandeAideDTO mettreAJour(@Valid DemandeAideDTO demandeDTO) {
LOG.infof("Mise à jour de la demande d'aide: %s", demandeDTO.getId());
public DemandeAideResponse mettreAJour(@NotNull UUID id, @Valid UpdateDemandeAideRequest request) {
LOG.infof("Mise à jour de la demande d'aide: %s", id);
// Vérification que la demande peut être modifiée
if (!demandeDTO.estModifiable()) {
DemandeAide entity = demandeAideRepository.findById(id);
if (entity == null) {
throw new IllegalArgumentException("Demande non trouvée: " + id);
}
if (!entity.getStatut().permetModification()) {
throw new IllegalStateException("Cette demande ne peut plus être modifiée");
}
// Mise à jour de la date de modification
demandeDTO.setDateModification(LocalDateTime.now());
demandeDTO.setVersion(demandeDTO.getVersion() + 1);
demandeAideMapper.updateEntityFromDTO(entity, request);
entity = demandeAideRepository.update(entity);
// Recalcul du score de priorité
demandeDTO.setScorePriorite(calculerScorePriorite(demandeDTO));
// Mise à jour du cache
ajouterAuCache(demandeDTO);
LOG.infof("Demande d'aide mise à jour avec succès: %s", demandeDTO.getId());
return demandeDTO;
DemandeAideResponse response = demandeAideMapper.toDTO(entity);
response.setScorePriorite(calculerScorePriorite(response));
ajouterAuCache(response);
LOG.infof("Demande d'aide mise à jour avec succès: %s", response.getId());
return response;
}
/**
@@ -125,87 +143,82 @@ public class DemandeAideService {
* @param id UUID de la demande
* @return La demande trouvée
*/
public DemandeAideDTO obtenirParId(@NotNull UUID id) {
public DemandeAideResponse obtenirParId(@NotNull UUID id) {
LOG.debugf("Récupération de la demande d'aide: %s", id);
// Vérification du cache
DemandeAideDTO demandeCachee = obtenirDuCache(id);
DemandeAideResponse demandeCachee = obtenirDuCache(id);
if (demandeCachee != null) {
LOG.debugf("Demande trouvée dans le cache: %s", id);
return demandeCachee;
}
// Simulation de récupération depuis la base de données
// Dans une vraie implémentation, ceci ferait appel au repository
DemandeAideDTO demande = simulerRecuperationBDD(id);
if (demande != null) {
ajouterAuCache(demande);
DemandeAide entity = demandeAideRepository.findById(id);
DemandeAideResponse response = entity != null ? demandeAideMapper.toDTO(entity) : null;
if (response != null) {
ajouterAuCache(response);
}
return demande;
return response;
}
/**
* Change le statut d'une demande d'aide
*
* @param demandeId UUID de la demande
* @param demandeId UUID de la demande
* @param nouveauStatut Nouveau statut
* @param motif Motif du changement
* @param motif Motif du changement
* @return La demande avec le nouveau statut
*/
@Transactional
public DemandeAideDTO changerStatut(
public DemandeAideResponse changerStatut(
@NotNull UUID demandeId, @NotNull StatutAide nouveauStatut, String motif) {
LOG.infof("Changement de statut pour la demande %s: %s", demandeId, nouveauStatut);
DemandeAideDTO demande = obtenirParId(demandeId);
if (demande == null) {
DemandeAide entity = demandeAideRepository.findById(demandeId);
if (entity == null) {
throw new IllegalArgumentException("Demande non trouvée: " + demandeId);
}
StatutAide ancienStatut = demande.getStatut();
// Validation de la transition
StatutAide ancienStatut = entity.getStatut();
if (!ancienStatut.peutTransitionnerVers(nouveauStatut)) {
throw new IllegalStateException(
String.format("Transition invalide de %s vers %s", ancienStatut, nouveauStatut));
}
// Mise à jour du statut
demande.setStatut(nouveauStatut);
demande.setDateModification(LocalDateTime.now());
// Ajout à l'historique
HistoriqueStatutDTO nouvelHistorique =
HistoriqueStatutDTO.builder()
.id(UUID.randomUUID().toString())
.ancienStatut(ancienStatut)
.nouveauStatut(nouveauStatut)
.dateChangement(LocalDateTime.now())
.motif(motif)
.estAutomatique(false)
.build();
List<HistoriqueStatutDTO> historique = new ArrayList<>(demande.getHistoriqueStatuts());
historique.add(nouvelHistorique);
demande.setHistoriqueStatuts(historique);
// Actions spécifiques selon le nouveau statut
switch (nouveauStatut) {
case SOUMISE -> demande.setDateSoumission(LocalDateTime.now());
case APPROUVEE, APPROUVEE_PARTIELLEMENT -> demande.setDateApprobation(LocalDateTime.now());
case VERSEE -> demande.setDateVersement(LocalDateTime.now());
case CLOTUREE -> demande.setDateCloture(LocalDateTime.now());
entity.setStatut(nouveauStatut);
if (motif != null && !motif.isBlank()) {
entity.setCommentaireEvaluation(
entity.getCommentaireEvaluation() != null
? entity.getCommentaireEvaluation() + "\n" + motif
: motif);
}
LocalDateTime now = LocalDateTime.now();
entity.setDateModification(now);
switch (nouveauStatut) {
case SOUMISE -> entity.setDateDemande(now);
case APPROUVEE, APPROUVEE_PARTIELLEMENT -> entity.setDateEvaluation(now);
case VERSEE -> entity.setDateVersement(now);
default -> {
}
}
entity = demandeAideRepository.update(entity);
// Mise à jour du cache
ajouterAuCache(demande);
DemandeAideResponse response = demandeAideMapper.toDTO(entity);
HistoriqueStatutDTO nouvelHistorique = HistoriqueStatutDTO.builder()
.id(UUID.randomUUID().toString())
.ancienStatut(ancienStatut)
.nouveauStatut(nouveauStatut)
.dateChangement(now)
.motif(motif)
.estAutomatique(false)
.build();
List<HistoriqueStatutDTO> historique = new ArrayList<>(
response.getHistoriqueStatuts() != null ? response.getHistoriqueStatuts() : List.of());
historique.add(nouvelHistorique);
response.setHistoriqueStatuts(historique);
ajouterAuCache(response);
LOG.infof(
"Statut changé avec succès pour la demande %s: %s -> %s",
demandeId, ancienStatut, nouveauStatut);
return demande;
return response;
}
// === RECHERCHE ET FILTRAGE ===
@@ -216,13 +229,10 @@ public class DemandeAideService {
* @param filtres Map des critères de recherche
* @return Liste des demandes correspondantes
*/
public List<DemandeAideDTO> rechercherAvecFiltres(Map<String, Object> filtres) {
public List<DemandeAideResponse> rechercherAvecFiltres(Map<String, Object> filtres) {
LOG.debugf("Recherche de demandes avec filtres: %s", filtres);
// Simulation de recherche - dans une vraie implémentation,
// ceci utiliserait des requêtes de base de données optimisées
List<DemandeAideDTO> toutesLesDemandes = simulerRecuperationToutesLesDemandes();
List<DemandeAideResponse> toutesLesDemandes = chargerToutesLesDemandesDepuisBDD();
return toutesLesDemandes.stream()
.filter(demande -> correspondAuxFiltres(demande, filtres))
.sorted(this::comparerParPriorite)
@@ -235,19 +245,18 @@ public class DemandeAideService {
* @param organisationId UUID de l'organisation
* @return Liste des demandes urgentes
*/
public List<DemandeAideDTO> obtenirDemandesUrgentes(UUID organisationId) {
public List<DemandeAideResponse> obtenirDemandesUrgentes(UUID organisationId) {
LOG.debugf("Récupération des demandes urgentes pour: %s", organisationId);
Map<String, Object> filtres =
Map.of(
"organisationId", organisationId,
"priorite", List.of(PrioriteAide.CRITIQUE, PrioriteAide.URGENTE),
"statut",
List.of(
StatutAide.SOUMISE,
StatutAide.EN_ATTENTE,
StatutAide.EN_COURS_EVALUATION,
StatutAide.APPROUVEE));
Map<String, Object> filtres = Map.of(
"organisationId", organisationId,
"priorite", List.of(PrioriteAide.CRITIQUE, PrioriteAide.URGENTE),
"statut",
List.of(
StatutAide.SOUMISE,
StatutAide.EN_ATTENTE,
StatutAide.EN_COURS_EVALUATION,
StatutAide.APPROUVEE));
return rechercherAvecFiltres(filtres);
}
@@ -258,12 +267,12 @@ public class DemandeAideService {
* @param organisationId ID de l'organisation
* @return Liste des demandes en retard
*/
public List<DemandeAideDTO> obtenirDemandesEnRetard(UUID organisationId) {
public List<DemandeAideResponse> obtenirDemandesEnRetard(UUID organisationId) {
LOG.debugf("Récupération des demandes en retard pour: %s", organisationId);
return simulerRecuperationToutesLesDemandes().stream()
.filter(demande -> demande.getAssociationId().equals(organisationId))
.filter(DemandeAideDTO::estDelaiDepasse)
return chargerToutesLesDemandesDepuisBDD().stream()
.filter(demande -> demande.getAssociationId() != null && demande.getAssociationId().equals(organisationId))
.filter(DemandeAideResponse::estDelaiDepasse)
.filter(demande -> !demande.estTerminee())
.sorted(this::comparerParPriorite)
.collect(Collectors.toList());
@@ -279,7 +288,7 @@ public class DemandeAideService {
}
/** Calcule le score de priorité d'une demande */
private double calculerScorePriorite(DemandeAideDTO demande) {
private double calculerScorePriorite(DemandeAideResponse demande) {
double score = demande.getPriorite().getScorePriorite();
// Bonus pour type d'aide urgent
@@ -295,8 +304,7 @@ public class DemandeAideService {
}
// Malus pour ancienneté
long joursDepuisCreation =
java.time.Duration.between(demande.getDateCreation(), LocalDateTime.now()).toDays();
long joursDepuisCreation = java.time.Duration.between(demande.getDateCreation(), LocalDateTime.now()).toDays();
if (joursDepuisCreation > 7) {
score += 0.3;
}
@@ -305,38 +313,43 @@ public class DemandeAideService {
}
/** Vérifie si une demande correspond aux filtres */
private boolean correspondAuxFiltres(DemandeAideDTO demande, Map<String, Object> filtres) {
private boolean correspondAuxFiltres(DemandeAideResponse demande, Map<String, Object> filtres) {
for (Map.Entry<String, Object> filtre : filtres.entrySet()) {
String cle = filtre.getKey();
Object valeur = filtre.getValue();
switch (cle) {
case "organisationId" -> {
if (!demande.getAssociationId().equals(valeur)) return false;
if (!demande.getAssociationId().equals(valeur))
return false;
}
case "typeAide" -> {
if (valeur instanceof List<?> liste) {
if (!liste.contains(demande.getTypeAide())) return false;
if (!liste.contains(demande.getTypeAide()))
return false;
} else if (!demande.getTypeAide().equals(valeur)) {
return false;
}
}
case "statut" -> {
if (valeur instanceof List<?> liste) {
if (!liste.contains(demande.getStatut())) return false;
if (!liste.contains(demande.getStatut()))
return false;
} else if (!demande.getStatut().equals(valeur)) {
return false;
}
}
case "priorite" -> {
if (valeur instanceof List<?> liste) {
if (!liste.contains(demande.getPriorite())) return false;
if (!liste.contains(demande.getPriorite()))
return false;
} else if (!demande.getPriorite().equals(valeur)) {
return false;
}
}
case "demandeurId" -> {
if (!demande.getMembreDemandeurId().equals(valeur)) return false;
if (demande.getMembreDemandeurId() == null || !demande.getMembreDemandeurId().equals(valeur))
return false;
}
}
}
@@ -344,18 +357,21 @@ public class DemandeAideService {
}
/** Compare deux demandes par priorité */
private int comparerParPriorite(DemandeAideDTO d1, DemandeAideDTO d2) {
// D'abord par score de priorité (plus bas = plus prioritaire)
int comparaisonScore = Double.compare(d1.getScorePriorite(), d2.getScorePriorite());
if (comparaisonScore != 0) return comparaisonScore;
private int comparerParPriorite(DemandeAideResponse d1, DemandeAideResponse d2) {
double s1 = d1.getScorePriorite() != null ? d1.getScorePriorite() : Double.MAX_VALUE;
double s2 = d2.getScorePriorite() != null ? d2.getScorePriorite() : Double.MAX_VALUE;
int comparaisonScore = Double.compare(s1, s2);
if (comparaisonScore != 0)
return comparaisonScore;
// Puis par date de création (plus ancien = plus prioritaire)
return d1.getDateCreation().compareTo(d2.getDateCreation());
LocalDateTime c1 = d1.getDateCreation() != null ? d1.getDateCreation() : LocalDateTime.MIN;
LocalDateTime c2 = d2.getDateCreation() != null ? d2.getDateCreation() : LocalDateTime.MIN;
return c1.compareTo(c2);
}
// === GESTION DU CACHE ===
private void ajouterAuCache(DemandeAideDTO demande) {
private void ajouterAuCache(DemandeAideResponse demande) {
cacheDemandesRecentes.put(demande.getId(), demande);
cacheTimestamps.put(demande.getId(), LocalDateTime.now());
@@ -365,9 +381,10 @@ public class DemandeAideService {
}
}
private DemandeAideDTO obtenirDuCache(UUID id) {
private DemandeAideResponse obtenirDuCache(UUID id) {
LocalDateTime timestamp = cacheTimestamps.get(id);
if (timestamp == null) return null;
if (timestamp == null)
return null;
// Vérification de l'expiration
if (LocalDateTime.now().minusMinutes(CACHE_DURATION_MINUTES).isAfter(timestamp)) {
@@ -386,15 +403,11 @@ public class DemandeAideService {
cacheDemandesRecentes.keySet().retainAll(cacheTimestamps.keySet());
}
// === MÉTHODES DE SIMULATION (À REMPLACER PAR DE VRAIS REPOSITORIES) ===
private DemandeAideDTO simulerRecuperationBDD(UUID id) {
// Simulation - dans une vraie implémentation, ceci ferait appel au repository
return null;
}
private List<DemandeAideDTO> simulerRecuperationToutesLesDemandes() {
// Simulation - dans une vraie implémentation, ceci ferait appel au repository
return new ArrayList<>();
/** Charge toutes les demandes depuis la base et les mappe en DTO. */
private List<DemandeAideResponse> chargerToutesLesDemandesDepuisBDD() {
List<DemandeAide> entities = demandeAideRepository.listAll();
return entities.stream()
.map(demandeAideMapper::toDTO)
.collect(Collectors.toList());
}
}