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,7 +1,7 @@
package dev.lions.unionflow.server.service;
import dev.lions.unionflow.server.api.dto.solidarite.DemandeAideDTO;
import dev.lions.unionflow.server.api.dto.solidarite.PropositionAideDTO;
import dev.lions.unionflow.server.api.dto.solidarite.response.DemandeAideResponse;
import dev.lions.unionflow.server.api.dto.solidarite.response.PropositionAideResponse;
import dev.lions.unionflow.server.api.enums.solidarite.TypeAide;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
@@ -15,7 +15,9 @@ import org.jboss.logging.Logger;
/**
* Service intelligent de matching entre demandes et propositions d'aide
*
* <p>Ce service utilise des algorithmes avancés pour faire correspondre les demandes d'aide avec
* <p>
* Ce service utilise des algorithmes avancés pour faire correspondre les
* demandes d'aide avec
* les propositions les plus appropriées.
*
* @author UnionFlow Team
@@ -27,9 +29,11 @@ public class MatchingService {
private static final Logger LOG = Logger.getLogger(MatchingService.class);
@Inject PropositionAideService propositionAideService;
@Inject
PropositionAideService propositionAideService;
@Inject DemandeAideService demandeAideService;
@Inject
DemandeAideService demandeAideService;
@ConfigProperty(name = "unionflow.matching.score-minimum", defaultValue = "30.0")
double scoreMinimumMatching;
@@ -51,15 +55,16 @@ public class MatchingService {
* @param demande La demande d'aide
* @return Liste des propositions compatibles triées par score
*/
public List<PropositionAideDTO> trouverPropositionsCompatibles(DemandeAideDTO demande) {
public List<PropositionAideResponse> trouverPropositionsCompatibles(DemandeAideResponse demande) {
LOG.infof("Recherche de propositions compatibles pour la demande: %s", demande.getId());
long startTime = System.currentTimeMillis();
try {
// 1. Recherche de base par type d'aide
List<PropositionAideDTO> candidats =
propositionAideService.obtenirPropositionsActives(demande.getTypeAide());
List<PropositionAideResponse> candidatsOriginal = propositionAideService
.obtenirPropositionsActives(demande.getTypeAide());
List<PropositionAideResponse> candidats = new ArrayList<>(candidatsOriginal);
// 2. Si pas assez de candidats, élargir à la catégorie
if (candidats.size() < 3) {
@@ -67,36 +72,33 @@ public class MatchingService {
}
// 3. Filtrage et scoring
List<ResultatMatching> resultats =
candidats.stream()
.filter(PropositionAideDTO::isActiveEtDisponible)
.filter(p -> p.peutAccepterBeneficiaires())
.map(
proposition -> {
double score = calculerScoreCompatibilite(demande, proposition);
return new ResultatMatching(proposition, score);
})
.filter(resultat -> resultat.score >= scoreMinimumMatching)
.sorted((r1, r2) -> Double.compare(r2.score, r1.score))
.limit(maxResultatsMatching)
.collect(Collectors.toList());
List<ResultatMatching> resultats = candidats.stream()
.filter(PropositionAideResponse::isActiveEtDisponible)
.filter(p -> p.peutAccepterBeneficiaires())
.map(
proposition -> {
double score = calculerScoreCompatibilite(demande, proposition);
return new ResultatMatching(proposition, score);
})
.filter(resultat -> resultat.score >= scoreMinimumMatching)
.sorted((r1, r2) -> Double.compare(r2.score, r1.score))
.limit(maxResultatsMatching)
.collect(Collectors.toList());
// 4. Extraction des propositions
List<PropositionAideDTO> propositionsCompatibles =
resultats.stream()
.map(
resultat -> {
// Stocker le score dans les données personnalisées
if (resultat.proposition.getDonneesPersonnalisees() == null) {
resultat.proposition.setDonneesPersonnalisees(new HashMap<>());
}
resultat
.proposition
.getDonneesPersonnalisees()
.put("scoreMatching", resultat.score);
return resultat.proposition;
})
.collect(Collectors.toList());
List<PropositionAideResponse> propositionsCompatibles = resultats.stream()
.map(
resultat -> {
// Stocker le score dans les données personnalisées
if (resultat.proposition.getDonneesPersonnalisees() == null) {
resultat.proposition.setDonneesPersonnalisees(new HashMap<>());
}
resultat.proposition
.getDonneesPersonnalisees()
.put("scoreMatching", resultat.score);
return resultat.proposition;
})
.collect(Collectors.toList());
long duration = System.currentTimeMillis() - startTime;
LOG.infof(
@@ -117,23 +119,21 @@ public class MatchingService {
* @param proposition La proposition d'aide
* @return Liste des demandes compatibles triées par score
*/
public List<DemandeAideDTO> trouverDemandesCompatibles(PropositionAideDTO proposition) {
public List<DemandeAideResponse> trouverDemandesCompatibles(PropositionAideResponse proposition) {
LOG.infof("Recherche de demandes compatibles pour la proposition: %s", proposition.getId());
try {
// Recherche des demandes actives du même type
Map<String, Object> filtres =
Map.of(
"typeAide", proposition.getTypeAide(),
"statut",
List.of(
dev.lions.unionflow.server.api.enums.solidarite.StatutAide.SOUMISE,
dev.lions.unionflow.server.api.enums.solidarite.StatutAide.EN_ATTENTE,
dev.lions.unionflow.server.api.enums.solidarite.StatutAide
.EN_COURS_EVALUATION,
dev.lions.unionflow.server.api.enums.solidarite.StatutAide.APPROUVEE));
Map<String, Object> filtres = Map.of(
"typeAide", proposition.getTypeAide(),
"statut",
List.of(
dev.lions.unionflow.server.api.enums.solidarite.StatutAide.SOUMISE,
dev.lions.unionflow.server.api.enums.solidarite.StatutAide.EN_ATTENTE,
dev.lions.unionflow.server.api.enums.solidarite.StatutAide.EN_COURS_EVALUATION,
dev.lions.unionflow.server.api.enums.solidarite.StatutAide.APPROUVEE));
List<DemandeAideDTO> candidats = demandeAideService.rechercherAvecFiltres(filtres);
List<DemandeAideResponse> candidats = demandeAideService.rechercherAvecFiltres(filtres);
// Scoring et tri
return candidats.stream()
@@ -148,9 +148,7 @@ public class MatchingService {
return demande;
})
.filter(
demande ->
(Double) demande.getDonneesPersonnalisees().get("scoreMatching")
>= scoreMinimumMatching)
demande -> (Double) demande.getDonneesPersonnalisees().get("scoreMatching") >= scoreMinimumMatching)
.sorted(
(d1, d2) -> {
Double score1 = (Double) d1.getDonneesPersonnalisees().get("scoreMatching");
@@ -174,7 +172,7 @@ public class MatchingService {
* @param demande La demande d'aide financière approuvée
* @return Liste des proposants financiers compatibles
*/
public List<PropositionAideDTO> rechercherProposantsFinanciers(DemandeAideDTO demande) {
public List<PropositionAideResponse> rechercherProposantsFinanciers(DemandeAideResponse demande) {
LOG.infof("Recherche de proposants financiers pour la demande: %s", demande.getId());
if (!demande.getTypeAide().isFinancier()) {
@@ -183,18 +181,17 @@ public class MatchingService {
}
// Filtres spécifiques pour les aides financières
Map<String, Object> filtres =
Map.of(
"typeAide",
demande.getTypeAide(),
"estDisponible",
true,
"montantMaximum",
demande.getMontantApprouve() != null
? demande.getMontantApprouve()
: demande.getMontantDemande());
Map<String, Object> filtres = Map.of(
"typeAide",
demande.getTypeAide(),
"estDisponible",
true,
"montantMaximum",
demande.getMontantApprouve() != null
? demande.getMontantApprouve()
: (demande.getMontantDemande() != null ? demande.getMontantDemande() : BigDecimal.ZERO));
List<PropositionAideDTO> propositions = propositionAideService.rechercherAvecFiltres(filtres);
List<PropositionAideResponse> propositions = propositionAideService.rechercherAvecFiltres(filtres);
// Scoring spécialisé pour les aides financières
return propositions.stream()
@@ -224,11 +221,11 @@ public class MatchingService {
* @param demande La demande d'aide urgente
* @return Liste des propositions d'urgence
*/
public List<PropositionAideDTO> matchingUrgence(DemandeAideDTO demande) {
public List<PropositionAideResponse> matchingUrgence(DemandeAideResponse demande) {
LOG.infof("Matching d'urgence pour la demande: %s", demande.getId());
// Recherche élargie pour les urgences
List<PropositionAideDTO> candidats = new ArrayList<>();
List<PropositionAideResponse> candidats = new ArrayList<>();
// 1. Même type d'aide
candidats.addAll(propositionAideService.obtenirPropositionsActives(demande.getTypeAide()));
@@ -242,7 +239,7 @@ public class MatchingService {
// Scoring avec bonus d'urgence
return candidats.stream()
.distinct()
.filter(PropositionAideDTO::isActiveEtDisponible)
.filter(PropositionAideResponse::isActiveEtDisponible)
.map(
proposition -> {
double score = calculerScoreCompatibilite(demande, proposition);
@@ -270,7 +267,7 @@ public class MatchingService {
/** Calcule le score de compatibilité entre une demande et une proposition */
private double calculerScoreCompatibilite(
DemandeAideDTO demande, PropositionAideDTO proposition) {
DemandeAideResponse demande, PropositionAideResponse proposition) {
double score = 0.0;
// 1. Correspondance du type d'aide (40 points max)
@@ -287,17 +284,17 @@ public class MatchingService {
// 2. Compatibilité financière (25 points max)
if (demande.getTypeAide().isNecessiteMontant() && proposition.getMontantMaximum() != null) {
BigDecimal montantDemande =
demande.getMontantApprouve() != null
? demande.getMontantApprouve()
: demande.getMontantDemande();
BigDecimal montantDemande = demande.getMontantApprouve() != null
? demande.getMontantApprouve()
: demande.getMontantDemande();
if (montantDemande != null) {
if (montantDemande.compareTo(proposition.getMontantMaximum()) <= 0) {
score += 25.0;
} else {
// Pénalité proportionnelle au dépassement
double ratio = proposition.getMontantMaximum().divide(montantDemande, 4, java.math.RoundingMode.HALF_UP).doubleValue();
double ratio = proposition.getMontantMaximum().divide(montantDemande, 4, java.math.RoundingMode.HALF_UP)
.doubleValue();
score += 25.0 * ratio;
}
}
@@ -306,19 +303,20 @@ public class MatchingService {
}
// 3. Expérience du proposant (15 points max)
if (proposition.getNombreBeneficiairesAides() > 0) {
if (proposition.getNombreBeneficiairesAides() != null && proposition.getNombreBeneficiairesAides() > 0) {
score += Math.min(15.0, proposition.getNombreBeneficiairesAides() * boostExperience);
}
// 4. Réputation (10 points max)
if (proposition.getNoteMoyenne() != null && proposition.getNombreEvaluations() >= 3) {
if (proposition.getNoteMoyenne() != null && proposition.getNombreEvaluations() != null
&& proposition.getNombreEvaluations() >= 3) {
score += (proposition.getNoteMoyenne() - 3.0) * 3.33; // 0 à 10 points
}
// 5. Disponibilité et capacité (10 points max)
if (proposition.peutAccepterBeneficiaires()) {
double ratioCapacite =
(double) proposition.getPlacesRestantes() / proposition.getNombreMaxBeneficiaires();
double ratioCapacite = (double) proposition.getPlacesRestantes()
/ (proposition.getNombreMaxBeneficiaires() != null ? proposition.getNombreMaxBeneficiaires() : 1);
score += 10.0 * ratioCapacite;
}
@@ -331,27 +329,27 @@ public class MatchingService {
}
/** Calcule le score spécialisé pour les aides financières */
private double calculerScoreFinancier(DemandeAideDTO demande, PropositionAideDTO proposition) {
private double calculerScoreFinancier(DemandeAideResponse demande, PropositionAideResponse proposition) {
double score = calculerScoreCompatibilite(demande, proposition);
// Bonus spécifiques aux aides financières
// 1. Historique de versements
if (proposition.getMontantTotalVerse() > 0) {
if (proposition.getMontantTotalVerse() != null && proposition.getMontantTotalVerse() > 0) {
score += Math.min(10.0, proposition.getMontantTotalVerse() / 10000.0);
}
// 2. Fiabilité (ratio versements/promesses)
if (proposition.getNombreDemandesTraitees() > 0) {
if (proposition.getNombreDemandesTraitees() != null && proposition.getNombreDemandesTraitees() > 0) {
// Simulation d'un ratio de fiabilité
double ratioFiabilite = 0.9; // À calculer réellement
score += ratioFiabilite * 15.0;
}
// 3. Rapidité de réponse
if (proposition.getDelaiReponseHeures() <= 24) {
if (proposition.getDelaiReponseHeures() != null && proposition.getDelaiReponseHeures() <= 24) {
score += 10.0;
} else if (proposition.getDelaiReponseHeures() <= 72) {
} else if (proposition.getDelaiReponseHeures() != null && proposition.getDelaiReponseHeures() <= 72) {
score += 5.0;
}
@@ -359,8 +357,9 @@ public class MatchingService {
}
/** Calcule le bonus géographique */
private double calculerBonusGeographique(DemandeAideDTO demande, PropositionAideDTO proposition) {
// Simulation - dans une vraie implémentation, ceci utiliserait les données de localisation
private double calculerBonusGeographique(DemandeAideResponse demande, PropositionAideResponse proposition) {
// Simulation - dans une vraie implémentation, ceci utiliserait les données de
// localisation
if (demande.getLocalisation() != null && proposition.getZonesGeographiques() != null) {
// Logique de proximité géographique
return boostGeographique;
@@ -369,7 +368,7 @@ public class MatchingService {
}
/** Calcule le bonus temporel (urgence, disponibilité) */
private double calculerBonusTemporel(DemandeAideDTO demande, PropositionAideDTO proposition) {
private double calculerBonusTemporel(DemandeAideResponse demande, PropositionAideResponse proposition) {
double bonus = 0.0;
// Bonus pour demande urgente
@@ -378,17 +377,18 @@ public class MatchingService {
}
// Bonus pour proposition récente
long joursDepuisCreation =
java.time.Duration.between(proposition.getDateCreation(), LocalDateTime.now()).toDays();
if (joursDepuisCreation <= 30) {
bonus += 3.0;
if (proposition.getDateCreation() != null) {
long joursDepuisCreation = java.time.Duration.between(proposition.getDateCreation(), LocalDateTime.now()).toDays();
if (joursDepuisCreation <= 30) {
bonus += 3.0;
}
}
return bonus;
}
/** Calcule le malus de délai */
private double calculerMalusDelai(DemandeAideDTO demande, PropositionAideDTO proposition) {
private double calculerMalusDelai(DemandeAideResponse demande, PropositionAideResponse proposition) {
double malus = 0.0;
// Malus si la demande est en retard
@@ -397,7 +397,8 @@ public class MatchingService {
}
// Malus si la proposition a un délai de réponse long
if (proposition.getDelaiReponseHeures() > 168) { // Plus d'une semaine
if (proposition.getDelaiReponseHeures() != null && proposition.getDelaiReponseHeures() > 168) { // Plus d'une
// semaine
malus += 3.0;
}
@@ -407,7 +408,7 @@ public class MatchingService {
// === MÉTHODES UTILITAIRES ===
/** Recherche des propositions par catégorie */
private List<PropositionAideDTO> rechercherParCategorie(String categorie) {
private List<PropositionAideResponse> rechercherParCategorie(String categorie) {
Map<String, Object> filtres = Map.of("estDisponible", true);
return propositionAideService.rechercherAvecFiltres(filtres).stream()
@@ -417,10 +418,10 @@ public class MatchingService {
/** Classe interne pour stocker les résultats de matching */
private static class ResultatMatching {
final PropositionAideDTO proposition;
final PropositionAideResponse proposition;
final double score;
ResultatMatching(PropositionAideDTO proposition, double score) {
ResultatMatching(PropositionAideResponse proposition, double score) {
this.proposition = proposition;
this.score = score;
}