Initial commit
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
package com.pmumali.simple.service;
|
||||
|
||||
import com.pmumali.simple.model.Cheval;
|
||||
import com.pmumali.simple.model.Combinaison;
|
||||
import com.pmumali.simple.model.Course;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CalculRapportService {
|
||||
|
||||
public Map<Combinaison, Double> calculerRapports(
|
||||
List<Combinaison> combinaisonsPayables,
|
||||
double masseAPartager) {
|
||||
|
||||
Map<Combinaison, Double> rapports = new HashMap<>();
|
||||
|
||||
if (combinaisonsPayables.size() == 1) {
|
||||
// Cas arrivée normale
|
||||
double rapport = masseAPartager / combinaisonsPayables.get(0).getNombreMises();
|
||||
rapports.put(combinaisonsPayables.get(0), Math.max(1.1, rapport));
|
||||
} else {
|
||||
// Cas Dead-Heat
|
||||
double beneficeParCombinaison = masseAPartager / combinaisonsPayables.size();
|
||||
|
||||
for (Combinaison combinaison : combinaisonsPayables) {
|
||||
double rapport = beneficeParCombinaison / combinaison.getNombreMises();
|
||||
rapports.put(combinaison, Math.max(1.1, rapport));
|
||||
}
|
||||
}
|
||||
|
||||
return rapports;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si une combinaison est gagnante selon les positions d'arrivée
|
||||
*/
|
||||
public boolean isCombinaisonGagnante(Combinaison combinaison, List<Cheval> classement) {
|
||||
if (classement.size() < 2) return false;
|
||||
|
||||
Cheval c1 = combinaison.getCheval1();
|
||||
Cheval c2 = combinaison.getCheval2();
|
||||
|
||||
// Les deux chevaux doivent être dans les 2 premiers (ordre quelconque)
|
||||
return classement.subList(0, 2).contains(c1) &&
|
||||
classement.subList(0, 2).contains(c2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule le nombre de mises pour une combinaison donnée
|
||||
*/
|
||||
public long compterMisesCombinaison(Course course, Combinaison combinaison) {
|
||||
return course.getParis().stream()
|
||||
.filter(p -> p.getChevauxJumeles().containsAll(
|
||||
List.of(combinaison.getCheval1(), combinaison.getCheval2())
|
||||
))
|
||||
.count();
|
||||
}
|
||||
|
||||
|
||||
public Map<Combinaison, Double> calculerRapportsJumelePlace(
|
||||
List<Combinaison> combinaisonsPayables,
|
||||
double masseAPartager) {
|
||||
|
||||
Map<Combinaison, Double> rapports = new HashMap<>();
|
||||
|
||||
if (combinaisonsPayables.isEmpty()) {
|
||||
return rapports;
|
||||
}
|
||||
|
||||
// Article 5a: Arrivée normale - division en 3 parts égales
|
||||
if (!combinaisonsPayables.get(0).getCourse().isDeadHeat()) {
|
||||
double part = masseAPartager / 3;
|
||||
|
||||
for (Combinaison combinaison : combinaisonsPayables) {
|
||||
double rapport = part / compterMisesCombinaison(combinaison);
|
||||
rapports.put(combinaison, Math.max(RAPPORT_MINIMUM, rapport));
|
||||
}
|
||||
|
||||
return rapports;
|
||||
}
|
||||
|
||||
// Article 5b: Dead-Heat
|
||||
Course course = combinaisonsPayables.get(0).getCourse();
|
||||
Map<Integer, List<Cheval>> parPosition = course.getChevaux().stream()
|
||||
.filter(c -> !c.isEstNonPartant())
|
||||
.collect(Collectors.groupingBy(Cheval::getPositionArrivee));
|
||||
|
||||
List<Cheval> premiers = parPosition.getOrDefault(1, Collections.emptyList());
|
||||
List<Cheval> deuxiemes = parPosition.getOrDefault(2, Collections.emptyList());
|
||||
List<Cheval> troisiemes = parPosition.getOrDefault(3, Collections.emptyList());
|
||||
|
||||
// Article 5b1: Dead-Heat 3+ premiers
|
||||
if (premiers.size() >= 3) {
|
||||
double part = masseAPartager / combinaisonsPayables.size();
|
||||
for (Combinaison combinaison : combinaisonsPayables) {
|
||||
double rapport = part / compterMisesCombinaison(combinaison);
|
||||
rapports.put(combinaison, Math.max(RAPPORT_MINIMUM, rapport));
|
||||
}
|
||||
return rapports;
|
||||
}
|
||||
|
||||
// Article 5b2: Dead-Heat 2 premiers + 1+ troisième
|
||||
if (premiers.size() == 2 && !troisiemes.isEmpty()) {
|
||||
// Répartition en 3 tiers
|
||||
double tiers = masseAPartager / 3;
|
||||
|
||||
// 1er tiers: combinaison des 2 premiers
|
||||
Combinaison combinaisonPremiers = new Combinaison(premiers.get(0), premiers.get(1));
|
||||
double rapportPremiers = tiers / compterMisesCombinaison(combinaisonPremiers);
|
||||
rapports.put(combinaisonPremiers, Math.max(RAPPORT_MINIMUM, rapportPremiers));
|
||||
|
||||
// 2ème tiers: premier1 avec troisièmes
|
||||
double partParCombinaison = tiers / troisiemes.size();
|
||||
for (Cheval troisieme : troisiemes) {
|
||||
Combinaison combinaison = new Combinaison(premiers.get(0), troisieme);
|
||||
double rapport = partParCombinaison / compterMisesCombinaison(combinaison);
|
||||
rapports.put(combinaison, Math.max(RAPPORT_MINIMUM, rapport));
|
||||
}
|
||||
|
||||
// 3ème tiers: premier2 avec troisièmes
|
||||
for (Cheval troisieme : troisiemes) {
|
||||
Combinaison combinaison = new Combinaison(premiers.get(1), troisieme);
|
||||
double rapport = partParCombinaison / compterMisesCombinaison(combinaison);
|
||||
rapports.put(combinaison, Math.max(RAPPORT_MINIMUM, rapport));
|
||||
}
|
||||
|
||||
return rapports;
|
||||
}
|
||||
|
||||
// ... autres cas Dead-Heat (implémenter de manière similaire)
|
||||
|
||||
return rapports;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.pmumali.simple.service;
|
||||
|
||||
import com.pmumali.simple.model.Cheval;
|
||||
import com.pmumali.simple.model.Combinaison;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class FormulaireService {
|
||||
|
||||
public double calculerCoutFormule(int nombreChevaux, boolean formuleComplete) {
|
||||
// Article 7: Tableaux des combinaisons
|
||||
int nbCombinaisons = formuleComplete ?
|
||||
nombreChevaux * (nombreChevaux - 1) / 2 :
|
||||
nombreChevaux;
|
||||
|
||||
return nbCombinaisons * 500; // 500 FCFA par combinaison
|
||||
}
|
||||
|
||||
public List<Combinaison> genererCombinaisonsFormule(
|
||||
List<Cheval> chevauxSelectionnes,
|
||||
boolean formuleComplete) {
|
||||
|
||||
List<Combinaison> combinaisons = new ArrayList<>();
|
||||
|
||||
if (formuleComplete) {
|
||||
// Toutes les combinaisons 2 à 2
|
||||
for (int i = 0; i < chevauxSelectionnes.size(); i++) {
|
||||
for (int j = i + 1; j < chevauxSelectionnes.size(); j++) {
|
||||
combinaisons.add(new Combinaison(
|
||||
chevauxSelectionnes.get(i),
|
||||
chevauxSelectionnes.get(j)
|
||||
));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Formule simplifiée (champ total/partiel)
|
||||
Cheval base = chevauxSelectionnes.get(0);
|
||||
for (int i = 1; i < chevauxSelectionnes.size(); i++) {
|
||||
combinaisons.add(new Combinaison(base, chevauxSelectionnes.get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
return combinaisons;
|
||||
}
|
||||
}
|
||||
309
src/main/java/com/pmumali/simple/service/PariService.java
Normal file
309
src/main/java/com/pmumali/simple/service/PariService.java
Normal file
@@ -0,0 +1,309 @@
|
||||
package com.pmumali.simple.service;
|
||||
|
||||
import com.pmumali.simple.dto.CombinaisonDto;
|
||||
import com.pmumali.model.*;
|
||||
import com.pmumali.simple.repository.ChevalRepository;
|
||||
import com.pmumali.simple.repository.CourseRepository;
|
||||
import com.pmumali.simple.repository.PariRepository;
|
||||
import com.pmumali.simple.model.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class PariService {
|
||||
|
||||
@Autowired
|
||||
private PariRepository pariRepository;
|
||||
|
||||
@Autowired
|
||||
private CourseRepository courseRepository;
|
||||
|
||||
@Autowired
|
||||
private ChevalRepository chevalRepository;
|
||||
|
||||
|
||||
public Pari placerPariJumele(Long clientId, Long courseId,
|
||||
List<Long> chevauxIds, double montant) {
|
||||
// Vérification mise minimum
|
||||
if (montant < 500) {
|
||||
throw new IllegalArgumentException("La mise minimale est de 500 FCFA");
|
||||
}
|
||||
|
||||
// Vérification limite de paris (20x mise min = 10 000 FCFA)
|
||||
double totalParisClient = pariRepository
|
||||
.sumByClientAndCourse(clientId, courseId);
|
||||
|
||||
if (totalParisClient + montant > 10000) {
|
||||
throw new IllegalArgumentException("Limite de mise dépassée");
|
||||
}
|
||||
|
||||
// Création du pari
|
||||
Pari pari = new Pari();
|
||||
// ... initialisation
|
||||
|
||||
return pariRepository.save(pari);
|
||||
}
|
||||
|
||||
public ResultatCourse calculerResultats(Long courseId) throws Exception {
|
||||
Course course = courseRepository.findById(courseId)
|
||||
.orElseThrow(() -> new Exception("Course non trouvée"));
|
||||
|
||||
// Implémentation des règles de calcul
|
||||
return calculerResultatsSelonReglement(course);
|
||||
}
|
||||
|
||||
private ResultatCourse calculerResultatsSelonReglement(Course course) {
|
||||
ResultatCourse resultat = new ResultatCourse();
|
||||
|
||||
// 1. Vérifier les non-partants (Article 4)
|
||||
List<Cheval> nonPartants = course.getChevaux().stream()
|
||||
.filter(Cheval::isEstNonPartant)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 2. Calcul selon Dead-Heat (Article 3)
|
||||
if (course.isDeadHeat()) {
|
||||
calculerDeadHeat(resultat, course);
|
||||
} else {
|
||||
calculerArriveeNormale(resultat, course);
|
||||
}
|
||||
|
||||
// 3. Appliquer tirelire si nécessaire (Article 9)
|
||||
if (resultat.getCombinaisonsPayables().isEmpty()) {
|
||||
resultat.setTirelire(true);
|
||||
}
|
||||
|
||||
return resultat;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Détermine les combinaisons payables selon les règles du PMU Mali
|
||||
*/
|
||||
private List<Combinaison> determinerCombinaisonsPayables(Course course) {
|
||||
List<Cheval> chevauxArrives = course.getChevaux().stream()
|
||||
.filter(c -> !c.isEstNonPartant())
|
||||
.sorted(Comparator.comparingInt(Cheval::getPositionArrivee))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Article 4: Remboursement si non-partant
|
||||
if (course.getChevaux().stream().anyMatch(Cheval::isEstNonPartant)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// Article 3: Gestion Dead-Heat
|
||||
if (course.isDeadHeat()) {
|
||||
return calculerCombinaisonsDeadHeat(chevauxArrives);
|
||||
}
|
||||
|
||||
// Article 5: Arrivée normale
|
||||
return calculerCombinaisonsNormales(chevauxArrives);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implémentation de l'article 3 - Cas Dead-Heat
|
||||
*/
|
||||
private List<Combinaison> calculerCombinaisonsDeadHeat(List<Cheval> chevauxArrives) {
|
||||
List<Combinaison> combinaisons = new ArrayList<>();
|
||||
|
||||
// Groupes par position d'arrivée
|
||||
Map<Integer, List<Cheval>> parPosition = chevauxArrives.stream()
|
||||
.collect(Collectors.groupingBy(Cheval::getPositionArrivee));
|
||||
|
||||
List<Cheval> premiers = parPosition.getOrDefault(1, Collections.emptyList());
|
||||
List<Cheval> deuxiemes = parPosition.getOrDefault(2, Collections.emptyList());
|
||||
|
||||
// Cas Dead-Heat premier place (Article 3a)
|
||||
if (premiers.size() >= 2) {
|
||||
for (int i = 0; i < premiers.size(); i++) {
|
||||
for (int j = i + 1; j < premiers.size(); j++) {
|
||||
combinaisons.add(new Combinaison(premiers.get(i), premiers.get(j)));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Cas Dead-Heat deuxième place (Article 3b)
|
||||
else if (deuxiemes.size() >= 2) {
|
||||
Cheval premier = premiers.get(0);
|
||||
for (Cheval deuxieme : deuxiemes) {
|
||||
combinaisons.add(new Combinaison(premier, deuxieme));
|
||||
}
|
||||
}
|
||||
|
||||
return combinaisons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implémentation de l'article 5 - Arrivée normale
|
||||
*/
|
||||
private List<Combinaison> calculerCombinaisonsNormales(List<Cheval> chevauxArrives) {
|
||||
if (chevauxArrives.size() < 2) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
Cheval premier = chevauxArrives.get(0);
|
||||
Cheval deuxieme = chevauxArrives.get(1);
|
||||
|
||||
return List.of(new Combinaison(premier, deuxieme));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule la masse à partager selon l'article 5
|
||||
*/
|
||||
private double calculerMasseAPartager(Course course, List<Combinaison> combinaisonsPayables) {
|
||||
double totalEnjeux = course.getParis().stream()
|
||||
.mapToDouble(Pari::getMontantMise)
|
||||
.sum();
|
||||
|
||||
double montantRembourse = course.getParis().stream()
|
||||
.filter(p -> p.getChevauxJumeles().stream().anyMatch(Cheval::isEstNonPartant))
|
||||
.mapToDouble(Pari::getMontantMise)
|
||||
.sum();
|
||||
|
||||
// Article 5: MAP = RNET - MREMB - PRELEV
|
||||
double prelevementsLegaux = totalEnjeux * 0.15; // Exemple: 15% de prélèvement
|
||||
return totalEnjeux - montantRembourse - prelevementsLegaux;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convertit les combinaisons en DTO pour la réponse API
|
||||
*/
|
||||
private List<CombinaisonDto> convertToDto(List<Combinaison> combinaisons) {
|
||||
return combinaisons.stream()
|
||||
.map(c -> new CombinaisonDto(
|
||||
c.getCheval1().getNom(),
|
||||
c.getCheval2().getNom(),
|
||||
c.getNombreMises()
|
||||
))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convertit les rapports en format lisible
|
||||
*/
|
||||
private Map<String, Double> convertRapports(Map<Combinaison, Double> rapports) {
|
||||
return rapports.entrySet().stream()
|
||||
.collect(Collectors.toMap(
|
||||
e -> e.getKey().getCheval1().getNom() + "-" + e.getKey().getCheval2().getNom(),
|
||||
Map.Entry::getValue
|
||||
));
|
||||
}
|
||||
|
||||
private List<Combinaison> determinerCombinaisonsPayablesJumelePlace(Course course) {
|
||||
List<Cheval> chevauxArrives = course.getChevaux().stream()
|
||||
.filter(c -> !c.isEstNonPartant())
|
||||
.sorted(Comparator.comparingInt(Cheval::getPositionArrivee))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Article 4: Remboursement si non-partant
|
||||
if (course.getChevaux().stream().anyMatch(Cheval::isEstNonPartant)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// Article 8: Moins de 3 chevaux arrivés
|
||||
if (chevauxArrives.size() < 3) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// Article 3: Gestion Dead-Heat
|
||||
if (course.isDeadHeat()) {
|
||||
return calculerCombinaisonsDeadHeatJumelePlace(chevauxArrives);
|
||||
}
|
||||
|
||||
// Cas normal - Article 1
|
||||
return calculerCombinaisonsNormalesJumelePlace(chevauxArrives);
|
||||
}
|
||||
|
||||
private List<Combinaison> calculerCombinaisonsNormalesJumelePlace(List<Cheval> chevauxArrives) {
|
||||
List<Combinaison> combinaisons = new ArrayList<>();
|
||||
Cheval premier = chevauxArrives.get(0);
|
||||
Cheval deuxieme = chevauxArrives.get(1);
|
||||
Cheval troisieme = chevauxArrives.get(2);
|
||||
|
||||
// Toutes les combinaisons 2 parmi 3
|
||||
combinaisons.add(new Combinaison(premier, deuxieme));
|
||||
combinaisons.add(new Combinaison(premier, troisieme));
|
||||
combinaisons.add(new Combinaison(deuxieme, troisieme));
|
||||
|
||||
return combinaisons;
|
||||
}
|
||||
|
||||
private List<Combinaison> calculerCombinaisonsDeadHeatJumelePlace(List<Cheval> chevauxArrives) {
|
||||
List<Combinaison> combinaisons = new ArrayList<>();
|
||||
Map<Integer, List<Cheval>> parPosition = chevauxArrives.stream()
|
||||
.collect(Collectors.groupingBy(Cheval::getPositionArrivee));
|
||||
|
||||
List<Cheval> premiers = parPosition.getOrDefault(1, Collections.emptyList());
|
||||
List<Cheval> deuxiemes = parPosition.getOrDefault(2, Collections.emptyList());
|
||||
List<Cheval> troisiemes = parPosition.getOrDefault(3, Collections.emptyList());
|
||||
|
||||
// Article 3a: Dead-Heat à la première place (3+ chevaux)
|
||||
if (premiers.size() >= 3) {
|
||||
for (int i = 0; i < premiers.size(); i++) {
|
||||
for (int j = i + 1; j < premiers.size(); j++) {
|
||||
combinaisons.add(new Combinaison(premiers.get(i), premiers.get(j)));
|
||||
}
|
||||
}
|
||||
return combinaisons;
|
||||
}
|
||||
|
||||
// Article 3b: Dead-Heat 2 premiers + 1+ troisième
|
||||
if (premiers.size() == 2 && !troisiemes.isEmpty()) {
|
||||
// Combinaison des deux premiers
|
||||
combinaisons.add(new Combinaison(premiers.get(0), premiers.get(1)));
|
||||
|
||||
// Combinaisons premier1 avec troisièmes
|
||||
for (Cheval troisieme : troisiemes) {
|
||||
combinaisons.add(new Combinaison(premiers.get(0), troisieme));
|
||||
}
|
||||
|
||||
// Combinaisons premier2 avec troisièmes
|
||||
for (Cheval troisieme : troisiemes) {
|
||||
combinaisons.add(new Combinaison(premiers.get(1), troisieme));
|
||||
}
|
||||
return combinaisons;
|
||||
}
|
||||
|
||||
// Article 3c: Dead-Heat à la deuxième place
|
||||
if (!deuxiemes.isEmpty() && deuxiemes.size() >= 2) {
|
||||
Cheval premier = premiers.get(0);
|
||||
|
||||
// Combinaisons premier avec deuxièmes
|
||||
for (Cheval deuxieme : deuxiemes) {
|
||||
combinaisons.add(new Combinaison(premier, deuxieme));
|
||||
}
|
||||
|
||||
// Combinaisons deuxièmes entre eux
|
||||
for (int i = 0; i < deuxiemes.size(); i++) {
|
||||
for (int j = i + 1; j < deuxiemes.size(); j++) {
|
||||
combinaisons.add(new Combinaison(deuxiemes.get(i), deuxiemes.get(j)));
|
||||
}
|
||||
}
|
||||
return combinaisons;
|
||||
}
|
||||
|
||||
// Article 3d: Dead-Heat à la troisième place
|
||||
if (!troisiemes.isEmpty() && troisiemes.size() >= 2) {
|
||||
Cheval premier = premiers.get(0);
|
||||
Cheval deuxieme = deuxiemes.get(0);
|
||||
|
||||
// Combinaison premier-deuxième
|
||||
combinaisons.add(new Combinaison(premier, deuxieme));
|
||||
|
||||
// Combinaisons premier avec troisièmes
|
||||
for (Cheval troisieme : troisiemes) {
|
||||
combinaisons.add(new Combinaison(premier, troisieme));
|
||||
}
|
||||
|
||||
// Combinaisons deuxième avec troisièmes
|
||||
for (Cheval troisieme : troisiemes) {
|
||||
combinaisons.add(new Combinaison(deuxieme, troisieme));
|
||||
}
|
||||
return combinaisons;
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.pmumali.simple.service;
|
||||
|
||||
import com.pmumali.simple.model.Cheval;
|
||||
import com.pmumali.simple.model.Combinaison;
|
||||
import com.pmumali.simple.model.Course;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PariServiceTest {
|
||||
|
||||
@Test
|
||||
public void testCombinaisonsPayables_Normal() {
|
||||
Course course = new Course();
|
||||
course.setDeadHeat(false);
|
||||
|
||||
Cheval c1 = new Cheval(); c1.setPositionArrivee(1);
|
||||
Cheval c2 = new Cheval(); c2.setPositionArrivee(2);
|
||||
Cheval c3 = new Cheval(); c3.setPositionArrivee(3);
|
||||
course.setChevaux(List.of(c1, c2, c3));
|
||||
|
||||
List<Combinaison> result = pariService.determinerCombinaisonsPayablesJumelePlace(course);
|
||||
|
||||
assertEquals(3, result.size());
|
||||
assertTrue(result.contains(new Combinaison(c1, c2)));
|
||||
assertTrue(result.contains(new Combinaison(c1, c3)));
|
||||
assertTrue(result.contains(new Combinaison(c2, c3)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeadHeat_TroisPremiers() {
|
||||
Course course = new Course();
|
||||
course.setDeadHeat(true);
|
||||
|
||||
Cheval c1 = new Cheval(); c1.setPositionArrivee(1);
|
||||
Cheval c2 = new Cheval(); c2.setPositionArrivee(1);
|
||||
Cheval c3 = new Cheval(); c3.setPositionArrivee(1);
|
||||
course.setChevaux(List.of(c1, c2, c3));
|
||||
|
||||
List<Combinaison> result = pariService.determinerCombinaisonsPayablesJumelePlace(course);
|
||||
|
||||
assertEquals(3, result.size()); // C(3,2) = 3 combinaisons
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.pmumali.simple.service;
|
||||
|
||||
import com.pmumali.simple.dto.PariRequest;
|
||||
import com.pmumali.simple.exception.PariException;
|
||||
import com.pmumali.simple.model.Cheval;
|
||||
import com.pmumali.simple.model.Client;
|
||||
import com.pmumali.simple.model.Course;
|
||||
import com.pmumali.simple.model.Pari;
|
||||
import com.pmumali.simple.model.enums.TypePari;
|
||||
import com.pmumali.simple.repository.PariRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class ValidationPariService {
|
||||
@Autowired
|
||||
private PariRepository pariRepository;
|
||||
|
||||
public void validerPari(Pari pari) {
|
||||
// Article 2: Limitation des enjeux
|
||||
validerLimiteEnjeux(pari);
|
||||
|
||||
// Article 4: Chevaux non-partants
|
||||
validerChevauxPartants(pari.getCourse(), pari.getChevauxJumeles());
|
||||
|
||||
// Article 6: Formules combinées
|
||||
validerFormule(pari);
|
||||
}
|
||||
|
||||
private void validerLimiteEnjeux(Pari pari) {
|
||||
double totalMises = pariRepository
|
||||
.sumByClientAndCourse(pari.getClient().getId(), pari.getCourse().getId());
|
||||
|
||||
if (totalMises + pari.getMontantMise() > 10000) {
|
||||
throw new PariException(PariException.ErrorCode.LIMITE_MISE_DEPASSEE, "Limite de mise dépassée (20x500 FCFA maximum)");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Valide la requête de pari selon les règles métier
|
||||
*/
|
||||
public void validerPariRequest(PariRequest request) {
|
||||
if (request.getMontantMise() < 500) {
|
||||
throw new PariException(PariException.ErrorCode.MISE_MINIMALE_NON_ATTEINTE,"La mise minimale est de 500 FCFA (Article 1)");
|
||||
}
|
||||
|
||||
if (request.getChevauxIds() == null || request.getChevauxIds().size() != 2) {
|
||||
throw new PariException(PariException.ErrorCode.PARI_INVALIDE,"Un pari Jumelé Gagnant doit porter sur exactement 2 chevaux");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Valide le pari selon toutes les règles du règlement
|
||||
*/
|
||||
public void validerPari(Client client, Course course, List<Cheval> chevaux, double montantMise) {
|
||||
// Article 2: Limitation des enjeux
|
||||
validerLimiteEnjeux(client, course, montantMise);
|
||||
|
||||
// Article 4: Chevaux non-partants
|
||||
validerChevauxPartants(course, chevaux);
|
||||
|
||||
// Article 10: Course non annulée
|
||||
if (course.isEstAnnulee()) {
|
||||
throw new PariException(PariException.ErrorCode.COURSE_ANNULEE,"Course annulée - tous les paris seront remboursés (Article 8)");
|
||||
}
|
||||
|
||||
// Vérifie que les chevaux appartiennent bien à la course
|
||||
if (chevaux.stream().anyMatch(c -> !c.getCourse().equals(course))) {
|
||||
throw new PariException(PariException.ErrorCode.CHEVAL_NON_PARTANT,"Un ou plusieurs chevaux ne font pas partie de cette course");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implémentation de l'article 2 - Limitation des enjeux
|
||||
*/
|
||||
private void validerLimiteEnjeux(Client client, Course course, double nouvelleMise) {
|
||||
double totalMises = pariRepository.sumByClientAndCourse(client.getId(), course.getId());
|
||||
double limite = 20 * 500; // 20 fois la mise de base (500 FCFA)
|
||||
|
||||
if (totalMises + nouvelleMise > limite) {
|
||||
throw new PariException( PariException.ErrorCode.LIMITE_MISE_DEPASSEE,
|
||||
String.format("Limite de mise dépassée (max %,.0f FCFA par course selon Article 2)", limite)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implémentation de l'article 4 - Chevaux non-partants
|
||||
*/
|
||||
private void validerChevauxPartants(Course course, List<Cheval> chevaux) {
|
||||
if (chevaux.stream().anyMatch(Cheval::isEstNonPartant)) {
|
||||
throw new PariException(PariException.ErrorCode.CHEVAL_NON_PARTANT,
|
||||
"Pari non valide : un ou plusieurs chevaux sont non-partants (Article 4)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public void validerPariJumelePlace(Pari pari) {
|
||||
// Article 1: Vérification que c'est bien un Jumelé Placé
|
||||
if (pari.getTypePari() != TypePari.JUMELEC_PLACE) {
|
||||
throw new PariException(PariException.ErrorCode.FORMULE_INVALIDE ,"Ce n'est pas un pari Jumelé Placé");
|
||||
}
|
||||
|
||||
// Article 2: Limitation des enjeux (identique)
|
||||
validerLimiteEnjeux(pari.getClient(), pari.getCourse(), pari.getMontantMise());
|
||||
|
||||
// Article 4: Chevaux non-partants
|
||||
if (pari.getChevauxJumeles().stream().anyMatch(Cheval::isEstNonPartant)) {
|
||||
throw new PariException(PariException.ErrorCode.CHEVAL_NON_PARTANT,"Pari non valide: cheval non-partant (Article 4)");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user