Sync: code local unifié
Synchronisation du code source local (fait foi). Signed-off-by: lions dev Team
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user