Fin intégration quinte plus
This commit is contained in:
@@ -1,40 +0,0 @@
|
||||
package com.pmu.jumele.controller;
|
||||
|
||||
import com.pmu.jumele.dto.*;
|
||||
import com.pmu.jumele.service.JumeleGagnantService;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/jumele")
|
||||
public class JumeleController {
|
||||
|
||||
private final JumeleGagnantService service;
|
||||
|
||||
public JumeleController(JumeleGagnantService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
@PostMapping("/pari")
|
||||
public ResponseEntity<?> enregistrerPari(@RequestBody PariRequest req) {
|
||||
try {
|
||||
Map<String, Object> res = service.enregistrerPari(req);
|
||||
return ResponseEntity.ok(res);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
return ResponseEntity.badRequest().body(Map.of("error", ex.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/resultat")
|
||||
public ResponseEntity<?> calculer(@RequestBody PositionsRequest req) {
|
||||
Map<String, Object> res = service.calculer(req);
|
||||
return ResponseEntity.ok(res);
|
||||
}
|
||||
|
||||
@GetMapping("/paris/{courseId}")
|
||||
public ResponseEntity<?> listerParis(@PathVariable String courseId) {
|
||||
return ResponseEntity.ok(service.getClass().getName()); // placeholder: add endpoint to fetch from repo if needed
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package com.pmu.jumele.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class PaiementResponse {
|
||||
private String parieur;
|
||||
private boolean gagnant;
|
||||
private BigDecimal gain;
|
||||
private String combinaison;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package com.pmu.jumele.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class PariRequest {
|
||||
private String parieur;
|
||||
private String courseId;
|
||||
private List<Integer> chevaux; // can be 2 (unit), or list to generate combinations (combiné/champ)
|
||||
private int mise; // montant total for the formula (we'll split for unitary bets)
|
||||
private String formuleType; // "unitaire","combine","champ_total","champ_partiel"
|
||||
private List<Integer> champSelection; // used for champ partiel (if formuleType == champ_partiel)
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package com.pmu.jumele.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class PositionsRequest {
|
||||
private String courseId;
|
||||
// positions: list of positions; each position is a list of horse numbers (to represent dead-heat)
|
||||
// positions.get(0) = list of horses classified first
|
||||
// positions.get(1) = list of horses classified second, etc.
|
||||
private List<List<Integer>> positions;
|
||||
private List<Integer> nonPartants; // optional
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package com.pmu.jumele.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Entity
|
||||
@Table(name = "paris_jumele")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class PariEntity {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String parieur;
|
||||
private String courseId;
|
||||
// store combinaison as "a,b"
|
||||
private String combinaison;
|
||||
private BigDecimal mise;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package com.pmu.jumele.repository;
|
||||
|
||||
import com.pmu.jumele.entity.PariEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import java.util.List;
|
||||
|
||||
public interface PariRepository extends JpaRepository<PariEntity, Long> {
|
||||
List<PariEntity> findByCourseId(String courseId);
|
||||
List<PariEntity> findByCourseIdAndParieurAndCombinaison(String courseId, String parieur, String combinaison);
|
||||
}
|
||||
@@ -1,290 +0,0 @@
|
||||
package com.pmu.jumele.service;
|
||||
|
||||
import com.pmu.jumele.dto.*;
|
||||
import com.pmu.jumele.entity.PariEntity;
|
||||
import com.pmu.jumele.repository.PariRepository;
|
||||
import com.pmu.jumele.util.CombinaisonUtil;
|
||||
import com.pmu.jumele.util.FormulesTable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class JumeleGagnantService {
|
||||
|
||||
private final PariRepository pariRepository;
|
||||
private static final BigDecimal MISE_MIN = BigDecimal.valueOf(500);
|
||||
private static final int PLAFOND_MULT = 20;
|
||||
private static final BigDecimal RAPPORT_MIN = BigDecimal.valueOf(1.1);
|
||||
private static final int SCALE = 8;
|
||||
|
||||
// prélèvements légaux (paramétrable)
|
||||
private BigDecimal prelevements = BigDecimal.ZERO;
|
||||
|
||||
public JumeleGagnantService(PariRepository pariRepository) {
|
||||
this.pariRepository = pariRepository;
|
||||
}
|
||||
|
||||
public void setPrelevements(BigDecimal p) { this.prelevements = p == null ? BigDecimal.ZERO : p; }
|
||||
|
||||
/**
|
||||
* Enregistre un pari request — peut être unitaire ou une formule.
|
||||
* Pour formules, convertit en paris unitaires (combinaisons) et enregistre chaque unité en respectant le tableau de valeurs.
|
||||
*/
|
||||
@Transactional
|
||||
public Map<String, Object> enregistrerPari(PariRequest req) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
// validation minimale
|
||||
if (req.getMise() < MISE_MIN.intValue()) throw new IllegalArgumentException("Mise minimum = 500");
|
||||
|
||||
// calculer unités
|
||||
Set<Set<Integer>> combsToPlace = new HashSet<>();
|
||||
if ("unitaire".equalsIgnoreCase(req.getFormuleType()) || req.getChevaux().size() == 2) {
|
||||
if (req.getChevaux().size() != 2) throw new IllegalArgumentException("Pari unitaire doit contenir 2 chevaux");
|
||||
combsToPlace.add(new HashSet<>(req.getChevaux()));
|
||||
} else if ("combine".equalsIgnoreCase(req.getFormuleType())) {
|
||||
combsToPlace = CombinaisonUtil.allPairs(req.getChevaux());
|
||||
} else if ("champ_total".equalsIgnoreCase(req.getFormuleType())) {
|
||||
// champ total: base x all others; req.getChevaux() must contain base + others?
|
||||
// For API simplicity: req.chevaux contains full list of partants; champ_total of a base is handled client-side by sending combos
|
||||
combsToPlace = CombinaisonUtil.allPairs(req.getChevaux());
|
||||
} else if ("champ_partiel".equalsIgnoreCase(req.getFormuleType())) {
|
||||
if (req.getChampSelection() == null || req.getChampSelection().isEmpty())
|
||||
throw new IllegalArgumentException("Champ partiel nécessite champSelection");
|
||||
// base is first element of req.chevaux
|
||||
Integer base = req.getChevaux().get(0);
|
||||
for (Integer c : req.getChampSelection()) {
|
||||
combsToPlace.add(new HashSet<>(Arrays.asList(base, c)));
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("FormuleType inconnu");
|
||||
}
|
||||
|
||||
// valeur unitaire par combinaison : on divise la mise totale proportionnellement selon tableau ou uniformément
|
||||
// Simplicité : on divise la mise totale uniformément par le nombre de combinaisons
|
||||
int nbComb = combsToPlace.size();
|
||||
BigDecimal totalMise = BigDecimal.valueOf(req.getMise());
|
||||
BigDecimal miseUnitaire = totalMise.divide(BigDecimal.valueOf(nbComb), SCALE, RoundingMode.HALF_UP);
|
||||
|
||||
// enregistrement avec respect du plafond 20 prises par parieur sur même combinaison
|
||||
BigDecimal plafond = MISE_MIN.multiply(BigDecimal.valueOf(PLAFOND_MULT));
|
||||
BigDecimal totalRembourse = BigDecimal.ZERO;
|
||||
List<PariEntity> enregistrés = new ArrayList<>();
|
||||
|
||||
for (Set<Integer> comb : combsToPlace) {
|
||||
String combStr = CombinaisonUtil.combToString(comb);
|
||||
// somme déjà engagée par ce parieur sur cette combinaison dans la même course
|
||||
List<PariEntity> deja = pariRepository.findByCourseIdAndParieurAndCombinaison(req.getCourseId(), req.getParieur(), combStr);
|
||||
BigDecimal dejaTotal = deja.stream().map(PariEntity::getMise).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
BigDecimal disponible = plafond.subtract(dejaTotal);
|
||||
BigDecimal toRecord = miseUnitaire.min(disponible);
|
||||
BigDecimal toRefund = miseUnitaire.subtract(toRecord);
|
||||
if (toRecord.compareTo(BigDecimal.ZERO) > 0) {
|
||||
PariEntity e = PariEntity.builder()
|
||||
.parieur(req.getParieur())
|
||||
.courseId(req.getCourseId())
|
||||
.combinaison(combStr)
|
||||
.mise(toRecord)
|
||||
.build();
|
||||
pariRepository.save(e);
|
||||
enregistrés.add(e);
|
||||
}
|
||||
if (toRefund.compareTo(BigDecimal.ZERO) > 0) {
|
||||
totalRembourse = totalRembourse.add(toRefund);
|
||||
}
|
||||
}
|
||||
|
||||
result.put("enregistres", enregistrés.size());
|
||||
result.put("a_rembourser", totalRembourse.setScale(2, RoundingMode.HALF_UP));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enregistre le résultat via PositionsRequest (positions = list of lists to express dead-heats).
|
||||
*/
|
||||
public void enregistrerResultat(PositionsRequest req) {
|
||||
// store positions in memory — for calculation we require positions per course; for simplicity, pass positions during calcul.
|
||||
// In this implementation, we'll pass PositionsRequest directly to calculer.
|
||||
// To persist results, create a ResultEntity (omitted here).
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculer paiements pour une course en fournissant PositionsRequest (dead-heat possible).
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public Map<String, Object> calculer(PositionsRequest positionsReq) {
|
||||
Map<String, Object> out = new HashMap<>();
|
||||
String courseId = positionsReq.getCourseId();
|
||||
List<PariEntity> parisCourse = pariRepository.findByCourseId(courseId);
|
||||
List<Integer> nonPartants = positionsReq.getNonPartants() == null ? Collections.emptyList() : positionsReq.getNonPartants();
|
||||
|
||||
// 1) Générer combinaisons payables selon article 3:
|
||||
// positionsReq.positions[0] => premiers (list), positionsReq.positions[1] => deuxiemes (list), etc.
|
||||
List<List<Integer>> positions = positionsReq.getPositions();
|
||||
if (positions == null || positions.size() < 2) {
|
||||
// moins de deux classés => tout remboursé (Article 8)
|
||||
List<PaiementResponse> remboursements = parisCourse.stream()
|
||||
.map(p -> new PaiementResponse(p.getParieur(), false, p.getMise(), p.getCombinaison()))
|
||||
.collect(Collectors.toList());
|
||||
out.put("paiements", remboursements);
|
||||
out.put("cagnotte", BigDecimal.ZERO.setScale(2));
|
||||
return out;
|
||||
}
|
||||
|
||||
Set<Set<Integer>> combPayables = new HashSet<>();
|
||||
// a) dead-heat premiers (positions[0] size >=2) -> toutes combinaisons 2-à-2 parmi premiers
|
||||
List<Integer> premiers = positions.get(0);
|
||||
if (premiers.size() >= 2) {
|
||||
combPayables.addAll(CombinaisonUtil.allPairs(premiers));
|
||||
}
|
||||
|
||||
// b) dead-heat seconds (positions[1] size >=2) -> all pairs combining each premier with each second
|
||||
List<Integer> deuxiemes = positions.get(1);
|
||||
if (deuxiemes.size() >= 1 && premiers.size() >= 1) {
|
||||
for (Integer p : premiers) {
|
||||
for (Integer d : deuxiemes) {
|
||||
combPayables.add(new HashSet<>(Arrays.asList(p, d)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// c) if positions[0].size()==1 and positions[1].size()==1 and no dead-heat: comb = {1er,2e}
|
||||
if (premiers.size() == 1 && deuxiemes.size() == 1) {
|
||||
combPayables.add(new HashSet<>(Arrays.asList(premiers.get(0), deuxiemes.get(0))));
|
||||
}
|
||||
|
||||
// Remove combos involving non-partants -> these combos are remboursed (Article 4)
|
||||
Set<Set<Integer>> combRemb = combPayables.stream()
|
||||
.filter(c -> c.stream().anyMatch(nonPartants::contains))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// RNET = total des mises enregistrées sur la course
|
||||
BigDecimal rnet = parisCourse.stream().map(PariEntity::getMise).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
|
||||
// MREMB = montant des mises sur combRemb
|
||||
BigDecimal mremb = parisCourse.stream()
|
||||
.filter(p -> combRemb.contains(stringToComb(p.getCombinaison())))
|
||||
.map(PariEntity::getMise).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
|
||||
BigDecimal map = rnet.subtract(mremb).subtract(prelevements);
|
||||
if (map.compareTo(BigDecimal.ZERO) < 0) map = BigDecimal.ZERO;
|
||||
|
||||
// Mises par combinaison (pour combPayables not remboursed)
|
||||
Map<Set<Integer>, BigDecimal> misesParComb = new HashMap<>();
|
||||
for (Set<Integer> comb : combPayables) {
|
||||
if (combRemb.contains(comb)) {
|
||||
misesParComb.put(comb, BigDecimal.ZERO);
|
||||
} else {
|
||||
BigDecimal s = parisCourse.stream()
|
||||
.filter(p -> stringToComb(p.getCombinaison()).equals(comb))
|
||||
.map(PariEntity::getMise).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
misesParComb.put(comb, s);
|
||||
}
|
||||
}
|
||||
|
||||
// cas : aucune comb active => cagnotte
|
||||
List<Set<Integer>> combActives = misesParComb.entrySet().stream()
|
||||
.filter(e -> e.getValue().compareTo(BigDecimal.ZERO) > 0)
|
||||
.map(Map.Entry::getKey).collect(Collectors.toList());
|
||||
|
||||
BigDecimal cagnotte = BigDecimal.ZERO;
|
||||
Map<String, BigDecimal> gains = new HashMap<>();
|
||||
|
||||
if (combActives.isEmpty()) {
|
||||
cagnotte = cagnotte.add(map);
|
||||
} else if (combActives.size() == 1) {
|
||||
// cas normal unique combinaison
|
||||
Set<Integer> comb = combActives.get(0);
|
||||
BigDecimal totMise = misesParComb.get(comb);
|
||||
if (totMise.compareTo(BigDecimal.ZERO) == 0) {
|
||||
cagnotte = cagnotte.add(map);
|
||||
} else {
|
||||
BigDecimal rapport = map.divide(totMise, SCALE, RoundingMode.HALF_UP).add(BigDecimal.ONE);
|
||||
if (rapport.compareTo(RAPPORT_MIN) < 0) rapport = RAPPORT_MIN;
|
||||
// payer chaque parieur sur la combinaison
|
||||
for (PariEntity p : parisCourse) {
|
||||
if (stringToComb(p.getCombinaison()).equals(comb)) {
|
||||
BigDecimal gain = p.getMise().multiply(rapport).setScale(2, RoundingMode.HALF_UP);
|
||||
gains.put(p.getParieur(), gains.getOrDefault(p.getParieur(), BigDecimal.ZERO).add(gain));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// dead-heat / multiple combs payables
|
||||
BigDecimal totalMisesOnCombActives = combActives.stream().map(misesParComb::get).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
BigDecimal benef = map.subtract(totalMisesOnCombActives);
|
||||
if (benef.compareTo(BigDecimal.ZERO) < 0) benef = BigDecimal.ZERO;
|
||||
int nbComb = combActives.size();
|
||||
BigDecimal partParComb = benef.divide(BigDecimal.valueOf(nbComb), SCALE, RoundingMode.HALF_UP);
|
||||
|
||||
// allocate for each comb: partParComb distributed proportionally to mises on that comb
|
||||
// but first for each comb we already have misesParComb.get(comb) that were removed from the map when computing benef
|
||||
for (Set<Integer> comb : combActives) {
|
||||
BigDecimal misesThis = misesParComb.get(comb);
|
||||
if (misesThis.compareTo(BigDecimal.ZERO) == 0) continue;
|
||||
BigDecimal ratio = partParComb.divide(misesThis, SCALE, RoundingMode.HALF_UP);
|
||||
BigDecimal rapport = ratio.add(BigDecimal.ONE);
|
||||
if (rapport.compareTo(RAPPORT_MIN) < 0) rapport = RAPPORT_MIN;
|
||||
for (PariEntity p : parisCourse) {
|
||||
if (stringToComb(p.getCombinaison()).equals(comb)) {
|
||||
BigDecimal gain = p.getMise().multiply(rapport).setScale(2, RoundingMode.HALF_UP);
|
||||
gains.put(p.getParieur(), gains.getOrDefault(p.getParieur(), BigDecimal.ZERO).add(gain));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// traiter parts non couvertes (combActives avec mises 0): redistribuer their part among covered combs
|
||||
BigDecimal partsNonCover = BigDecimal.ZERO;
|
||||
for (Set<Integer> comb : combActives) {
|
||||
if (misesParComb.get(comb).compareTo(BigDecimal.ZERO) == 0) {
|
||||
partsNonCover = partsNonCover.add(partParComb);
|
||||
}
|
||||
}
|
||||
if (partsNonCover.compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal totalMisesCouvertes = combActives.stream()
|
||||
.filter(c -> misesParComb.get(c).compareTo(BigDecimal.ZERO) > 0)
|
||||
.map(misesParComb::get).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
if (totalMisesCouvertes.compareTo(BigDecimal.ZERO) == 0) {
|
||||
cagnotte = cagnotte.add(map);
|
||||
} else {
|
||||
// redistribute partsNonCover to covered combos proportionally
|
||||
for (Set<Integer> comb : combActives) {
|
||||
BigDecimal misesThis = misesParComb.get(comb);
|
||||
if (misesThis.compareTo(BigDecimal.ZERO) == 0) continue;
|
||||
BigDecimal share = partsNonCover.multiply(misesThis).divide(totalMisesCouvertes, SCALE, RoundingMode.HALF_UP);
|
||||
// distribute share proportionally to parieurs on the comb
|
||||
for (PariEntity p : parisCourse) {
|
||||
if (stringToComb(p.getCombinaison()).equals(comb)) {
|
||||
BigDecimal extra = p.getMise().multiply(share).divide(misesThis, SCALE, RoundingMode.HALF_UP).setScale(2, RoundingMode.HALF_UP);
|
||||
gains.put(p.getParieur(), gains.getOrDefault(p.getParieur(), BigDecimal.ZERO).add(extra));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build payment responses per record (each pari)
|
||||
List<PaiementResponse> payments = new ArrayList<>();
|
||||
for (PariEntity p : parisCourse) {
|
||||
BigDecimal gain = gains.getOrDefault(p.getParieur(), BigDecimal.ZERO);
|
||||
boolean gagnant = gain.compareTo(BigDecimal.ZERO) > 0;
|
||||
payments.add(new PaiementResponse(p.getParieur(), gagnant, gain, p.getCombinaison()));
|
||||
}
|
||||
|
||||
out.put("paiements", payments);
|
||||
out.put("cagnotte", cagnotte.setScale(2, RoundingMode.HALF_UP));
|
||||
out.put("rnet", rnet.setScale(2, RoundingMode.HALF_UP));
|
||||
out.put("mremb", mremb.setScale(2, RoundingMode.HALF_UP));
|
||||
out.put("map", map.setScale(2, RoundingMode.HALF_UP));
|
||||
return out;
|
||||
}
|
||||
|
||||
private Set<Integer> stringToComb(String s) {
|
||||
return Arrays.stream(s.split(",")).map(Integer::valueOf).collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package com.pmu.jumele.service;
|
||||
|
||||
import com.pmu.jumele.dto.PariRequest;
|
||||
import com.pmu.jumele.dto.PositionsRequest;
|
||||
import com.pmu.jumele.entity.PariEntity;
|
||||
import com.pmu.jumele.repository.PariRepository;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
|
||||
//@DataJpaTest
|
||||
public class JumeleGagnantServiceTest {
|
||||
|
||||
// @Autowired
|
||||
PariRepository repo;
|
||||
|
||||
// @Test
|
||||
void test_simple_flow() {
|
||||
JumeleGagnantService svc = new JumeleGagnantService(repo);
|
||||
|
||||
PariRequest pr = new PariRequest();
|
||||
pr.setParieur("Alice");
|
||||
pr.setCourseId("C1");
|
||||
pr.setChevaux(Arrays.asList(1,2));
|
||||
pr.setMise(500);
|
||||
pr.setFormuleType("unitaire");
|
||||
|
||||
svc.enregistrerPari(pr);
|
||||
|
||||
// ajouter un autre pari sur la même comb
|
||||
pr.setParieur("Bob");
|
||||
svc.enregistrerPari(pr);
|
||||
|
||||
PositionsRequest pos = new PositionsRequest();
|
||||
pos.setCourseId("C1");
|
||||
pos.setPositions(List.of(List.of(1), List.of(2))); // 1er=1 ; 2e=2
|
||||
|
||||
Map<String,Object> res = svc.calculer(pos);
|
||||
// assertThat(res).containsKey("paiements");
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package com.pmu.jumele.util;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CombinaisonUtil {
|
||||
|
||||
// génère toutes paires (unordered) d'une liste de chevaux
|
||||
public static Set<Set<Integer>> allPairs(List<Integer> chevaux) {
|
||||
Set<Set<Integer>> res = new HashSet<>();
|
||||
int n = chevaux.size();
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (int j = i+1; j < n; j++) {
|
||||
res.add(new HashSet<>(Arrays.asList(chevaux.get(i), chevaux.get(j))));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// conversion Set<Integer> -> "a,b" sorted string
|
||||
public static String combToString(Set<Integer> comb) {
|
||||
return comb.stream().sorted().map(Object::toString).collect(Collectors.joining(","));
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package com.pmu.jumele.util;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class FormulesTable {
|
||||
// For simplicity we store unit value per formula (complete combined) and simplified etc.
|
||||
// We'll only need unit price to split a given "mise" into each unit bet when user submits a formula.
|
||||
// Here we give reference table values per article 7 (unit values for "complete" formulas)
|
||||
public static final Map<Integer, BigDecimal> COMBINED_COMPLETE = new HashMap<>();
|
||||
public static final Map<Integer, BigDecimal> COMBINED_SIMPLE = new HashMap<>();
|
||||
public static final Map<Integer, BigDecimal> CHAMP_TOTAL = new HashMap<>();
|
||||
public static final Map<Integer, BigDecimal> CHAMP_PARTIEL = new HashMap<>();
|
||||
|
||||
static {
|
||||
// fill a subset (full table can be added)
|
||||
COMBINED_COMPLETE.put(3, BigDecimal.valueOf(3000));
|
||||
COMBINED_COMPLETE.put(4, BigDecimal.valueOf(6000));
|
||||
COMBINED_COMPLETE.put(5, BigDecimal.valueOf(10000));
|
||||
// ... add rest as needed
|
||||
|
||||
COMBINED_SIMPLE.put(3, BigDecimal.valueOf(1500));
|
||||
COMBINED_SIMPLE.put(4, BigDecimal.valueOf(3000));
|
||||
// ...
|
||||
|
||||
CHAMP_TOTAL.put(3, BigDecimal.valueOf(1000));
|
||||
CHAMP_TOTAL.put(4, BigDecimal.valueOf(1500));
|
||||
// ...
|
||||
|
||||
CHAMP_PARTIEL.put(3, BigDecimal.valueOf(1500));
|
||||
CHAMP_PARTIEL.put(4, BigDecimal.valueOf(2000));
|
||||
// ...
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package com.pmu.mali.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class Pari {
|
||||
private String parieur; // identifiant client ou ticket
|
||||
private int numeroCheval; // numéro du cheval parié
|
||||
private TypePari type; // SIMPLE_GAGNANT ou SIMPLE_PLACE
|
||||
private BigDecimal mise; // montant mis (ex: 500)
|
||||
private boolean nonPartant; // vrai si cheval non-partant (remboursement)
|
||||
private String ecurieId; // identifiant d'écurie / coupling (null si aucun)
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package com.pmu.mali.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class ResultatCourse {
|
||||
// listes des numéros de chevaux classés (peuvent contenir plusieurs en cas de dead-heat)
|
||||
private List<Integer> premiers; // un ou plusieurs (dead-heat)
|
||||
private List<Integer> deuxiemes; // peut être vide
|
||||
private List<Integer> troisiemes; // peut être vide
|
||||
private int nombrePartants; // nombre de chevaux engagés (programme officiel)
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
package com.pmu.mali.model;
|
||||
|
||||
public enum TypePari {
|
||||
SIMPLE_GAGNANT,
|
||||
SIMPLE_PLACE
|
||||
}
|
||||
@@ -1,367 +0,0 @@
|
||||
package com.pmu.mali.service;
|
||||
|
||||
import com.pmu.mali.model.Pari;
|
||||
import com.pmu.mali.model.ResultatCourse;
|
||||
import com.pmu.mali.model.TypePari;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Service de calcul des gains pour PARI SIMPLE (gagnant / placé)
|
||||
* Implémente écurie, dead-heat, remboursements, cagnotte, rapport min.
|
||||
*/
|
||||
@Service
|
||||
public class CalculPariService {
|
||||
|
||||
private static final BigDecimal RAPPORT_MIN = BigDecimal.valueOf(1.1); // rapport minimum
|
||||
private static final int SCALE = 6; // précision interne
|
||||
|
||||
/**
|
||||
* Résultat retourné : map parieur -> gain (montant payé, inclut la mise).
|
||||
* On retourne aussi une structure pour la cagnotte (optionnel).
|
||||
*/
|
||||
public static class ResultatCalcul {
|
||||
public Map<String, BigDecimal> gainsParParieur = new HashMap<>();
|
||||
public BigDecimal cagnotteGagnant = BigDecimal.ZERO;
|
||||
public BigDecimal cagnottePlace = BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Point d'entrée principal.
|
||||
*
|
||||
* @param paris liste de paris (SIMPLE_GAGNANT et SIMPLE_PLACE mélangés)
|
||||
* @param resultat résultat de la course
|
||||
* @param prelevements montant total prélevé légalement (sur les deux types, si applicable)
|
||||
* @return ResultatCalcul
|
||||
*/
|
||||
public ResultatCalcul calculer(List<Pari> paris, ResultatCourse resultat, BigDecimal prelevements) {
|
||||
ResultatCalcul res = new ResultatCalcul();
|
||||
|
||||
if (prelevements == null) prelevements = BigDecimal.ZERO;
|
||||
|
||||
// Séparer paris par type
|
||||
List<Pari> parisGagnant = filterByType(paris, TypePari.SIMPLE_GAGNANT);
|
||||
List<Pari> parisPlace = filterByType(paris, TypePari.SIMPLE_PLACE);
|
||||
|
||||
// 1) Remboursement des non-partants
|
||||
BigDecimal rembGagnant = rembourserNonPartants(parisGagnant, res);
|
||||
BigDecimal rembPlace = rembourserNonPartants(parisPlace, res);
|
||||
|
||||
// 2) Calcul des masses à partager (RNET - MREMB - PRELEV)
|
||||
BigDecimal masseGagnant = totalMise(parisGagnant).subtract(rembGagnant).subtract(prelevements);
|
||||
BigDecimal massePlace = totalMise(parisPlace).subtract(rembPlace).subtract(prelevements);
|
||||
|
||||
if (masseGagnant.compareTo(BigDecimal.ZERO) < 0) masseGagnant = BigDecimal.ZERO;
|
||||
if (massePlace.compareTo(BigDecimal.ZERO) < 0) massePlace = BigDecimal.ZERO;
|
||||
|
||||
// 3) Calcul gagnant (avec ecurie et dead-heat)
|
||||
calculerGagnant(parisGagnant, resultat, masseGagnant, res);
|
||||
|
||||
// 4) Calcul placé (avec règles 4-7 / >=8 et dead-heat)
|
||||
calculerPlace(parisPlace, resultat, massePlace, res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* ----------------------
|
||||
Méthodes utilitaires
|
||||
---------------------- */
|
||||
|
||||
private List<Pari> filterByType(List<Pari> all, TypePari t) {
|
||||
return all.stream().filter(p -> p.getType() == t).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private BigDecimal totalMise(List<Pari> list) {
|
||||
return list.stream().map(Pari::getMise).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rembourse toutes les mises dont pari.nonPartant == true
|
||||
* Ajoute le montant remboursé aux gains du parieur.
|
||||
* Retourne le total remboursé.
|
||||
*/
|
||||
private BigDecimal rembourserNonPartants(List<Pari> list, ResultatCalcul res) {
|
||||
return rembourserNonPartants(list, res.gainsParParieur);
|
||||
}
|
||||
|
||||
private BigDecimal rembourserNonPartants(List<Pari> list, Map<String, BigDecimal> gainsMap) {
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
for (Pari p : list) {
|
||||
if (p.isNonPartant()) {
|
||||
total = total.add(p.getMise());
|
||||
gainsMap.put(p.getParieur(),
|
||||
gainsMap.getOrDefault(p.getParieur(), BigDecimal.ZERO).add(p.getMise()));
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
/* ---------------------------------------
|
||||
Calcul du GAGNANT (inclut dead-heat/ecurie)
|
||||
--------------------------------------- */
|
||||
private void calculerGagnant(List<Pari> parisGagnant, ResultatCourse resultat, BigDecimal masse, ResultatCalcul res) {
|
||||
List<Integer> premiers = resultat.getPremiers(); // liste des premiers (1 ou plusieurs)
|
||||
if (premiers == null || premiers.isEmpty()) {
|
||||
// Aucun cheval classé -> masse va en cagnotte
|
||||
res.cagnotteGagnant = res.cagnotteGagnant.add(masse);
|
||||
return;
|
||||
}
|
||||
|
||||
// Si arrivée normale (un seul premier)
|
||||
if (premiers.size() == 1) {
|
||||
int premier = premiers.get(0);
|
||||
// déterminer écurie du cheval gagnant (s'il y a), puis totaliser mises sur l'écurie
|
||||
// On crée une clé d'ecurie : si pari.ecurieId == null, utilises le numéro du cheval comme "ecurie"
|
||||
Map<String, BigDecimal> miseParEcurie = new HashMap<>();
|
||||
for (Pari p : parisGagnant) {
|
||||
if (p.isNonPartant()) continue;
|
||||
String ecurie = keyEcurie(p);
|
||||
// Si ce cheval appartient à l'écurie gagnante OU il est lui-même le gagnant, on prendra en compte
|
||||
miseParEcurie.put(ecurie, miseParEcurie.getOrDefault(ecurie, BigDecimal.ZERO).add(p.getMise()));
|
||||
}
|
||||
|
||||
String ecurieGagnanteKey = null;
|
||||
// trouver si le gagnant a une écurie dans les paris
|
||||
Optional<Pari> anyPariSurGagnant = parisGagnant.stream()
|
||||
.filter(p -> p.getNumeroCheval() == premier && !p.isNonPartant()).findAny();
|
||||
if (anyPariSurGagnant.isPresent()) {
|
||||
ecurieGagnanteKey = keyEcurie(anyPariSurGagnant.get());
|
||||
} else {
|
||||
// pas de mise sur le gagnant -> vérifier s'il y a mises sur autres chevaux de la même écurie
|
||||
// chercher toute mise sur chevaux avec même ecurieId (si ecurie known)
|
||||
// Si pas de mise du tout sur la combinaison gagnante => cagnotte
|
||||
// So we'll compute totalMisePayable below
|
||||
}
|
||||
|
||||
// calculer total des mises payables (mises sur le cheval gagnant et, si écurie, sur ses co-écuries)
|
||||
BigDecimal totalMisePayable = BigDecimal.ZERO;
|
||||
if (ecurieGagnanteKey != null) {
|
||||
totalMisePayable = miseParEcurie.getOrDefault(ecurieGagnanteKey, BigDecimal.ZERO);
|
||||
} else {
|
||||
// pas de pari identifié sur gagnant -> peut être 0
|
||||
// cherché toutes mises portant exactement sur le cheval gagnant
|
||||
totalMisePayable = parisGagnant.stream()
|
||||
.filter(p -> p.getNumeroCheval() == premier && !p.isNonPartant())
|
||||
.map(Pari::getMise).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
}
|
||||
|
||||
if (totalMisePayable.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
// Aucun pari sur le gagnant (ni sur son ecurie) => vers cagnotte
|
||||
res.cagnotteGagnant = res.cagnotteGagnant.add(masse);
|
||||
return;
|
||||
}
|
||||
|
||||
// Rapport brut = (masse / totalMisePayable) + 1 (on restitue la mise + bénéfice)
|
||||
BigDecimal rapport = masse.divide(totalMisePayable, SCALE, RoundingMode.HALF_UP).add(BigDecimal.ONE);
|
||||
if (rapport.compareTo(RAPPORT_MIN) < 0) rapport = RAPPORT_MIN;
|
||||
|
||||
// payer chaque parieur ayant parié sur ce cheval/ecurie
|
||||
for (Pari p : parisGagnant) {
|
||||
if (p.isNonPartant()) continue;
|
||||
String key = keyEcurie(p);
|
||||
boolean payable = false;
|
||||
if (ecurieGagnanteKey != null) {
|
||||
// payables si même ecurie
|
||||
payable = ecurieGagnanteKey.equals(key);
|
||||
} else {
|
||||
payable = (p.getNumeroCheval() == premier);
|
||||
}
|
||||
if (payable) {
|
||||
BigDecimal gain = p.getMise().multiply(rapport).setScale(2, RoundingMode.HALF_UP);
|
||||
res.gainsParParieur.put(p.getParieur(),
|
||||
res.gainsParParieur.getOrDefault(p.getParieur(), BigDecimal.ZERO).add(gain));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* -------------------------
|
||||
* Cas dead-heat (plusieurs premiers)
|
||||
* ------------------------- */
|
||||
// 1) calculer total mise sur les chevaux payables (les chevaux classés premiers)
|
||||
// Règle du règlement : "le montant de toutes les mises sur les divers chevaux payables est d’abord retiré de la masse à partager."
|
||||
BigDecimal misesSurPayables = parisGagnant.stream()
|
||||
.filter(p -> !p.isNonPartant() && premiers.contains(p.getNumeroCheval()))
|
||||
.map(Pari::getMise).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
|
||||
// Bénéfice à répartir
|
||||
BigDecimal benefice = masse.subtract(misesSurPayables);
|
||||
if (benefice.compareTo(BigDecimal.ZERO) < 0) benefice = BigDecimal.ZERO;
|
||||
|
||||
int nbChevauxPremiers = premiers.size();
|
||||
if (nbChevauxPremiers == 0) {
|
||||
res.cagnotteGagnant = res.cagnotteGagnant.add(masse);
|
||||
return;
|
||||
}
|
||||
|
||||
// Diviser le bénéfice en autant de parts qu'il y a de chevaux classés premiers
|
||||
BigDecimal partParCheval = benefice.divide(BigDecimal.valueOf(nbChevauxPremiers), SCALE, RoundingMode.HALF_UP);
|
||||
|
||||
// Pour chaque cheval premier : répartir sa part au prorata des mises sur ce cheval
|
||||
for (Integer cheval : premiers) {
|
||||
// mises sur ce cheval
|
||||
BigDecimal misesSurCheval = parisGagnant.stream()
|
||||
.filter(p -> !p.isNonPartant() && p.getNumeroCheval() == cheval)
|
||||
.map(Pari::getMise).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
|
||||
if (misesSurCheval.compareTo(BigDecimal.ZERO) == 0) {
|
||||
// si aucune mise sur ce cheval, sa part est redistribuée entre les autres premiers
|
||||
// impl: ajouter cette part à cagnotte temporaire pour redistribution
|
||||
// pour simplicité on ajoute à cagnotte et on répartira après
|
||||
res.cagnotteGagnant = res.cagnotteGagnant.add(partParCheval);
|
||||
continue;
|
||||
}
|
||||
|
||||
// montant additionnel par unité de mise = partParCheval / misesSurCheval
|
||||
BigDecimal ratio = partParCheval.divide(misesSurCheval, SCALE, RoundingMode.HALF_UP);
|
||||
BigDecimal rapport = ratio.add(BigDecimal.ONE); // ajoute la mise
|
||||
if (rapport.compareTo(RAPPORT_MIN) < 0) rapport = RAPPORT_MIN;
|
||||
|
||||
// payer chaque parieur sur ce cheval
|
||||
for (Pari p : parisGagnant) {
|
||||
if (p.isNonPartant()) continue;
|
||||
if (p.getNumeroCheval() == cheval) {
|
||||
BigDecimal gain = p.getMise().multiply(rapport).setScale(2, RoundingMode.HALF_UP);
|
||||
res.gainsParParieur.put(p.getParieur(),
|
||||
res.gainsParParieur.getOrDefault(p.getParieur(), BigDecimal.ZERO).add(gain));
|
||||
} else {
|
||||
// si écurie: si cheval p appartient à écurie du cheval premier, il peut avoir droit (règle d'écurie)
|
||||
// on doit totaliser mises par écurie gagnante et partager la part correspondante.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NB: gestion complète des écuries dans dead-heat nécessite agrégation par ecurie + redistribution des parts relatives.
|
||||
// Pour respecter à la lettre, on devrait :
|
||||
// - agréger mises par ecurie pour les chevaux premiers,
|
||||
// - distribuer la partParCheval à l'ecurie, puis parmi les chevaux de l'ecurie proportionnellement,
|
||||
// - ci-dessus on a fait la distribution cheval-par-cheval (simplifié).
|
||||
}
|
||||
|
||||
private String keyEcurie(Pari p) {
|
||||
if (p.getEcurieId() != null && !p.getEcurieId().trim().isEmpty()) {
|
||||
return "E:" + p.getEcurieId();
|
||||
} else {
|
||||
return "H:" + p.getNumeroCheval(); // seul cheval = sa "propre ecurie"
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------------------------
|
||||
* Calcul du PLACE (inclut dead-heat/ecurie)
|
||||
* --------------------------------------- */
|
||||
private void calculerPlace(List<Pari> parisPlace, ResultatCourse resultat, BigDecimal masse, ResultatCalcul res) {
|
||||
// déterminer horses payable: si >=8 partants => 3 places payées, si 4-7 => 2, sinon mise en cagnotte (article)
|
||||
int nPartants = resultat.getNombrePartants();
|
||||
int nbPlacesPayees;
|
||||
if (nPartants >= 8) nbPlacesPayees = 3;
|
||||
else if (nPartants >= 4) nbPlacesPayees = 2;
|
||||
else {
|
||||
// si moins de 4 partants, la masse place va en cagnotte
|
||||
res.cagnottePlace = res.cagnottePlace.add(masse);
|
||||
return;
|
||||
}
|
||||
|
||||
// Construction des chevaux payables selon les listes de résultat :
|
||||
// Remarque : listes peuvent contenir dead-heats (plusieurs elements)
|
||||
List<Integer> payables = new ArrayList<>();
|
||||
if (nbPlacesPayees == 2) {
|
||||
payables.addAll(resultat.getPremiers());
|
||||
payables.addAll(resultat.getDeuxiemes());
|
||||
} else {
|
||||
// 3 places
|
||||
payables.addAll(resultat.getPremiers());
|
||||
payables.addAll(resultat.getDeuxiemes());
|
||||
payables.addAll(resultat.getTroisiemes());
|
||||
}
|
||||
// on conserve distincts tout en gardant l'ordre logique
|
||||
LinkedHashSet<Integer> set = new LinkedHashSet<>(payables);
|
||||
List<Integer> chevauxPayables = new ArrayList<>(set);
|
||||
|
||||
if (chevauxPayables.isEmpty()) {
|
||||
res.cagnottePlace = res.cagnottePlace.add(masse);
|
||||
return;
|
||||
}
|
||||
|
||||
// Retirer "le montant de toutes les mises sur les divers chevaux payables" de la masse
|
||||
BigDecimal miseSurPayables = parisPlace.stream()
|
||||
.filter(p -> !p.isNonPartant() && chevauxPayables.contains(p.getNumeroCheval()))
|
||||
.map(Pari::getMise).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
|
||||
BigDecimal benefice = masse.subtract(miseSurPayables);
|
||||
if (benefice.compareTo(BigDecimal.ZERO) < 0) benefice = BigDecimal.ZERO;
|
||||
|
||||
// Le bénéfice est divisé en autant de parts égales qu'il y a de "combinaisons payables".
|
||||
// Ici on considère "combinaisons payables" = nombre de positions payées (nbPlacesPayees)
|
||||
// -> chaque part ensuite partagée proportionnellement entre chevaux payables de la position concernée.
|
||||
BigDecimal partParCombinaison = benefice.divide(BigDecimal.valueOf(nbPlacesPayees), SCALE, RoundingMode.HALF_UP);
|
||||
|
||||
// Répartition : pour chaque position (1er, 2e, 3e) on divise la part entre les chevaux classés à cette position
|
||||
// puis, pour chaque cheval, on partage au prorata de ses mises sur cette position parmi les chevaux payables de la position.
|
||||
// Simplification pratique: on va parcourir les positions et appliquer.
|
||||
// Position 1
|
||||
List<Integer> premiers = resultat.getPremiers();
|
||||
repartirPlacePosition(parisPlace, premiers, partParCombinaison, res);
|
||||
|
||||
if (nbPlacesPayees >= 2) {
|
||||
List<Integer> deuxiemes = resultat.getDeuxiemes();
|
||||
repartirPlacePosition(parisPlace, deuxiemes, partParCombinaison, res);
|
||||
}
|
||||
if (nbPlacesPayees >= 3) {
|
||||
List<Integer> troisiemes = resultat.getTroisiemes();
|
||||
repartirPlacePosition(parisPlace, troisiemes, partParCombinaison, res);
|
||||
}
|
||||
|
||||
// Note : si pour une position donnée il n'y a aucune mise, selon le règlement sa part est répartie entre les autres chevaux payables.
|
||||
// Ici, si aucune mise sur une position entière, on ajoute cette part à la cagnottePlace (impl simplifiée).
|
||||
}
|
||||
|
||||
private void repartirPlacePosition(List<Pari> parisPlace, List<Integer> chevauxPosition, BigDecimal partParCombinaison, ResultatCalcul res) {
|
||||
if (chevauxPosition == null || chevauxPosition.isEmpty()) {
|
||||
// pas de cheval classé à cette position -> part va en cagnotte
|
||||
res.cagnottePlace = res.cagnottePlace.add(partParCombinaison);
|
||||
return;
|
||||
}
|
||||
// total mises sur les chevaux de cette position
|
||||
BigDecimal totalMises = parisPlace.stream()
|
||||
.filter(p -> !p.isNonPartant() && chevauxPosition.contains(p.getNumeroCheval()))
|
||||
.map(Pari::getMise).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
|
||||
if (totalMises.compareTo(BigDecimal.ZERO) == 0) {
|
||||
// aucune mise sur cette position -> part est ajoutée à cagnotte pour redistribution (impl simplifiée)
|
||||
res.cagnottePlace = res.cagnottePlace.add(partParCombinaison);
|
||||
return;
|
||||
}
|
||||
|
||||
// chaque cheval recevra une fraction proportionnelle de la partParCombinaison
|
||||
for (Integer cheval : chevauxPosition) {
|
||||
BigDecimal misesSurCheval = parisPlace.stream()
|
||||
.filter(p -> !p.isNonPartant() && p.getNumeroCheval() == cheval)
|
||||
.map(Pari::getMise).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
|
||||
if (misesSurCheval.compareTo(BigDecimal.ZERO) == 0) continue;
|
||||
|
||||
// montant additionnel pour ce cheval = partParCombinaison * (misesSurCheval / totalMises)
|
||||
BigDecimal share = partParCombinaison.multiply(misesSurCheval).divide(totalMises, SCALE, RoundingMode.HALF_UP);
|
||||
|
||||
// Le rapport brut pour chaque mise sur ce cheval = (share / misesSurCheval) + 1
|
||||
BigDecimal ratio = share.divide(misesSurCheval, SCALE, RoundingMode.HALF_UP);
|
||||
BigDecimal rapport = ratio.add(BigDecimal.ONE);
|
||||
if (rapport.compareTo(RAPPORT_MIN) < 0) rapport = RAPPORT_MIN;
|
||||
|
||||
// appliquer paiement à tous les parieurs sur ce cheval
|
||||
for (Pari p : parisPlace) {
|
||||
if (p.isNonPartant()) continue;
|
||||
if (p.getNumeroCheval() == cheval) {
|
||||
BigDecimal gain = p.getMise().multiply(rapport).setScale(2, RoundingMode.HALF_UP);
|
||||
res.gainsParParieur.put(p.getParieur(),
|
||||
res.gainsParParieur.getOrDefault(p.getParieur(), BigDecimal.ZERO).add(gain));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.pmu.mali.apiplr;
|
||||
package com.pmumali;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.pmumali.ch10_multi.controller;
|
||||
|
||||
import com.pmumali.ch10_multi.model.*;
|
||||
import com.pmumali.ch10_multi.repository.*;
|
||||
import com.pmumali.ch10_multi.service.ServiceMulti;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/multi")
|
||||
@RequiredArgsConstructor
|
||||
public class ControleurMulti {
|
||||
|
||||
private final ServiceMulti serviceMulti;
|
||||
private final PariMultiRepository pariRepository;
|
||||
private final PaiementMultiRepository paiementRepository;
|
||||
private final CagnotteMultiRepository cagnotteRepository;
|
||||
|
||||
@PostMapping("/pari")
|
||||
public ResponseEntity<PariMulti> placerPari(@RequestBody RequetePariMulti requete) {
|
||||
try {
|
||||
PariMulti pari = serviceMulti.placerPari(requete);
|
||||
return ResponseEntity.ok(pari);
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/calculer-paiements")
|
||||
public ResponseEntity<List<ReponsePaiementMulti>> calculerPaiements(@RequestBody RequeteResultatMulti requete) {
|
||||
try {
|
||||
List<ReponsePaiementMulti> paiements = serviceMulti.calculerPaiements(requete);
|
||||
return ResponseEntity.ok(paiements);
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/calcul-combinaison")
|
||||
public ResponseEntity<CalculCombinaisonMulti> calculerCombinaison(
|
||||
@RequestParam TypeMulti typeMulti,
|
||||
@RequestParam TypeFormuleMulti typeFormule,
|
||||
@RequestParam Integer nombreChevauxTotal,
|
||||
@RequestParam Integer nombreChevauxBase) {
|
||||
try {
|
||||
CalculCombinaisonMulti resultat = serviceMulti.calculerCombinaison(
|
||||
typeMulti, typeFormule, nombreChevauxTotal, nombreChevauxBase);
|
||||
return ResponseEntity.ok(resultat);
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/cagnotte/{cagnotteId}/utiliser")
|
||||
public ResponseEntity<Void> utiliserCagnotte(
|
||||
@PathVariable Long cagnotteId,
|
||||
@RequestParam Long courseId) {
|
||||
try {
|
||||
serviceMulti.utiliserCagnotte(cagnotteId, courseId);
|
||||
return ResponseEntity.ok().build();
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/cagnottes/disponibles")
|
||||
public ResponseEntity<List<CagnotteMulti>> getCagnottesDisponibles() {
|
||||
return ResponseEntity.ok(cagnotteRepository.findByUtilisee(false));
|
||||
}
|
||||
|
||||
@GetMapping("/paris/course/{courseId}")
|
||||
public ResponseEntity<List<PariMulti>> getParisCourse(@PathVariable Long courseId) {
|
||||
return ResponseEntity.ok(pariRepository.findByCourseId(courseId));
|
||||
}
|
||||
|
||||
@GetMapping("/paiements/pari/{pariId}")
|
||||
public ResponseEntity<List<PaiementMulti>> getPaiementsPari(@PathVariable Long pariId) {
|
||||
return ResponseEntity.ok(paiementRepository.findByPariId(pariId));
|
||||
}
|
||||
|
||||
@GetMapping("/statistiques/course/{courseId}")
|
||||
public ResponseEntity<Map<TypeMulti, Long>> getStatistiquesParis(@PathVariable Long courseId) {
|
||||
Map<TypeMulti, Long> stats = new HashMap<>();
|
||||
for (TypeMulti type : TypeMulti.values()) {
|
||||
long count = pariRepository.findByCourseIdAndTypeMulti(courseId, type).size();
|
||||
stats.put(type, count);
|
||||
}
|
||||
return ResponseEntity.ok(stats);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.pmumali.ch10_multi.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Table(name = "cagnotte_multi")
|
||||
public class CagnotteMulti {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private Double montant;
|
||||
private LocalDateTime dateCreation;
|
||||
private LocalDateTime dateUtilisation;
|
||||
private Boolean utilisee;
|
||||
private Long courseSourceId;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.pmumali.ch10_multi.model;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class CalculCombinaisonMulti {
|
||||
private TypeMulti typeMulti;
|
||||
private TypeFormuleMulti typeFormule;
|
||||
private Integer nombreChevauxTotal;
|
||||
private Integer nombreChevauxBase;
|
||||
private Integer nombreCombinaisons;
|
||||
private Double valeurMise;
|
||||
}
|
||||
20
src/main/java/com/pmumali/ch10_multi/model/Cheval.java
Normal file
20
src/main/java/com/pmumali/ch10_multi/model/Cheval.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package com.pmumali.ch10_multi.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Table(name = "cheval")
|
||||
public class Cheval {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String nom;
|
||||
private Integer numero;
|
||||
private Boolean nonPartant;
|
||||
}
|
||||
29
src/main/java/com/pmumali/ch10_multi/model/Course.java
Normal file
29
src/main/java/com/pmumali/ch10_multi/model/Course.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package com.pmumali.ch10_multi.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Table(name = "course")
|
||||
public class Course {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String nom;
|
||||
private LocalDateTime heureCourse;
|
||||
private Integer nombreChevauxPartants;
|
||||
|
||||
@OneToMany
|
||||
private List<Cheval> chevaux;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
private StatutCourse statut;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.pmumali.ch10_multi.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Table(name = "paiement_multi")
|
||||
public class PaiementMulti {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@ManyToOne
|
||||
private PariMulti pari;
|
||||
|
||||
private Double montant;
|
||||
private LocalDateTime heurePaiement;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
private TypePaiementMulti typePaiement;
|
||||
}
|
||||
39
src/main/java/com/pmumali/ch10_multi/model/PariMulti.java
Normal file
39
src/main/java/com/pmumali/ch10_multi/model/PariMulti.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package com.pmumali.ch10_multi.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Table(name = "pari_multi")
|
||||
public class PariMulti {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@ManyToOne
|
||||
private Course course;
|
||||
|
||||
@ManyToMany
|
||||
private List<Cheval> chevauxSelectionnes;
|
||||
|
||||
private Double mise;
|
||||
private LocalDateTime heurePari;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
private TypeMulti typeMulti;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
private TypeFormuleMulti typeFormule;
|
||||
|
||||
@ManyToOne
|
||||
private Parieur parieur;
|
||||
|
||||
private Integer nombreChevauxBase;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.pmumali.quarteplus.model;
|
||||
package com.pmumali.ch10_multi.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
@@ -17,4 +17,4 @@ public class Parieur {
|
||||
private String nom;
|
||||
private String identification;
|
||||
private Double miseTotale;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.pmumali.ch10_multi.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class ReponsePaiementMulti {
|
||||
private Long pariId;
|
||||
private Double montant;
|
||||
private TypePaiementMulti typePaiement;
|
||||
private String message;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.pmumali.ch10_multi.model;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class RequetePariMulti {
|
||||
private Long courseId;
|
||||
private List<Long> chevalIds;
|
||||
private Double mise;
|
||||
private TypeMulti typeMulti;
|
||||
private TypeFormuleMulti typeFormule;
|
||||
private Long parieurId;
|
||||
private Integer nombreChevauxBase;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.pmumali.ch10_multi.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class RequeteResultatMulti {
|
||||
private Long courseId;
|
||||
private List<Long> premiersIds;
|
||||
private List<Long> secondsIds;
|
||||
private List<Long> troisiemesIds;
|
||||
private List<Long> quatriemesIds;
|
||||
private List<Long> ordreArriveeIds;
|
||||
private Double recetteNette;
|
||||
private Double prelevementsLegaux;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.pmumali.ch10_multi.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ResultatCalculRapport {
|
||||
private Double rapportBase;
|
||||
private Double rapportMulti4;
|
||||
private Double rapportMulti5;
|
||||
private Double rapportMulti6;
|
||||
private Double rapportMulti7;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.pmumali.ch10_multi.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Table(name = "resultat_course")
|
||||
public class ResultatCourse {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@ManyToOne
|
||||
private Course course;
|
||||
|
||||
@ManyToMany
|
||||
private List<Cheval> premiers;
|
||||
|
||||
@ManyToMany
|
||||
private List<Cheval> seconds;
|
||||
|
||||
@ManyToMany
|
||||
private List<Cheval> troisiemes;
|
||||
|
||||
@ManyToMany
|
||||
private List<Cheval> quatriemes;
|
||||
|
||||
@ManyToMany
|
||||
private List<Cheval> ordreArrivee;
|
||||
|
||||
private Double recetteNette;
|
||||
private Double montantRembourse;
|
||||
private Double prelevementsLegaux;
|
||||
private Double masseAPartager;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
package com.pmumali.quarteplus.model;
|
||||
package com.pmumali.ch10_multi.model;
|
||||
|
||||
public enum StatutCourse {
|
||||
PROGRAMMEE, EN_COURS, TERMINEE, ANNULEE
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.pmumali.quarteplus.model;
|
||||
package com.pmumali.ch10_multi.model;
|
||||
|
||||
public enum TypeDeadHeat {
|
||||
QUATRE_PREMIERS_OU_PLUS,
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.pmumali.ch10_multi.model;
|
||||
|
||||
public enum TypeFormuleMulti {
|
||||
UNITAIRE, CHAMP_TOTAL, CHAMP_PARTIEL
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.pmumali.ch10_multi.model;
|
||||
|
||||
public enum TypeMulti {
|
||||
MULTI_4, MULTI_5, MULTI_6, MULTI_7
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.pmumali.ch10_multi.model;
|
||||
|
||||
public enum TypePaiementMulti {
|
||||
MULTI_4, MULTI_5, MULTI_6, MULTI_7, REMBOURSEMENT
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.pmumali.ch10_multi.repository;
|
||||
|
||||
import com.pmumali.ch10_multi.model.CagnotteMulti;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface CagnotteMultiRepository extends JpaRepository<CagnotteMulti, Long> {
|
||||
List<CagnotteMulti> findByUtilisee(Boolean utilisee);
|
||||
List<CagnotteMulti> findByCourseSourceId(Long courseId);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.pmumali.ch10_multi.repository;
|
||||
|
||||
import com.pmumali.ch10_multi.model.Cheval;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ChevalRepository extends JpaRepository<Cheval, Long> {
|
||||
List<Cheval> findByNonPartant(Boolean nonPartant);
|
||||
List<Cheval> findByIdIn(List<Long> ids);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.pmumali.ch10_multi.repository;
|
||||
|
||||
import com.pmumali.ch10_multi.model.Course;
|
||||
import com.pmumali.ch10_multi.model.StatutCourse;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface CourseRepository extends JpaRepository<Course, Long> {
|
||||
List<Course> findByStatut(StatutCourse statut);
|
||||
List<Course> findByNombreChevauxPartantsGreaterThanEqual(Integer nombre);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.pmumali.ch10_multi.repository;
|
||||
|
||||
import com.pmumali.ch10_multi.model.PaiementMulti;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface PaiementMultiRepository extends JpaRepository<PaiementMulti, Long> {
|
||||
List<PaiementMulti> findByPariId(Long pariId);
|
||||
List<PaiementMulti> findByPariCourseId(Long courseId);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.pmumali.ch10_multi.repository;
|
||||
|
||||
import com.pmumali.ch10_multi.model.PariMulti;
|
||||
import com.pmumali.ch10_multi.model.TypeFormuleMulti;
|
||||
import com.pmumali.ch10_multi.model.TypeMulti;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import java.util.List;
|
||||
|
||||
public interface PariMultiRepository extends JpaRepository<PariMulti, Long> {
|
||||
List<PariMulti> findByCourseId(Long courseId);
|
||||
List<PariMulti> findByParieurId(Long parieurId);
|
||||
List<PariMulti> findByCourseIdAndTypeMulti(Long courseId, TypeMulti typeMulti);
|
||||
List<PariMulti> findByCourseIdAndTypeFormule(Long courseId, TypeFormuleMulti typeFormule);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.pmumali.quatro.repository;
|
||||
package com.pmumali.ch10_multi.repository;
|
||||
|
||||
import com.pmumali.quatro.model.Parieur;
|
||||
import com.pmumali.ch10_multi.model.Parieur;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.pmumali.quarteplus.repository;
|
||||
package com.pmumali.ch10_multi.repository;
|
||||
|
||||
import com.pmumali.quarteplus.model.ResultatCourse;
|
||||
import com.pmumali.ch10_multi.model.ResultatCourse;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface ResultatCourseRepository extends JpaRepository<ResultatCourse, Long> {
|
||||
875
src/main/java/com/pmumali/ch10_multi/service/ServiceMulti.java
Normal file
875
src/main/java/com/pmumali/ch10_multi/service/ServiceMulti.java
Normal file
@@ -0,0 +1,875 @@
|
||||
package com.pmumali.ch10_multi.service;
|
||||
|
||||
import com.pmumali.ch10_multi.model.*;
|
||||
import com.pmumali.ch10_multi.repository.*;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ServiceMulti {
|
||||
|
||||
private final PariMultiRepository pariRepository;
|
||||
private final CourseRepository courseRepository;
|
||||
private final ChevalRepository chevalRepository;
|
||||
private final PaiementMultiRepository paiementRepository;
|
||||
private final ResultatCourseRepository resultatRepository;
|
||||
private final CagnotteMultiRepository cagnotteRepository;
|
||||
private final ParieurRepository parieurRepository;
|
||||
|
||||
private static final Double MISE_BASE = 500.0;
|
||||
private static final Double MISE_MAX = 200 * MISE_BASE;
|
||||
private static final Integer NOMBRE_CHEVAUX_MINIMUM = 10;
|
||||
|
||||
// Coefficients pour le calcul des rapports (Article 5)
|
||||
private static final Map<TypeMulti, Integer> COEFFICIENTS = Map.of(
|
||||
TypeMulti.MULTI_4, 105,
|
||||
TypeMulti.MULTI_5, 21,
|
||||
TypeMulti.MULTI_6, 7,
|
||||
TypeMulti.MULTI_7, 3
|
||||
);
|
||||
|
||||
// Rapports minimum (Article 6)
|
||||
private static final Map<TypeMulti, Double> RAPPORTS_MINIMUM = Map.of(
|
||||
TypeMulti.MULTI_4, 1.6,
|
||||
TypeMulti.MULTI_5, 1.4,
|
||||
TypeMulti.MULTI_6, 1.2,
|
||||
TypeMulti.MULTI_7, 1.1
|
||||
);
|
||||
|
||||
@Transactional
|
||||
public PariMulti placerPari(RequetePariMulti requete) {
|
||||
// Validation de la mise
|
||||
if (requete.getMise() < MISE_BASE) {
|
||||
throw new IllegalArgumentException("La mise doit être au moins " + MISE_BASE + " FCFA");
|
||||
}
|
||||
|
||||
Course course = courseRepository.findById(requete.getCourseId())
|
||||
.orElseThrow(() -> new RuntimeException("Course non trouvée"));
|
||||
|
||||
// Vérification du nombre minimum de chevaux
|
||||
if (course.getNombreChevauxPartants() < NOMBRE_CHEVAUX_MINIMUM) {
|
||||
throw new IllegalArgumentException("La course doit avoir au moins " + NOMBRE_CHEVAUX_MINIMUM + " chevaux partants");
|
||||
}
|
||||
|
||||
List<Cheval> chevaux = chevalRepository.findAllById(requete.getChevalIds());
|
||||
|
||||
// Validation du nombre de chevaux selon le type MULTI
|
||||
validerNombreChevaux(requete.getTypeMulti(), chevaux.size());
|
||||
|
||||
// Vérification des non-partants
|
||||
long nonPartants = chevaux.stream().filter(Cheval::getNonPartant).count();
|
||||
validerNonPartants(requete.getTypeMulti(), nonPartants);
|
||||
|
||||
// Limitation de mise selon l'article 2
|
||||
Double miseEffective = Math.min(requete.getMise(), MISE_MAX);
|
||||
|
||||
Parieur parieur = parieurRepository.findById(requete.getParieurId())
|
||||
.orElseThrow(() -> new RuntimeException("Parieur non trouvé"));
|
||||
|
||||
PariMulti pari = PariMulti.builder()
|
||||
.course(course)
|
||||
.chevauxSelectionnes(chevaux)
|
||||
.mise(miseEffective)
|
||||
.heurePari(LocalDateTime.now())
|
||||
.typeMulti(requete.getTypeMulti())
|
||||
.typeFormule(requete.getTypeFormule())
|
||||
.parieur(parieur)
|
||||
.nombreChevauxBase(requete.getNombreChevauxBase())
|
||||
.build();
|
||||
|
||||
return pariRepository.save(pari);
|
||||
}
|
||||
|
||||
private void validerNombreChevaux(TypeMulti typeMulti, int nombreChevaux) {
|
||||
switch (typeMulti) {
|
||||
case MULTI_4:
|
||||
if (nombreChevaux != 4) throw new IllegalArgumentException("MULTI en 4 nécessite exactement 4 chevaux");
|
||||
break;
|
||||
case MULTI_5:
|
||||
if (nombreChevaux != 5) throw new IllegalArgumentException("MULTI en 5 nécessite exactement 5 chevaux");
|
||||
break;
|
||||
case MULTI_6:
|
||||
if (nombreChevaux != 6) throw new IllegalArgumentException("MULTI en 6 nécessite exactement 6 chevaux");
|
||||
break;
|
||||
case MULTI_7:
|
||||
if (nombreChevaux != 7) throw new IllegalArgumentException("MULTI en 7 nécessite exactement 7 chevaux");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void validerNonPartants(TypeMulti typeMulti, long nonPartants) {
|
||||
switch (typeMulti) {
|
||||
case MULTI_4:
|
||||
if (nonPartants >= 1) throw new IllegalArgumentException("MULTI en 4 ne permet pas de chevaux non-partants");
|
||||
break;
|
||||
case MULTI_5:
|
||||
if (nonPartants >= 2) throw new IllegalArgumentException("MULTI en 5 permet maximum 1 cheval non-partant");
|
||||
break;
|
||||
case MULTI_6:
|
||||
if (nonPartants >= 3) throw new IllegalArgumentException("MULTI en 6 permet maximum 2 chevaux non-partants");
|
||||
break;
|
||||
case MULTI_7:
|
||||
if (nonPartants >= 4) throw new IllegalArgumentException("MULTI en 7 permet maximum 3 chevaux non-partants");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public List<ReponsePaiementMulti> calculerPaiements(RequeteResultatMulti requete) {
|
||||
ResultatCourse resultat = creerResultat(requete);
|
||||
List<PariMulti> paris = pariRepository.findByCourseId(requete.getCourseId());
|
||||
List<ReponsePaiementMulti> paiements = new ArrayList<>();
|
||||
|
||||
// Vérifier si la course a moins de 10 chevaux (Article 1)
|
||||
if (resultat.getCourse().getNombreChevauxPartants() < NOMBRE_CHEVAUX_MINIMUM) {
|
||||
transfererEnCagnotte(resultat);
|
||||
return paiements;
|
||||
}
|
||||
|
||||
// Calcul de la masse à partager selon l'article 5
|
||||
Double masseAPartager = calculerMasseAPartager(resultat);
|
||||
resultat.setMasseAPartager(masseAPartager);
|
||||
resultatRepository.save(resultat);
|
||||
|
||||
// Gérer les cas de dead-heat si nécessaire
|
||||
TypeDeadHeat typeDeadHeat = detecterDeadHeat(resultat);
|
||||
if (typeDeadHeat != null) {
|
||||
return gererDeadHeat(resultat, typeDeadHeat);
|
||||
}
|
||||
|
||||
// Calcul normal des paiements
|
||||
Map<TypeMulti, List<PariMulti>> parisGagnantsParType = new HashMap<>();
|
||||
Map<TypeMulti, Integer> nombreParisGagnantsParType = new HashMap<>();
|
||||
|
||||
for (PariMulti pari : paris) {
|
||||
ReponsePaiementMulti paiement = calculerPaiementPari(pari, resultat, masseAPartager);
|
||||
if (paiement != null) {
|
||||
paiements.add(paiement);
|
||||
enregistrerPaiement(pari, paiement);
|
||||
|
||||
// Compter les paris gagnants par type
|
||||
TypeMulti typePari = getTypeMultiFromPaiement(paiement.getTypePaiement());
|
||||
parisGagnantsParType.computeIfAbsent(typePari, k -> new ArrayList<>()).add(pari);
|
||||
nombreParisGagnantsParType.put(typePari,
|
||||
nombreParisGagnantsParType.getOrDefault(typePari, 0) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Appliquer les règles de rapport minimum (Article 6)
|
||||
appliquerReglesRapportMinimum(paiements, masseAPartager, nombreParisGagnantsParType);
|
||||
|
||||
return paiements;
|
||||
}
|
||||
|
||||
private ReponsePaiementMulti calculerPaiementPari(PariMulti pari, ResultatCourse resultat, Double masseAPartager) {
|
||||
List<Cheval> chevauxPari = pari.getChevauxSelectionnes();
|
||||
long nonPartants = chevauxPari.stream().filter(Cheval::getNonPartant).count();
|
||||
|
||||
// Article 4: Gestion des non-partants et transformations
|
||||
if (doitEtreRembourse(pari.getTypeMulti(), nonPartants)) {
|
||||
return new ReponsePaiementMulti(pari.getId(), pari.getMise(),
|
||||
TypePaiementMulti.REMBOURSEMENT, "Remboursement selon article 4");
|
||||
}
|
||||
|
||||
// Transformer les paris avec non-partants (Article 4)
|
||||
TypeMulti typeTransforme = transformerPari(pari.getTypeMulti(), nonPartants);
|
||||
List<Cheval> chevauxParticipants = chevauxPari.stream()
|
||||
.filter(cheval -> !cheval.getNonPartant())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Vérifier si les chevaux sont dans les 4 premiers
|
||||
boolean estGagnant = estCombinaisonGagnante(chevauxParticipants, resultat);
|
||||
|
||||
if (estGagnant) {
|
||||
Double montant = calculerMontantPari(typeTransforme, masseAPartager);
|
||||
TypePaiementMulti typePaiement = getTypePaiementFromTypeMulti(typeTransforme);
|
||||
return new ReponsePaiementMulti(pari.getId(), montant, typePaiement,
|
||||
"Paiement MULTI " + typeTransforme + " transformé");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean doitEtreRembourse(TypeMulti typeMulti, long nonPartants) {
|
||||
switch (typeMulti) {
|
||||
case MULTI_4: return nonPartants >= 1;
|
||||
case MULTI_5: return nonPartants >= 2;
|
||||
case MULTI_6: return nonPartants >= 3;
|
||||
case MULTI_7: return nonPartants >= 4;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
private TypeMulti transformerPari(TypeMulti typeOriginal, long nonPartants) {
|
||||
// Article 4: Transformations des paris avec non-partants
|
||||
if (nonPartants == 0) return typeOriginal;
|
||||
|
||||
switch (typeOriginal) {
|
||||
case MULTI_5:
|
||||
return nonPartants == 1 ? TypeMulti.MULTI_4 : typeOriginal;
|
||||
case MULTI_6:
|
||||
if (nonPartants == 1) return TypeMulti.MULTI_5;
|
||||
if (nonPartants == 2) return TypeMulti.MULTI_4;
|
||||
return typeOriginal;
|
||||
case MULTI_7:
|
||||
if (nonPartants == 1) return TypeMulti.MULTI_6;
|
||||
if (nonPartants == 2) return TypeMulti.MULTI_5;
|
||||
if (nonPartants == 3) return TypeMulti.MULTI_4;
|
||||
return typeOriginal;
|
||||
default:
|
||||
return typeOriginal;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean estCombinaisonGagnante(List<Cheval> chevauxPari, ResultatCourse resultat) {
|
||||
// Vérifier si au moins 4 chevaux sont dans les 4 premiers
|
||||
long countDansTop4 = chevauxPari.stream()
|
||||
.filter(cheval -> estDansTop4(cheval, resultat))
|
||||
.count();
|
||||
return countDansTop4 >= 4;
|
||||
}
|
||||
|
||||
private boolean estDansTop4(Cheval cheval, ResultatCourse resultat) {
|
||||
return estDansListe(cheval, resultat.getPremiers()) ||
|
||||
estDansListe(cheval, resultat.getSeconds()) ||
|
||||
estDansListe(cheval, resultat.getTroisiemes()) ||
|
||||
estDansListe(cheval, resultat.getQuatriemes());
|
||||
}
|
||||
|
||||
private boolean estDansListe(Cheval cheval, List<Cheval> liste) {
|
||||
return liste != null && liste.contains(cheval);
|
||||
}
|
||||
|
||||
private Double calculerMontantPari(TypeMulti typeMulti, Double masseAPartager) {
|
||||
// Article 5: Calcul basé sur les coefficients
|
||||
int coefficient = COEFFICIENTS.get(typeMulti);
|
||||
|
||||
// Calcul simplifié - en réalité, il faudrait compter le nombre de paris gagnants
|
||||
return (masseAPartager * 0.25) / coefficient; // 25% de la masse pour chaque type
|
||||
}
|
||||
|
||||
private TypePaiementMulti getTypePaiementFromTypeMulti(TypeMulti typeMulti) {
|
||||
switch (typeMulti) {
|
||||
case MULTI_4: return TypePaiementMulti.MULTI_4;
|
||||
case MULTI_5: return TypePaiementMulti.MULTI_5;
|
||||
case MULTI_6: return TypePaiementMulti.MULTI_6;
|
||||
case MULTI_7: return TypePaiementMulti.MULTI_7;
|
||||
default: return TypePaiementMulti.REMBOURSEMENT;
|
||||
}
|
||||
}
|
||||
|
||||
private TypeMulti getTypeMultiFromPaiement(TypePaiementMulti typePaiement) {
|
||||
switch (typePaiement) {
|
||||
case MULTI_4: return TypeMulti.MULTI_4;
|
||||
case MULTI_5: return TypeMulti.MULTI_5;
|
||||
case MULTI_6: return TypeMulti.MULTI_6;
|
||||
case MULTI_7: return TypeMulti.MULTI_7;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void appliquerReglesRapportMinimum(List<ReponsePaiementMulti> paiements, Double masseAPartager,
|
||||
Map<TypeMulti, Integer> nombreParisGagnantsParType) {
|
||||
// Article 6: Application des règles de rapport minimum
|
||||
|
||||
// Étape 1: Vérifier MULTI_7
|
||||
Double rapportMulti7 = calculerRapportMoyen(paiements, TypeMulti.MULTI_7);
|
||||
if (rapportMulti7 < RAPPORTS_MINIMUM.get(TypeMulti.MULTI_7)) {
|
||||
ajusterRapportsMulti7(paiements, masseAPartager, nombreParisGagnantsParType);
|
||||
// Recalculer après ajustement
|
||||
rapportMulti7 = RAPPORTS_MINIMUM.get(TypeMulti.MULTI_7);
|
||||
}
|
||||
|
||||
// Étape 2: Vérifier MULTI_6
|
||||
Double rapportMulti6 = calculerRapportMoyen(paiements, TypeMulti.MULTI_6);
|
||||
if (rapportMulti6 < RAPPORTS_MINIMUM.get(TypeMulti.MULTI_6)) {
|
||||
ajusterRapportsMulti6(paiements, masseAPartager, nombreParisGagnantsParType);
|
||||
rapportMulti6 = RAPPORTS_MINIMUM.get(TypeMulti.MULTI_6);
|
||||
}
|
||||
|
||||
// Étape 3: Vérifier MULTI_5
|
||||
Double rapportMulti5 = calculerRapportMoyen(paiements, TypeMulti.MULTI_5);
|
||||
if (rapportMulti5 < RAPPORTS_MINIMUM.get(TypeMulti.MULTI_5)) {
|
||||
ajusterRapportsMulti5(paiements, masseAPartager, nombreParisGagnantsParType);
|
||||
rapportMulti5 = RAPPORTS_MINIMUM.get(TypeMulti.MULTI_5);
|
||||
}
|
||||
|
||||
// Étape 4: Vérifier MULTI_4
|
||||
Double rapportMulti4 = calculerRapportMoyen(paiements, TypeMulti.MULTI_4);
|
||||
if (rapportMulti4 < RAPPORTS_MINIMUM.get(TypeMulti.MULTI_4)) {
|
||||
ajusterRapportsMulti4(paiements, masseAPartager, nombreParisGagnantsParType);
|
||||
}
|
||||
}
|
||||
|
||||
private Double calculerRapportMoyen(List<ReponsePaiementMulti> paiements, TypeMulti typeMulti) {
|
||||
TypePaiementMulti typePaiement = getTypePaiementFromTypeMulti(typeMulti);
|
||||
List<ReponsePaiementMulti> paiementsType = paiements.stream()
|
||||
.filter(p -> p.getTypePaiement() == typePaiement)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (paiementsType.isEmpty()) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return paiementsType.stream()
|
||||
.mapToDouble(ReponsePaiementMulti::getMontant)
|
||||
.average()
|
||||
.orElse(0.0);
|
||||
}
|
||||
|
||||
private void ajusterRapportsMulti7(List<ReponsePaiementMulti> paiements, Double masseAPartager,
|
||||
Map<TypeMulti, Integer> nombreParisGagnantsParType) {
|
||||
// Article 6.1: Ajustement MULTI_7
|
||||
TypePaiementMulti typePaiement = getTypePaiementFromTypeMulti(TypeMulti.MULTI_7);
|
||||
int nombreGagnants = nombreParisGagnantsParType.getOrDefault(TypeMulti.MULTI_7, 0);
|
||||
|
||||
if (nombreGagnants > 0) {
|
||||
Double montantTotal = RAPPORTS_MINIMUM.get(TypeMulti.MULTI_7) * nombreGagnants;
|
||||
|
||||
// Ajuster les montants
|
||||
for (ReponsePaiementMulti paiement : paiements) {
|
||||
if (paiement.getTypePaiement() == typePaiement) {
|
||||
paiement.setMontant(RAPPORTS_MINIMUM.get(TypeMulti.MULTI_7));
|
||||
}
|
||||
}
|
||||
|
||||
// Réduire la masse à partager
|
||||
masseAPartager -= montantTotal;
|
||||
}
|
||||
}
|
||||
|
||||
private void ajusterRapportsMulti6(List<ReponsePaiementMulti> paiements, Double masseAPartager,
|
||||
Map<TypeMulti, Integer> nombreParisGagnantsParType) {
|
||||
// Article 6.2: Ajustement MULTI_6
|
||||
TypePaiementMulti typePaiement = getTypePaiementFromTypeMulti(TypeMulti.MULTI_6);
|
||||
int nombreGagnants = nombreParisGagnantsParType.getOrDefault(TypeMulti.MULTI_6, 0);
|
||||
|
||||
if (nombreGagnants > 0) {
|
||||
// Calculer le rapport de base
|
||||
Double rapportBase = masseAPartager / (nombreGagnants * 1 +
|
||||
nombreParisGagnantsParType.getOrDefault(TypeMulti.MULTI_5, 0) * 3 +
|
||||
nombreParisGagnantsParType.getOrDefault(TypeMulti.MULTI_4, 0) * 15);
|
||||
|
||||
// Ajuster les montants
|
||||
for (ReponsePaiementMulti paiement : paiements) {
|
||||
TypeMulti typeMulti = getTypeMultiFromPaiement(paiement.getTypePaiement());
|
||||
if (typeMulti == TypeMulti.MULTI_6) {
|
||||
paiement.setMontant(rapportBase);
|
||||
} else if (typeMulti == TypeMulti.MULTI_5) {
|
||||
paiement.setMontant(rapportBase * 3);
|
||||
} else if (typeMulti == TypeMulti.MULTI_4) {
|
||||
paiement.setMontant(rapportBase * 15);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ajusterRapportsMulti5(List<ReponsePaiementMulti> paiements, Double masseAPartager,
|
||||
Map<TypeMulti, Integer> nombreParisGagnantsParType) {
|
||||
// Article 6.3: Ajustement MULTI_5
|
||||
TypePaiementMulti typePaiement = getTypePaiementFromTypeMulti(TypeMulti.MULTI_5);
|
||||
int nombreGagnants = nombreParisGagnantsParType.getOrDefault(TypeMulti.MULTI_5, 0);
|
||||
|
||||
if (nombreGagnants > 0) {
|
||||
// Calculer le rapport de base
|
||||
Double rapportBase = masseAPartager / (nombreGagnants * 1 +
|
||||
nombreParisGagnantsParType.getOrDefault(TypeMulti.MULTI_4, 0) * 5);
|
||||
|
||||
// Ajuster les montants
|
||||
for (ReponsePaiementMulti paiement : paiements) {
|
||||
TypeMulti typeMulti = getTypeMultiFromPaiement(paiement.getTypePaiement());
|
||||
if (typeMulti == TypeMulti.MULTI_5) {
|
||||
paiement.setMontant(rapportBase);
|
||||
} else if (typeMulti == TypeMulti.MULTI_4) {
|
||||
paiement.setMontant(rapportBase * 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ajusterRapportsMulti4(List<ReponsePaiementMulti> paiements, Double masseAPartager,
|
||||
Map<TypeMulti, Integer> nombreParisGagnantsParType) {
|
||||
// Article 6.4: Ajustement MULTI_4
|
||||
TypePaiementMulti typePaiement = getTypePaiementFromTypeMulti(TypeMulti.MULTI_4);
|
||||
int nombreGagnants = nombreParisGagnantsParType.getOrDefault(TypeMulti.MULTI_4, 0);
|
||||
|
||||
if (nombreGagnants > 0) {
|
||||
// Calculer le rapport de base
|
||||
Double rapportBase = masseAPartager / nombreGagnants;
|
||||
|
||||
// Forcer le rapport minimum si nécessaire
|
||||
if (rapportBase < RAPPORTS_MINIMUM.get(TypeMulti.MULTI_4)) {
|
||||
rapportBase = RAPPORTS_MINIMUM.get(TypeMulti.MULTI_4);
|
||||
}
|
||||
|
||||
// Ajuster les montants
|
||||
for (ReponsePaiementMulti paiement : paiements) {
|
||||
if (paiement.getTypePaiement() == typePaiement) {
|
||||
paiement.setMontant(rapportBase);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Méthodes pour le calcul des combinaisons (Article 7)
|
||||
public CalculCombinaisonMulti calculerCombinaison(TypeMulti typeMulti, TypeFormuleMulti typeFormule,
|
||||
Integer nombreChevauxTotal, Integer nombreChevauxBase) {
|
||||
Integer nombreCombinaisons = calculerNombreCombinaisons(typeMulti, typeFormule, nombreChevauxTotal, nombreChevauxBase);
|
||||
Double valeurMise = nombreCombinaisons * MISE_BASE;
|
||||
|
||||
return CalculCombinaisonMulti.builder()
|
||||
.typeMulti(typeMulti)
|
||||
.typeFormule(typeFormule)
|
||||
.nombreChevauxTotal(nombreChevauxTotal)
|
||||
.nombreChevauxBase(nombreChevauxBase)
|
||||
.nombreCombinaisons(nombreCombinaisons)
|
||||
.valeurMise(valeurMise)
|
||||
.build();
|
||||
}
|
||||
|
||||
private Integer calculerNombreCombinaisons(TypeMulti typeMulti, TypeFormuleMulti typeFormule,
|
||||
Integer n, Integer p) {
|
||||
if (typeFormule == TypeFormuleMulti.UNITAIRE) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Implémentation des formules de combinaison selon l'article 7
|
||||
switch (typeMulti) {
|
||||
case MULTI_4:
|
||||
return calculerCombinaisonsMulti4(typeFormule, n, p);
|
||||
case MULTI_5:
|
||||
return calculerCombinaisonsMulti5(typeFormule, n, p);
|
||||
case MULTI_6:
|
||||
return calculerCombinaisonsMulti6(typeFormule, n, p);
|
||||
case MULTI_7:
|
||||
return calculerCombinaisonsMulti7(typeFormule, n, p);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private Integer calculerCombinaisonsMulti4(TypeFormuleMulti typeFormule, Integer n, Integer p) {
|
||||
if (typeFormule == TypeFormuleMulti.CHAMP_TOTAL) {
|
||||
if (p == 3) return n - 3;
|
||||
if (p == 2) return (n - 2) * (n - 3) / 2;
|
||||
if (p == 1) return (n - 1) * (n - 2) * (n - 3) / 6;
|
||||
} else {
|
||||
if (p == 3) return p;
|
||||
if (p == 2) return p * (p - 1) / 2;
|
||||
if (p == 1) return p * (p - 1) * (p - 2) / 6;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private Integer calculerCombinaisonsMulti5(TypeFormuleMulti typeFormule, Integer n, Integer p) {
|
||||
if (typeFormule == TypeFormuleMulti.CHAMP_TOTAL) {
|
||||
if (p == 4) return n - 4;
|
||||
if (p == 3) return (n - 3) * (n - 4) / 2;
|
||||
if (p == 2) return (n - 2) * (n - 3) * (n - 4) / 6;
|
||||
if (p == 1) return (n - 1) * (n - 2) * (n - 3) * (n - 4) / 24;
|
||||
} else {
|
||||
if (p == 4) return p;
|
||||
if (p == 3) return p * (p - 1) / 2;
|
||||
if (p == 2) return p * (p - 1) * (p - 2) / 6;
|
||||
if (p == 1) return p * (p - 1) * (p - 2) * (p - 3) / 24;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private Integer calculerCombinaisonsMulti6(TypeFormuleMulti typeFormule, Integer n, Integer p) {
|
||||
if (typeFormule == TypeFormuleMulti.CHAMP_TOTAL) {
|
||||
if (p == 5) return n - 5;
|
||||
if (p == 4) return (n - 4) * (n - 5) / 2;
|
||||
if (p == 3) return (n - 3) * (n - 4) * (n - 5) / 6;
|
||||
if (p == 2) return (n - 2) * (n - 3) * (n - 4) * (n - 5) / 24;
|
||||
if (p == 1) return (n - 1) * (n - 2) * (n - 3) * (n - 4) * (n - 5) / 120;
|
||||
} else {
|
||||
if (p == 5) return p;
|
||||
if (p == 4) return p * (p - 1) / 2;
|
||||
if (p == 3) return p * (p - 1) * (p - 2) / 6;
|
||||
if (p == 2) return p * (p - 1) * (p - 2) * (p - 3) / 24;
|
||||
if (p == 1) return p * (p - 1) * (p - 2) * (p - 3) * (p - 4) / 120;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private Integer calculerCombinaisonsMulti7(TypeFormuleMulti typeFormule, Integer n, Integer p) {
|
||||
if (typeFormule == TypeFormuleMulti.CHAMP_TOTAL) {
|
||||
if (p == 6) return n - 6;
|
||||
if (p == 5) return (n - 5) * (n - 6) / 2;
|
||||
if (p == 4) return (n - 4) * (n - 5) * (n - 6) / 6;
|
||||
if (p == 3) return (n - 3) * (n - 4) * (n - 5) * (n - 6) / 24;
|
||||
if (p == 2) return (n - 2) * (n - 3) * (n - 4) * (n - 5) * (n - 6) / 120;
|
||||
if (p == 1) return (n - 1) * (n - 2) * (n - 3) * (n - 4) * (n - 5) * (n - 6) / 720;
|
||||
} else {
|
||||
if (p == 6) return p;
|
||||
if (p == 5) return p * (p - 1) / 2;
|
||||
if (p == 4) return p * (p - 1) * (p - 2) / 6;
|
||||
if (p == 3) return p * (p - 1) * (p - 2) * (p - 3) / 24;
|
||||
if (p == 2) return p * (p - 1) * (p - 2) * (p - 3) * (p - 4) / 120;
|
||||
if (p == 1) return p * (p - 1) * (p - 2) * (p - 3) * (p - 4) * (p - 5) / 720;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Gestion des dead-heat (Article 3)
|
||||
private TypeDeadHeat detecterDeadHeat(ResultatCourse resultat) {
|
||||
if (resultat.getPremiers().size() >= 4) return TypeDeadHeat.QUATRE_PREMIERS_OU_PLUS;
|
||||
if (resultat.getPremiers().size() >= 3 && resultat.getQuatriemes().size() >= 1)
|
||||
return TypeDeadHeat.TROIS_PREMIERS_UN_QUATRIEME;
|
||||
if (resultat.getPremiers().size() >= 2 && resultat.getTroisiemes().size() >= 2)
|
||||
return TypeDeadHeat.DEUX_PREMIERS_DEUX_TROISIEMES;
|
||||
if (resultat.getPremiers().size() >= 2 && resultat.getTroisiemes().size() == 1 && resultat.getQuatriemes().size() >= 1)
|
||||
return TypeDeadHeat.DEUX_PREMIERS_UN_TROISIEME_UN_QUATRIEME;
|
||||
if (resultat.getSeconds().size() >= 3)
|
||||
return TypeDeadHeat.TROIS_SECONDS_OU_PLUS;
|
||||
if (resultat.getSeconds().size() >= 2 && resultat.getQuatriemes().size() >= 1)
|
||||
return TypeDeadHeat.DEUX_SECONDS_UN_QUATRIEME;
|
||||
if (resultat.getTroisiemes().size() >= 2)
|
||||
return TypeDeadHeat.DEUX_TROISIEMES_OU_PLUS;
|
||||
if (resultat.getQuatriemes().size() >= 2)
|
||||
return TypeDeadHeat.DEUX_QUATRIEMES_OU_PLUS;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<ReponsePaiementMulti> gererDeadHeat(ResultatCourse resultat, TypeDeadHeat typeDeadHeat) {
|
||||
List<PariMulti> paris = pariRepository.findByCourseId(resultat.getCourse().getId());
|
||||
List<ReponsePaiementMulti> paiements = new ArrayList<>();
|
||||
Double masseAPartager = resultat.getMasseAPartager();
|
||||
|
||||
for (PariMulti pari : paris) {
|
||||
ReponsePaiementMulti paiement = calculerPaiementDeadHeat(pari, resultat, masseAPartager, typeDeadHeat);
|
||||
if (paiement != null) {
|
||||
paiements.add(paiement);
|
||||
enregistrerPaiement(pari, paiement);
|
||||
}
|
||||
}
|
||||
|
||||
return paiements;
|
||||
}
|
||||
|
||||
private ReponsePaiementMulti calculerPaiementDeadHeat(PariMulti pari, ResultatCourse resultat,
|
||||
Double masseAPartager, TypeDeadHeat typeDeadHeat) {
|
||||
// Implémentation spécifique pour chaque type de dead-heat
|
||||
switch (typeDeadHeat) {
|
||||
case QUATRE_PREMIERS_OU_PLUS:
|
||||
return gererDeadHeatQuatrePremiers(pari, resultat, masseAPartager);
|
||||
case TROIS_PREMIERS_UN_QUATRIEME:
|
||||
return gererDeadHeatTroisPremiersUnQuatrieme(pari, resultat, masseAPartager);
|
||||
case DEUX_PREMIERS_DEUX_TROISIEMES:
|
||||
return gererDeadHeatDeuxPremiersDeuxTroisiemes(pari, resultat, masseAPartager);
|
||||
case DEUX_PREMIERS_UN_TROISIEME_UN_QUATRIEME:
|
||||
return gererDeadHeatDeuxPremiersUnTroisiemeUnQuatrieme(pari, resultat, masseAPartager);
|
||||
case TROIS_SECONDS_OU_PLUS:
|
||||
return gererDeadHeatTroisSeconds(pari, resultat, masseAPartager);
|
||||
case DEUX_SECONDS_UN_QUATRIEME:
|
||||
return gererDeadHeatDeuxSecondsUnQuatrieme(pari, resultat, masseAPartager);
|
||||
case DEUX_TROISIEMES_OU_PLUS:
|
||||
return gererDeadHeatDeuxTroisiemes(pari, resultat, masseAPartager);
|
||||
case DEUX_QUATRIEMES_OU_PLUS:
|
||||
return gererDeadHeatDeuxQuatriemes(pari, resultat, masseAPartager);
|
||||
default:
|
||||
return calculerPaiementPari(pari, resultat, masseAPartager);
|
||||
}
|
||||
}
|
||||
|
||||
private ReponsePaiementMulti gererDeadHeatQuatrePremiers(PariMulti pari, ResultatCourse resultat, Double masseAPartager) {
|
||||
// Article 3a: Dead-heat de 4+ chevaux premiers
|
||||
List<Cheval> chevauxPari = pari.getChevauxSelectionnes();
|
||||
|
||||
// Vérifier si tous les chevaux du pari sont parmi les premiers
|
||||
if (chevauxPari.stream().allMatch(cheval -> estDansListe(cheval, resultat.getPremiers()))) {
|
||||
int nombreCombinaisons = calculerNombreCombinaisonsDeadHeat(resultat.getPremiers().size(), 4);
|
||||
Double montant = masseAPartager / nombreCombinaisons;
|
||||
return new ReponsePaiementMulti(pari.getId(), montant,
|
||||
TypePaiementMulti.MULTI_4, "Dead-Heat 4+ premiers");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private ReponsePaiementMulti gererDeadHeatTroisPremiersUnQuatrieme(PariMulti pari, ResultatCourse resultat, Double masseAPartager) {
|
||||
// Article 3b: Dead-heat de 3 premiers + 1+ quatrième
|
||||
List<Cheval> chevauxPari = pari.getChevauxSelectionnes();
|
||||
|
||||
boolean troisPremiersOk = chevauxPari.subList(0, 3).stream()
|
||||
.allMatch(cheval -> estDansListe(cheval, resultat.getPremiers()));
|
||||
boolean quatriemeOk = estDansListe(chevauxPari.get(3), resultat.getQuatriemes());
|
||||
|
||||
if (troisPremiersOk && quatriemeOk) {
|
||||
int nombreCombinaisons = resultat.getPremiers().size() * resultat.getQuatriemes().size();
|
||||
Double montant = (masseAPartager * 0.4) / (nombreCombinaisons * 6); // 40% pour l'ordre exact, 6 permutations
|
||||
return new ReponsePaiementMulti(pari.getId(), montant,
|
||||
TypePaiementMulti.MULTI_4, "Dead-Heat 3 premiers + 1 quatrième");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Implémentations similaires pour les autres types de dead-heat...
|
||||
private ReponsePaiementMulti gererDeadHeatDeuxPremiersDeuxTroisiemes(PariMulti pari, ResultatCourse resultat, Double masseAPartager) {
|
||||
// Article 3c: Implémentation spécifique
|
||||
return calculerPaiementGeneriqueDeadHeat(pari, resultat, masseAPartager, 2.0);
|
||||
}
|
||||
|
||||
private ReponsePaiementMulti gererDeadHeatDeuxPremiersUnTroisiemeUnQuatrieme(PariMulti pari, ResultatCourse resultat, Double masseAPartager) {
|
||||
// Article 3d: Implémentation spécifique
|
||||
return calculerPaiementGeneriqueDeadHeat(pari, resultat, masseAPartager, 4.0);
|
||||
}
|
||||
|
||||
private ReponsePaiementMulti gererDeadHeatTroisSeconds(PariMulti pari, ResultatCourse resultat, Double masseAPartager) {
|
||||
// Article 3e: Implémentation spécifique
|
||||
return calculerPaiementGeneriqueDeadHeat(pari, resultat, masseAPartager, 2.0);
|
||||
}
|
||||
|
||||
private ReponsePaiementMulti gererDeadHeatDeuxSecondsUnQuatrieme(PariMulti pari, ResultatCourse resultat, Double masseAPartager) {
|
||||
// Article 3f: Implémentation spécifique
|
||||
return calculerPaiementGeneriqueDeadHeat(pari, resultat, masseAPartager, 4.0);
|
||||
}
|
||||
|
||||
private ReponsePaiementMulti gererDeadHeatDeuxTroisiemes(PariMulti pari, ResultatCourse resultat, Double masseAPartager) {
|
||||
// Article 3g: Implémentation spécifique
|
||||
return calculerPaiementGeneriqueDeadHeat(pari, resultat, masseAPartager, 4.0);
|
||||
}
|
||||
|
||||
private ReponsePaiementMulti gererDeadHeatDeuxQuatriemes(PariMulti pari, ResultatCourse resultat, Double masseAPartager) {
|
||||
// Article 3h: Implémentation spécifique
|
||||
return calculerPaiementGeneriqueDeadHeat(pari, resultat, masseAPartager, 4.0);
|
||||
}
|
||||
|
||||
private ReponsePaiementMulti calculerPaiementGeneriqueDeadHeat(PariMulti pari, ResultatCourse resultat,
|
||||
Double masseAPartager, Double coefficient) {
|
||||
// Méthode générique pour les dead-heat avec coefficient spécifique
|
||||
List<Cheval> chevauxPari = pari.getChevauxSelectionnes();
|
||||
|
||||
if (chevauxPari.stream().allMatch(cheval ->
|
||||
estDansListe(cheval, resultat.getPremiers()) ||
|
||||
estDansListe(cheval, resultat.getSeconds()) ||
|
||||
estDansListe(cheval, resultat.getTroisiemes()) ||
|
||||
estDansListe(cheval, resultat.getQuatriemes()))) {
|
||||
|
||||
int nombreCombinaisons = compterCombinaisonsGagnantesDeadHeat(resultat);
|
||||
Double montant = (masseAPartager * 0.4) / (nombreCombinaisons * coefficient);
|
||||
return new ReponsePaiementMulti(pari.getId(), montant,
|
||||
TypePaiementMulti.MULTI_4, "Dead-Heat générique");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private int calculerNombreCombinaisonsDeadHeat(int nombreChevaux, int prise) {
|
||||
// Calcul des combinaisons C(n, k)
|
||||
if (prise == 0) return 1;
|
||||
if (nombreChevaux < prise) return 0;
|
||||
|
||||
int resultat = 1;
|
||||
for (int i = 1; i <= prise; i++) {
|
||||
resultat = resultat * (nombreChevaux - i + 1) / i;
|
||||
}
|
||||
return resultat;
|
||||
}
|
||||
|
||||
private int compterCombinaisonsGagnantesDeadHeat(ResultatCourse resultat) {
|
||||
// Comptage simplifié des combinaisons gagnantes
|
||||
// Implémentation réelle dépendrait de la structure exacte du dead-heat
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Gestion de la cagnotte (Articles 9 et 10)
|
||||
private void transfererEnCagnotte(ResultatCourse resultat) {
|
||||
CagnotteMulti cagnotte = CagnotteMulti.builder()
|
||||
.montant(resultat.getMasseAPartager())
|
||||
.dateCreation(LocalDateTime.now())
|
||||
.utilisee(false)
|
||||
.courseSourceId(resultat.getCourse().getId())
|
||||
.build();
|
||||
cagnotteRepository.save(cagnotte);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void utiliserCagnotte(Long cagnotteId, Long courseId) {
|
||||
CagnotteMulti cagnotte = cagnotteRepository.findById(cagnotteId)
|
||||
.orElseThrow(() -> new RuntimeException("Cagnotte non trouvée"));
|
||||
|
||||
if (cagnotte.getUtilisee()) {
|
||||
throw new RuntimeException("Cagnotte déjà utilisée");
|
||||
}
|
||||
|
||||
cagnotte.setUtilisee(true);
|
||||
cagnotte.setDateUtilisation(LocalDateTime.now());
|
||||
cagnotteRepository.save(cagnotte);
|
||||
|
||||
// Ajouter le montant à la masse à partager de la course
|
||||
ResultatCourse resultat = resultatRepository.findByCourseId(courseId);
|
||||
if (resultat != null) {
|
||||
resultat.setMasseAPartager(resultat.getMasseAPartager() + cagnotte.getMontant());
|
||||
resultatRepository.save(resultat);
|
||||
}
|
||||
}
|
||||
|
||||
// Méthodes utilitaires
|
||||
private Double calculerMasseAPartager(ResultatCourse resultat) {
|
||||
// Article 5: MAP = RNET - MREMB - PRELEV
|
||||
return resultat.getRecetteNette() -
|
||||
resultat.getMontantRembourse() -
|
||||
resultat.getPrelevementsLegaux();
|
||||
}
|
||||
|
||||
private ResultatCourse creerResultat(RequeteResultatMulti requete) {
|
||||
Course course = courseRepository.findById(requete.getCourseId())
|
||||
.orElseThrow(() -> new RuntimeException("Course non trouvée"));
|
||||
|
||||
return ResultatCourse.builder()
|
||||
.course(course)
|
||||
.premiers(chevalRepository.findAllById(requete.getPremiersIds()))
|
||||
.seconds(chevalRepository.findAllById(requete.getSecondsIds()))
|
||||
.troisiemes(chevalRepository.findAllById(requete.getTroisiemesIds()))
|
||||
.quatriemes(chevalRepository.findAllById(requete.getQuatriemesIds()))
|
||||
.ordreArrivee(chevalRepository.findAllById(requete.getOrdreArriveeIds()))
|
||||
.recetteNette(requete.getRecetteNette())
|
||||
.prelevementsLegaux(requete.getPrelevementsLegaux())
|
||||
.montantRembourse(0.0)
|
||||
.build();
|
||||
}
|
||||
|
||||
private void enregistrerPaiement(PariMulti pari, ReponsePaiementMulti reponse) {
|
||||
PaiementMulti paiement = PaiementMulti.builder()
|
||||
.pari(pari)
|
||||
.montant(reponse.getMontant())
|
||||
.heurePaiement(LocalDateTime.now())
|
||||
.typePaiement(reponse.getTypePaiement())
|
||||
.build();
|
||||
paiementRepository.save(paiement);
|
||||
}
|
||||
|
||||
// Méthodes supplémentaires pour la gestion des cas particuliers (Article 9)
|
||||
@Transactional
|
||||
public List<ReponsePaiementMulti> gererCasParticuliers(RequeteResultatMulti requete) {
|
||||
ResultatCourse resultat = creerResultat(requete);
|
||||
List<PariMulti> paris = pariRepository.findByCourseId(requete.getCourseId());
|
||||
|
||||
// Article 9: Cas où il n'y a aucune mise sur la combinaison gagnante
|
||||
if (!existeParisGagnants(paris, resultat)) {
|
||||
return appliquerReglesDegradation(resultat, paris);
|
||||
}
|
||||
|
||||
return calculerPaiements(requete);
|
||||
}
|
||||
|
||||
private boolean existeParisGagnants(List<PariMulti> paris, ResultatCourse resultat) {
|
||||
for (PariMulti pari : paris) {
|
||||
if (estCombinaisonGagnante(pari.getChevauxSelectionnes(), resultat)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private List<ReponsePaiementMulti> appliquerReglesDegradation(ResultatCourse resultat, List<PariMulti> paris) {
|
||||
// Article 9: Application des règles de dégradation
|
||||
List<ReponsePaiementMulti> paiements = new ArrayList<>();
|
||||
Double masseAPartager = resultat.getMasseAPartager();
|
||||
|
||||
// Essayer successivement les combinaisons dégradées
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (PariMulti pari : paris) {
|
||||
ReponsePaiementMulti paiement = calculerPaiementDegrade(pari, resultat, masseAPartager, i);
|
||||
if (paiement != null) {
|
||||
paiements.add(paiement);
|
||||
enregistrerPaiement(pari, paiement);
|
||||
return paiements; // On s'arrête au premier succès
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Si aucune combinaison dégradée ne fonctionne, transférer en cagnotte
|
||||
transfererEnCagnotte(resultat);
|
||||
return paiements;
|
||||
}
|
||||
|
||||
private ReponsePaiementMulti calculerPaiementDegrade(PariMulti pari, ResultatCourse resultat,
|
||||
Double masseAPartager, int niveauDegradation) {
|
||||
// Implémentation des règles de dégradation selon l'article 9
|
||||
List<Cheval> chevauxPari = pari.getChevauxSelectionnes();
|
||||
|
||||
// Vérifier différents patterns de dégradation
|
||||
boolean estGagnant = false;
|
||||
switch (niveauDegradation) {
|
||||
case 0: // 1er, 2ème, 3ème, 5ème
|
||||
estGagnant = estPatternDegrade(chevauxPari, resultat, 0, 1, 2, 4);
|
||||
break;
|
||||
case 1: // 1er, 2ème, 4ème, 5ème
|
||||
estGagnant = estPatternDegrade(chevauxPari, resultat, 0, 1, 3, 4);
|
||||
break;
|
||||
case 2: // 1er, 3ème, 4ème, 5ème
|
||||
estGagnant = estPatternDegrade(chevauxPari, resultat, 0, 2, 3, 4);
|
||||
break;
|
||||
case 3: // 2ème, 3ème, 4ème, 5ème
|
||||
estGagnant = estPatternDegrade(chevauxPari, resultat, 1, 2, 3, 4);
|
||||
break;
|
||||
}
|
||||
|
||||
if (estGagnant) {
|
||||
Double montant = calculerMontantPari(pari.getTypeMulti(), masseAPartager);
|
||||
TypePaiementMulti typePaiement = getTypePaiementFromTypeMulti(pari.getTypeMulti());
|
||||
return new ReponsePaiementMulti(pari.getId(), montant, typePaiement,
|
||||
"Paiement dégradé niveau " + niveauDegradation);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean estPatternDegrade(List<Cheval> chevauxPari, ResultatCourse resultat,
|
||||
int pos1, int pos2, int pos3, int pos4) {
|
||||
// Vérifier un pattern spécifique de dégradation
|
||||
if (resultat.getOrdreArrivee().size() < 5) return false;
|
||||
|
||||
Cheval cheval1 = resultat.getOrdreArrivee().get(pos1);
|
||||
Cheval cheval2 = resultat.getOrdreArrivee().get(pos2);
|
||||
Cheval cheval3 = resultat.getOrdreArrivee().get(pos3);
|
||||
Cheval cheval4 = resultat.getOrdreArrivee().get(pos4);
|
||||
|
||||
return chevauxPari.contains(cheval1) &&
|
||||
chevauxPari.contains(cheval2) &&
|
||||
chevauxPari.contains(cheval3) &&
|
||||
chevauxPari.contains(cheval4);
|
||||
}
|
||||
|
||||
// Méthodes pour les statistiques et rapports
|
||||
public Map<TypeMulti, Integer> getStatistiquesParis(Long courseId) {
|
||||
List<PariMulti> paris = pariRepository.findByCourseId(courseId);
|
||||
return paris.stream()
|
||||
.collect(Collectors.groupingBy(PariMulti::getTypeMulti,
|
||||
Collectors.summingInt(p -> 1)));
|
||||
}
|
||||
|
||||
public Map<TypeFormuleMulti, Integer> getStatistiquesFormules(Long courseId) {
|
||||
List<PariMulti> paris = pariRepository.findByCourseId(courseId);
|
||||
return paris.stream()
|
||||
.collect(Collectors.groupingBy(PariMulti::getTypeFormule,
|
||||
Collectors.summingInt(p -> 1)));
|
||||
}
|
||||
|
||||
public Double getMasseAPartagerCourse(Long courseId) {
|
||||
ResultatCourse resultat = resultatRepository.findByCourseId(courseId);
|
||||
return resultat != null ? resultat.getMasseAPartager() : 0.0;
|
||||
}
|
||||
|
||||
public List<CagnotteMulti> getCagnottesDisponibles() {
|
||||
return cagnotteRepository.findByUtilisee(false);
|
||||
}
|
||||
|
||||
public List<PaiementMulti> getHistoriquePaiementsParieur(Long parieurId) {
|
||||
List<PariMulti> paris = pariRepository.findByParieurId(parieurId);
|
||||
return paris.stream()
|
||||
.flatMap(pari -> paiementRepository.findByPariId(pari.getId()).stream())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
67
src/main/java/com/pmumali/ch11_quinteplus/InitData.java
Normal file
67
src/main/java/com/pmumali/ch11_quinteplus/InitData.java
Normal file
@@ -0,0 +1,67 @@
|
||||
package com.pmumali.ch11_quinteplus;
|
||||
|
||||
import com.pmumali.ch11_quinteplus.model.Cheval;
|
||||
import com.pmumali.ch11_quinteplus.model.Course;
|
||||
import com.pmumali.ch11_quinteplus.repository.ChevalRepository;
|
||||
import com.pmumali.ch11_quinteplus.repository.CourseRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
|
||||
@Component
|
||||
public class InitData implements CommandLineRunner {
|
||||
|
||||
@Autowired
|
||||
private CourseRepository repositoryCourse;
|
||||
|
||||
@Autowired
|
||||
private ChevalRepository repositoryCheval;
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
// Création d'une course de test
|
||||
Course course = new Course();
|
||||
course.setNom("Course du 15 août");
|
||||
course.setDate(LocalDateTime.now().plusDays(1));
|
||||
course.setLieu("Hippodrome de Bamako");
|
||||
course.setEstTerminee(false);
|
||||
|
||||
Course courseSauvee = repositoryCourse.save(course);
|
||||
|
||||
// Création de chevaux de test
|
||||
Cheval cheval1 = new Cheval();
|
||||
cheval1.setNom("Foudre");
|
||||
cheval1.setNumero(1);
|
||||
cheval1.setEstNonPartant(false);
|
||||
cheval1.setCourse(courseSauvee);
|
||||
|
||||
Cheval cheval2 = new Cheval();
|
||||
cheval2.setNom("Éclair");
|
||||
cheval2.setNumero(2);
|
||||
cheval2.setEstNonPartant(false);
|
||||
cheval2.setCourse(courseSauvee);
|
||||
|
||||
Cheval cheval3 = new Cheval();
|
||||
cheval3.setNom("Tonnerre");
|
||||
cheval3.setNumero(3);
|
||||
cheval3.setEstNonPartant(false);
|
||||
cheval3.setCourse(courseSauvee);
|
||||
|
||||
Cheval cheval4 = new Cheval();
|
||||
cheval4.setNom("Vent");
|
||||
cheval4.setNumero(4);
|
||||
cheval4.setEstNonPartant(false);
|
||||
cheval4.setCourse(courseSauvee);
|
||||
|
||||
Cheval cheval5 = new Cheval();
|
||||
cheval5.setNom("Pluie");
|
||||
cheval5.setNumero(5);
|
||||
cheval5.setEstNonPartant(false);
|
||||
cheval5.setCourse(courseSauvee);
|
||||
|
||||
repositoryCheval.saveAll(Arrays.asList(cheval1, cheval2, cheval3, cheval4, cheval5));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.pmumali.ch11_quinteplus.controller;
|
||||
|
||||
|
||||
|
||||
import com.pmumali.ch11_quinteplus.model.Cagnotte;
|
||||
import com.pmumali.ch11_quinteplus.service.ServiceCagnotte;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/cagnotte")
|
||||
public class ControleurCagnotte {
|
||||
|
||||
@Autowired
|
||||
private ServiceCagnotte serviceCagnotte;
|
||||
|
||||
@GetMapping("/montant")
|
||||
public ResponseEntity<Double> getMontantCagnotteActive() {
|
||||
return ResponseEntity.ok(serviceCagnotte.getMontantCagnotteActive());
|
||||
}
|
||||
|
||||
@GetMapping("/historique")
|
||||
public ResponseEntity<List<Cagnotte>> getHistoriqueCagnottes() {
|
||||
return ResponseEntity.ok(serviceCagnotte.getHistoriqueCagnottes());
|
||||
}
|
||||
|
||||
@PostMapping("/gerer-cas-particuliers")
|
||||
public ResponseEntity<String> gererCasParticuliers() {
|
||||
serviceCagnotte.gererCasParticuliersCagnotte();
|
||||
return ResponseEntity.ok("Cas particuliers de cagnotte gérés avec succès");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.pmumali.ch11_quinteplus.controller;
|
||||
|
||||
import com.pmumali.ch11_quinteplus.model.Course;
|
||||
import com.pmumali.ch11_quinteplus.service.ServiceCourse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/courses")
|
||||
public class ControleurCourse {
|
||||
|
||||
@Autowired
|
||||
private ServiceCourse serviceCourse;
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<Course> creerCourse(@RequestBody Course course) {
|
||||
return ResponseEntity.ok(serviceCourse.creerCourse(course));
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<List<Course>> obtenirToutesCourses() {
|
||||
return ResponseEntity.ok(serviceCourse.obtenirToutesCourses());
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<Course> obtenirCourseParId(@PathVariable Long id) {
|
||||
Course course = serviceCourse.obtenirCourseParId(id);
|
||||
return course != null ? ResponseEntity.ok(course) : ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
@GetMapping("/terminees")
|
||||
public ResponseEntity<List<Course>> obtenirCoursesTerminees() {
|
||||
return ResponseEntity.ok(serviceCourse.obtenirCoursesTerminees());
|
||||
}
|
||||
|
||||
@GetMapping("/avenir")
|
||||
public ResponseEntity<List<Course>> obtenirCoursesAVenir() {
|
||||
return ResponseEntity.ok(serviceCourse.obtenirCoursesAVenir());
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public ResponseEntity<Course> mettreAJourCourse(@PathVariable Long id, @RequestBody Course course) {
|
||||
Course courseMaj = serviceCourse.mettreAJourCourse(id, course);
|
||||
return courseMaj != null ? ResponseEntity.ok(courseMaj) : ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public ResponseEntity<?> supprimerCourse(@PathVariable Long id) {
|
||||
boolean supprime = serviceCourse.supprimerCourse(id);
|
||||
return supprime ? ResponseEntity.ok().build() : ResponseEntity.notFound().build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.pmumali.ch11_quinteplus.controller;
|
||||
|
||||
import com.pmumali.ch11_quinteplus.model.Gains;
|
||||
import com.pmumali.ch11_quinteplus.model.Resultat;
|
||||
import com.pmumali.ch11_quinteplus.service.ServiceGains;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/gains")
|
||||
public class ControleurGains {
|
||||
|
||||
@Autowired
|
||||
private ServiceGains serviceGains;
|
||||
|
||||
@PostMapping("/calculer/{courseId}")
|
||||
public ResponseEntity<Gains> calculerGains(@PathVariable Long courseId, @RequestBody Resultat resultat) {
|
||||
return ResponseEntity.ok(serviceGains.calculerGains(courseId, resultat));
|
||||
}
|
||||
|
||||
@GetMapping("/course/{courseId}")
|
||||
public ResponseEntity<Gains> obtenirGainsParCourse(@PathVariable Long courseId) {
|
||||
Gains gains = serviceGains.obtenirGainsParCourse(courseId);
|
||||
return gains != null ? ResponseEntity.ok(gains) : ResponseEntity.notFound().build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.pmumali.ch11_quinteplus.controller;
|
||||
|
||||
import com.pmumali.ch11_quinteplus.model.Pari;
|
||||
import com.pmumali.ch11_quinteplus.service.ServicePari;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/paris")
|
||||
public class ControleurPari {
|
||||
|
||||
@Autowired
|
||||
private ServicePari servicePari;
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<Pari> placerPari(@RequestBody Pari pari) {
|
||||
return ResponseEntity.ok(servicePari.placerPari(pari));
|
||||
}
|
||||
|
||||
@GetMapping("/course/{courseId}")
|
||||
public ResponseEntity<List<Pari>> obtenirParisParCourse(@PathVariable Long courseId) {
|
||||
return ResponseEntity.ok(servicePari.obtenirParisParCourse(courseId));
|
||||
}
|
||||
|
||||
@GetMapping("/parieur/{nomParieur}")
|
||||
public ResponseEntity<List<Pari>> obtenirParisParParieur(@PathVariable String nomParieur) {
|
||||
return ResponseEntity.ok(servicePari.obtenirParisParParieur(nomParieur));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<Pari> obtenirPariParId(@PathVariable Long id) {
|
||||
Pari pari = servicePari.obtenirPariParId(id);
|
||||
return pari != null ? ResponseEntity.ok(pari) : ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public ResponseEntity<Pari> mettreAJourPari(@PathVariable Long id, @RequestBody Pari pari) {
|
||||
Pari pariMaj = servicePari.mettreAJourPari(id, pari);
|
||||
return pariMaj != null ? ResponseEntity.ok(pariMaj) : ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public ResponseEntity<?> supprimerPari(@PathVariable Long id) {
|
||||
boolean supprime = servicePari.supprimerPari(id);
|
||||
return supprime ? ResponseEntity.ok().build() : ResponseEntity.notFound().build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.pmumali.ch11_quinteplus.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class Cagnotte {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private double montant;
|
||||
private LocalDateTime dateCreation;
|
||||
private LocalDateTime dateDerniereModification;
|
||||
private String statut; // ACTIVE, UTILISEE, REPORTEE
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "course_source_id")
|
||||
private Course courseSource;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "course_destination_id")
|
||||
private Course courseDestination;
|
||||
|
||||
private String typeCagnotte; // ORDRE_EXACT, ORDRE_INEXACT, COMBINEE
|
||||
|
||||
public Cagnotte(double montant, Course courseSource, String typeCagnotte) {
|
||||
this.montant = montant;
|
||||
this.courseSource = courseSource;
|
||||
this.typeCagnotte = typeCagnotte;
|
||||
this.dateCreation = LocalDateTime.now();
|
||||
this.statut = "ACTIVE";
|
||||
}
|
||||
}
|
||||
23
src/main/java/com/pmumali/ch11_quinteplus/model/Cheval.java
Normal file
23
src/main/java/com/pmumali/ch11_quinteplus/model/Cheval.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package com.pmumali.ch11_quinteplus.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Table(name = "cheval")
|
||||
public class Cheval {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String nom;
|
||||
private int numero;
|
||||
private boolean estNonPartant;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "course_id")
|
||||
private Course course;
|
||||
}
|
||||
32
src/main/java/com/pmumali/ch11_quinteplus/model/Course.java
Normal file
32
src/main/java/com/pmumali/ch11_quinteplus/model/Course.java
Normal file
@@ -0,0 +1,32 @@
|
||||
package com.pmumali.ch11_quinteplus.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Table(name = "course")
|
||||
public class Course {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String nom;
|
||||
private LocalDateTime date;
|
||||
private String lieu;
|
||||
private boolean estTerminee;
|
||||
|
||||
@OneToMany(mappedBy = "course", cascade = CascadeType.ALL)
|
||||
private List<Cheval> chevaux;
|
||||
|
||||
@OneToOne(mappedBy = "course", cascade = CascadeType.ALL)
|
||||
private Resultat resultat;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.pmumali.ch11_quinteplus.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class CourseDto {
|
||||
private Long id;
|
||||
private String nom;
|
||||
private LocalDateTime date;
|
||||
private String lieu;
|
||||
private boolean estTerminee;
|
||||
private List<Long> idsChevaux;
|
||||
}
|
||||
33
src/main/java/com/pmumali/ch11_quinteplus/model/Gains.java
Normal file
33
src/main/java/com/pmumali/ch11_quinteplus/model/Gains.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package com.pmumali.ch11_quinteplus.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Table(name = "gains")
|
||||
public class Gains {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "course_id")
|
||||
private Course course;
|
||||
|
||||
private double masseOrdreExact;
|
||||
private double masseOrdreInexact;
|
||||
private double masseBonus4;
|
||||
|
||||
private double dividendeOrdreExact;
|
||||
private double dividendeOrdreInexact;
|
||||
private double dividendeBonus4;
|
||||
|
||||
private LocalDateTime dateCalcul;
|
||||
|
||||
private double masseCagnotte;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.pmumali.ch11_quinteplus.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class GainsDto {
|
||||
private Long id;
|
||||
private Long courseId;
|
||||
private double masseOrdreExact;
|
||||
private double masseOrdreInexact;
|
||||
private double masseBonus4;
|
||||
private double dividendeOrdreExact;
|
||||
private double dividendeOrdreInexact;
|
||||
private double dividendeBonus4;
|
||||
private LocalDateTime dateCalcul;
|
||||
}
|
||||
36
src/main/java/com/pmumali/ch11_quinteplus/model/Pari.java
Normal file
36
src/main/java/com/pmumali/ch11_quinteplus/model/Pari.java
Normal file
@@ -0,0 +1,36 @@
|
||||
package com.pmumali.ch11_quinteplus.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Table(name = "pari")
|
||||
public class Pari {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String nomParieur;
|
||||
private LocalDateTime datePari;
|
||||
private double mise = 500.0;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "course_id")
|
||||
private Course course;
|
||||
|
||||
@ElementCollection
|
||||
private List<Long> chevauxSelectionnes;
|
||||
|
||||
@ElementCollection
|
||||
private List<Integer> ordrePredit;
|
||||
|
||||
private String typePari;
|
||||
|
||||
private boolean estPaye;
|
||||
}
|
||||
19
src/main/java/com/pmumali/ch11_quinteplus/model/PariDto.java
Normal file
19
src/main/java/com/pmumali/ch11_quinteplus/model/PariDto.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package com.pmumali.ch11_quinteplus.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class PariDto {
|
||||
private Long id;
|
||||
private String nomParieur;
|
||||
private LocalDateTime datePari;
|
||||
private double mise;
|
||||
private Long courseId;
|
||||
private List<Long> chevauxSelectionnes;
|
||||
private List<Integer> ordrePredit;
|
||||
private String typePari;
|
||||
private boolean estPaye;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.pmumali.ch11_quinteplus.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Table(name = "resultat")
|
||||
public class Resultat {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@OneToOne
|
||||
@JoinColumn(name = "course_id")
|
||||
private Course course;
|
||||
|
||||
@ElementCollection
|
||||
private List<Long> idsChevauxOrdreArrivee;
|
||||
|
||||
private boolean aDeadHeat;
|
||||
|
||||
@ElementCollection
|
||||
private List<Integer> positionsDeadHeat;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.pmumali.ch11_quinteplus.repository;
|
||||
|
||||
import com.pmumali.ch11_quinteplus.model.Cagnotte;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface CagnotteRepository extends JpaRepository<Cagnotte, Long> {
|
||||
List<Cagnotte> findByStatut(String statut);
|
||||
List<Cagnotte> findByCourseSourceId(Long courseId);
|
||||
List<Cagnotte> findByCourseDestinationId(Long courseId);
|
||||
List<Cagnotte> findByTypeCagnotte(String typeCagnotte);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.pmumali.ch11_quinteplus.repository;
|
||||
|
||||
import com.pmumali.ch11_quinteplus.model.Cheval;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface ChevalRepository extends JpaRepository<Cheval, Long> {
|
||||
List<Cheval> findByCourseId(Long courseId);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.pmumali.ch11_quinteplus.repository;
|
||||
|
||||
import com.pmumali.ch11_quinteplus.model.Course;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface CourseRepository extends JpaRepository<Course, Long> {
|
||||
List<Course> findByEstTerminee(boolean estTerminee);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.pmumali.ch11_quinteplus.repository;
|
||||
|
||||
import com.pmumali.ch11_quinteplus.model.Gains;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface GainsRepository extends JpaRepository<Gains, Long> {
|
||||
Gains findByCourseId(Long courseId);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.pmumali.ch11_quinteplus.repository;
|
||||
|
||||
import com.pmumali.ch11_quinteplus.model.Pari;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface PariRepository extends JpaRepository<Pari, Long> {
|
||||
List<Pari> findByCourseId(Long courseId);
|
||||
List<Pari> findByNomParieur(String nomParieur);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.pmumali.ch11_quinteplus.repository;
|
||||
|
||||
import com.pmumali.ch11_quinteplus.model.Resultat;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface ResultatRepository extends JpaRepository<Resultat,Long> {
|
||||
|
||||
Resultat findByCourseId(Long courseId);
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
package com.pmumali.ch11_quinteplus.service;
|
||||
|
||||
import com.pmumali.ch11_quinteplus.model.*;
|
||||
import com.pmumali.ch11_quinteplus.repository.CagnotteRepository;
|
||||
import com.pmumali.ch11_quinteplus.repository.CourseRepository;
|
||||
import com.pmumali.ch11_quinteplus.repository.GainsRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
public class ServiceCagnotte {
|
||||
|
||||
@Autowired
|
||||
private CagnotteRepository cagnotteRepository;
|
||||
|
||||
@Autowired
|
||||
private CourseRepository repositoryCourse;
|
||||
|
||||
@Autowired
|
||||
private GainsRepository repositoryGains;
|
||||
|
||||
// Méthode principale pour gérer la cagnotte selon l'article 9
|
||||
public void gererCagnotte(Gains gains, List<Pari> paris, Resultat resultat) {
|
||||
Course course = gains.getCourse();
|
||||
|
||||
// Vérifier s'il n'y a aucun gagnant dans une catégorie
|
||||
List<Pari> gagnantsOrdreExact = determinerGagnantsOrdreExact(paris, resultat);
|
||||
List<Pari> gagnantsOrdreInexact = determinerGagnantsOrdreInexact(paris, resultat);
|
||||
|
||||
double montantCagnotte = 0.0;
|
||||
boolean cagnotteOrdreExact = false;
|
||||
boolean cagnotteOrdreInexact = false;
|
||||
|
||||
// Article 9a: Aucun gagnant dans l'ordre exact
|
||||
if (gagnantsOrdreExact.isEmpty()) {
|
||||
montantCagnotte += gains.getMasseOrdreExact();
|
||||
gains.setMasseOrdreExact(0.0);
|
||||
gains.setDividendeOrdreExact(0.0);
|
||||
cagnotteOrdreExact = true;
|
||||
}
|
||||
|
||||
// Article 9a: Aucun gagnant dans l'ordre inexact
|
||||
if (gagnantsOrdreInexact.isEmpty()) {
|
||||
montantCagnotte += gains.getMasseOrdreInexact();
|
||||
gains.setMasseOrdreInexact(0.0);
|
||||
gains.setDividendeOrdreInexact(0.0);
|
||||
cagnotteOrdreInexact = true;
|
||||
}
|
||||
|
||||
// Si une cagnotte doit être créée
|
||||
if (montantCagnotte > 0) {
|
||||
String typeCagnotte = determinerTypeCagnotte(cagnotteOrdreExact, cagnotteOrdreInexact);
|
||||
Cagnotte cagnotte = new Cagnotte(montantCagnotte, course, typeCagnotte);
|
||||
cagnotteRepository.save(cagnotte);
|
||||
|
||||
// Mettre à jour le gain avec le montant de la cagnotte
|
||||
gains.setMasseCagnotte(montantCagnotte);
|
||||
}
|
||||
|
||||
// Gérer le report des cagnottes existantes vers la prochaine course
|
||||
reporterCagnottesVersProchaineCourse(course);
|
||||
}
|
||||
|
||||
private String determinerTypeCagnotte(boolean ordreExact, boolean ordreInexact) {
|
||||
if (ordreExact && ordreInexact) {
|
||||
return "COMBINEE";
|
||||
} else if (ordreExact) {
|
||||
return "ORDRE_EXACT";
|
||||
} else {
|
||||
return "ORDRE_INEXACT";
|
||||
}
|
||||
}
|
||||
|
||||
// Méthode pour reporter les cagnottes vers la prochaine course
|
||||
public void reporterCagnottesVersProchaineCourse(Course courseActuelle) {
|
||||
// Trouver toutes les cagnottes actives
|
||||
List<Cagnotte> cagnottesActives = cagnotteRepository.findByStatut("ACTIVE");
|
||||
|
||||
// Trouver la prochaine course
|
||||
Course prochaineCourse = trouverProchaineCourse(courseActuelle);
|
||||
|
||||
if (prochaineCourse != null) {
|
||||
for (Cagnotte cagnotte : cagnottesActives) {
|
||||
// Article 9b: Reporter la cagnotte vers la prochaine course
|
||||
cagnotte.setCourseDestination(prochaineCourse);
|
||||
cagnotte.setDateDerniereModification(LocalDateTime.now());
|
||||
cagnotteRepository.save(cagnotte);
|
||||
}
|
||||
} else {
|
||||
// Article 9d: Si pas de course la semaine suivante, reporter à la prochaine disponible
|
||||
for (Cagnotte cagnotte : cagnottesActives) {
|
||||
cagnotte.setStatut("REPORTEE");
|
||||
cagnotte.setDateDerniereModification(LocalDateTime.now());
|
||||
cagnotteRepository.save(cagnotte);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Méthode pour appliquer les cagnottes reportées à une course
|
||||
public void appliquerCagnottesReportees(Course course) {
|
||||
// Trouver toutes les cagnottes reportées pour cette course
|
||||
List<Cagnotte> cagnottesReportees = cagnotteRepository.findByCourseDestinationId(course.getId());
|
||||
|
||||
double montantTotalCagnotte = 0.0;
|
||||
|
||||
for (Cagnotte cagnotte : cagnottesReportees) {
|
||||
if ("REPORTEE".equals(cagnotte.getStatut())) {
|
||||
montantTotalCagnotte += cagnotte.getMontant();
|
||||
|
||||
// Marquer la cagnotte comme utilisée
|
||||
cagnotte.setStatut("UTILISEE");
|
||||
cagnotte.setDateDerniereModification(LocalDateTime.now());
|
||||
cagnotteRepository.save(cagnotte);
|
||||
}
|
||||
}
|
||||
|
||||
// Appliquer la cagnotte à la masse à partager de la course
|
||||
if (montantTotalCagnotte > 0) {
|
||||
Optional<Gains> gainsOpt = Optional.ofNullable(repositoryGains.findByCourseId(course.getId()));
|
||||
|
||||
if (gainsOpt.isPresent()) {
|
||||
Gains gains = gainsOpt.get();
|
||||
|
||||
// Article 9b: Ajouter la cagnotte à la masse à partager de l'ordre exact
|
||||
gains.setMasseOrdreExact(gains.getMasseOrdreExact() + montantTotalCagnotte);
|
||||
gains.setMasseCagnotte(montantTotalCagnotte);
|
||||
|
||||
repositoryGains.save(gains);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Méthode pour trouver la prochaine course après une date donnée
|
||||
private Course trouverProchaineCourse(Course courseActuelle) {
|
||||
List<Course> toutesCourses = repositoryCourse.findAll();
|
||||
Course prochaineCourse = null;
|
||||
|
||||
for (Course course : toutesCourses) {
|
||||
if (course.getDate().isAfter(courseActuelle.getDate()) &&
|
||||
(prochaineCourse == null || course.getDate().isBefore(prochaineCourse.getDate()))) {
|
||||
prochaineCourse = course;
|
||||
}
|
||||
}
|
||||
|
||||
return prochaineCourse;
|
||||
}
|
||||
|
||||
// Méthode pour gérer les cas particuliers de cagnotte (Article 9c)
|
||||
public void gererCasParticuliersCagnotte() {
|
||||
// Article 9c: Si plusieurs cagnottes sont constituées dans l'intervalle,
|
||||
// elles sont toutes ajoutées à la masse à partager de la prochaine course
|
||||
|
||||
List<Cagnotte> cagnottesActives = cagnotteRepository.findByStatut("ACTIVE");
|
||||
|
||||
if (cagnottesActives.size() > 1) {
|
||||
// Trouver la prochaine course
|
||||
Course prochaineCourse = trouverProchaineCourseParDate(LocalDateTime.now());
|
||||
|
||||
if (prochaineCourse != null) {
|
||||
double montantTotal = 0.0;
|
||||
|
||||
for (Cagnotte cagnotte : cagnottesActives) {
|
||||
montantTotal += cagnotte.getMontant();
|
||||
cagnotte.setCourseDestination(prochaineCourse);
|
||||
cagnotte.setStatut("UTILISEE");
|
||||
cagnotte.setDateDerniereModification(LocalDateTime.now());
|
||||
cagnotteRepository.save(cagnotte);
|
||||
}
|
||||
|
||||
// Appliquer le montant total à la prochaine course
|
||||
appliquerCagnotteACourse(prochaineCourse, montantTotal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Course trouverProchaineCourseParDate(LocalDateTime date) {
|
||||
List<Course> toutesCourses = repositoryCourse.findAll();
|
||||
Course prochaineCourse = null;
|
||||
|
||||
for (Course course : toutesCourses) {
|
||||
if (course.getDate().isAfter(date) &&
|
||||
(prochaineCourse == null || course.getDate().isBefore(prochaineCourse.getDate()))) {
|
||||
prochaineCourse = course;
|
||||
}
|
||||
}
|
||||
|
||||
return prochaineCourse;
|
||||
}
|
||||
|
||||
private void appliquerCagnotteACourse(Course course, double montant) {
|
||||
Optional<Gains> gainsOpt = Optional.ofNullable(repositoryGains.findByCourseId(course.getId()));
|
||||
|
||||
if (gainsOpt.isPresent()) {
|
||||
Gains gains = gainsOpt.get();
|
||||
gains.setMasseOrdreExact(gains.getMasseOrdreExact() + montant);
|
||||
gains.setMasseCagnotte(montant);
|
||||
repositoryGains.save(gains);
|
||||
} else {
|
||||
// Créer un gains pour la course si il n'existe pas encore
|
||||
Gains gains = new Gains();
|
||||
gains.setCourse(course);
|
||||
gains.setMasseOrdreExact(montant);
|
||||
gains.setMasseCagnotte(montant);
|
||||
gains.setDateCalcul(LocalDateTime.now());
|
||||
repositoryGains.save(gains);
|
||||
}
|
||||
}
|
||||
|
||||
// Méthode pour obtenir le montant total de la cagnotte active
|
||||
public double getMontantCagnotteActive() {
|
||||
List<Cagnotte> cagnottesActives = cagnotteRepository.findByStatut("ACTIVE");
|
||||
return cagnottesActives.stream().mapToDouble(Cagnotte::getMontant).sum();
|
||||
}
|
||||
|
||||
// Méthode pour obtenir l'historique des cagnottes
|
||||
public List<Cagnotte> getHistoriqueCagnottes() {
|
||||
return cagnotteRepository.findAll();
|
||||
}
|
||||
|
||||
// Méthodes utilitaires pour déterminer les gagnants
|
||||
private List<Pari> determinerGagnantsOrdreExact(List<Pari> paris, Resultat resultat) {
|
||||
return paris.stream()
|
||||
.filter(pari -> estOrdreExact(pari, resultat))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<Pari> determinerGagnantsOrdreInexact(List<Pari> paris, Resultat resultat) {
|
||||
return paris.stream()
|
||||
.filter(pari -> estOrdreInexact(pari, resultat))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private boolean estOrdreExact(Pari pari, Resultat resultat) {
|
||||
return pari.getOrdrePredit().equals(resultat.getIdsChevauxOrdreArrivee());
|
||||
}
|
||||
|
||||
private boolean estOrdreInexact(Pari pari, Resultat resultat) {
|
||||
List<Long> chevauxArrivee = resultat.getIdsChevauxOrdreArrivee();
|
||||
List<Long> chevauxPari = pari.getChevauxSelectionnes();
|
||||
|
||||
return chevauxArrivee.containsAll(chevauxPari) &&
|
||||
!pari.getOrdrePredit().equals(chevauxArrivee);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.pmumali.ch11_quinteplus.service;
|
||||
|
||||
import com.pmumali.ch11_quinteplus.model.Course;
|
||||
import com.pmumali.ch11_quinteplus.repository.ChevalRepository;
|
||||
import com.pmumali.ch11_quinteplus.repository.CourseRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Service
|
||||
public class ServiceCourse {
|
||||
|
||||
@Autowired
|
||||
private CourseRepository repositoryCourse;
|
||||
|
||||
@Autowired
|
||||
private ChevalRepository repositoryCheval;
|
||||
|
||||
public Course creerCourse(Course course) {
|
||||
return repositoryCourse.save(course);
|
||||
}
|
||||
|
||||
public List<Course> obtenirToutesCourses() {
|
||||
return repositoryCourse.findAll();
|
||||
}
|
||||
|
||||
public Course obtenirCourseParId(Long id) {
|
||||
return repositoryCourse.findById(id).orElse(null);
|
||||
}
|
||||
|
||||
public List<Course> obtenirCoursesTerminees() {
|
||||
return repositoryCourse.findByEstTerminee(true);
|
||||
}
|
||||
|
||||
public List<Course> obtenirCoursesAVenir() {
|
||||
return repositoryCourse.findByEstTerminee(false);
|
||||
}
|
||||
|
||||
public Course mettreAJourCourse(Long id, Course detailsCourse) {
|
||||
Course course = repositoryCourse.findById(id).orElse(null);
|
||||
if (course != null) {
|
||||
course.setNom(detailsCourse.getNom());
|
||||
course.setDate(detailsCourse.getDate());
|
||||
course.setLieu(detailsCourse.getLieu());
|
||||
course.setEstTerminee(detailsCourse.isEstTerminee());
|
||||
return repositoryCourse.save(course);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean supprimerCourse(Long id) {
|
||||
if (repositoryCourse.existsById(id)) {
|
||||
repositoryCourse.deleteById(id);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
1052
src/main/java/com/pmumali/ch11_quinteplus/service/ServiceGains.java
Normal file
1052
src/main/java/com/pmumali/ch11_quinteplus/service/ServiceGains.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,619 @@
|
||||
package com.pmumali.ch11_quinteplus.service;
|
||||
|
||||
|
||||
import com.pmumali.ch11_quinteplus.model.Cheval;
|
||||
import com.pmumali.ch11_quinteplus.model.Pari;
|
||||
import com.pmumali.ch11_quinteplus.model.Resultat;
|
||||
import com.pmumali.ch11_quinteplus.repository.ChevalRepository;
|
||||
import com.pmumali.ch11_quinteplus.repository.CourseRepository;
|
||||
import com.pmumali.ch11_quinteplus.repository.GainsRepository;
|
||||
import com.pmumali.ch11_quinteplus.repository.PariRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
public class ServicePari {
|
||||
|
||||
@Autowired
|
||||
private PariRepository repositoryPari;
|
||||
|
||||
@Autowired
|
||||
private CourseRepository repositoryCourse;
|
||||
|
||||
@Autowired
|
||||
private ChevalRepository repositoryCheval;
|
||||
|
||||
@Autowired
|
||||
private GainsRepository repositoryGains;
|
||||
|
||||
private static final double MISE_DE_BASE = 500.0;
|
||||
private static final double MISE_MAXIMALE = 20 * MISE_DE_BASE;
|
||||
|
||||
public Pari placerPari(Pari pari) {
|
||||
// Vérification de la limite d'enjeu (Article 2)
|
||||
if (pari.getMise() > MISE_MAXIMALE) {
|
||||
pari.setMise(MISE_MAXIMALE);
|
||||
// TODO: Implémenter le remboursement de la différence
|
||||
}
|
||||
|
||||
// Vérification de la validité du pari selon le type
|
||||
if (!estPariValide(pari)) {
|
||||
throw new IllegalArgumentException("Pari invalide");
|
||||
}
|
||||
|
||||
return repositoryPari.save(pari);
|
||||
}
|
||||
|
||||
private boolean estPariValide(Pari pari) {
|
||||
// Vérifier que le pari a exactement 5 chevaux sélectionnés
|
||||
if (pari.getChevauxSelectionnes() == null || pari.getChevauxSelectionnes().size() != 5) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Vérifier que l'ordre prédit a exactement 5 positions
|
||||
if (pari.getOrdrePredit() == null || pari.getOrdrePredit().size() != 5) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Vérifier que tous les chevaux sélectionnés existent
|
||||
List<Cheval> chevaux = repositoryCheval.findAllById(pari.getChevauxSelectionnes());
|
||||
if (chevaux.size() != 5) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Vérifier que la course existe
|
||||
if (pari.getCourse() == null || !repositoryCourse.existsById(pari.getCourse().getId())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<Pari> obtenirParisParCourse(Long courseId) {
|
||||
return repositoryPari.findByCourseId(courseId);
|
||||
}
|
||||
|
||||
public List<Pari> obtenirParisParParieur(String nomParieur) {
|
||||
return repositoryPari.findByNomParieur(nomParieur);
|
||||
}
|
||||
|
||||
public Pari obtenirPariParId(Long id) {
|
||||
return repositoryPari.findById(id).orElse(null);
|
||||
}
|
||||
|
||||
public Pari mettreAJourPari(Long id, Pari detailsPari) {
|
||||
Pari pari = repositoryPari.findById(id).orElse(null);
|
||||
if (pari != null) {
|
||||
pari.setNomParieur(detailsPari.getNomParieur());
|
||||
pari.setMise(detailsPari.getMise());
|
||||
pari.setChevauxSelectionnes(detailsPari.getChevauxSelectionnes());
|
||||
pari.setOrdrePredit(detailsPari.getOrdrePredit());
|
||||
pari.setTypePari(detailsPari.getTypePari());
|
||||
return repositoryPari.save(pari);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean supprimerPari(Long id) {
|
||||
if (repositoryPari.existsById(id)) {
|
||||
repositoryPari.deleteById(id);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Méthodes pour gérer les dead-heats (Article 3)
|
||||
public Map<String, Object> gererDeadHeat(Resultat resultat, List<Pari> paris) {
|
||||
Map<String, Object> resultatsDeadHeat = new HashMap<>();
|
||||
|
||||
if (!resultat.isADeadHeat()) {
|
||||
resultatsDeadHeat.put("message", "Aucun dead-heat à traiter");
|
||||
return resultatsDeadHeat;
|
||||
}
|
||||
|
||||
// Déterminer le type de dead-heat et appliquer les règles appropriées
|
||||
String typeDeadHeat = determinerTypeDeadHeat(resultat);
|
||||
resultatsDeadHeat.put("typeDeadHeat", typeDeadHeat);
|
||||
|
||||
switch (typeDeadHeat) {
|
||||
case "CINQ_PREMIERS":
|
||||
resultatsDeadHeat.put("combinaisons", traiterDeadHeatCinqPremiers(resultat, paris));
|
||||
break;
|
||||
case "QUATRE_PREMIERS_UN_CINQUIEME":
|
||||
resultatsDeadHeat.put("combinaisons", traiterDeadHeatQuatrePremiersUnCinquieme(resultat, paris));
|
||||
break;
|
||||
case "TROIS_PREMIERS_DEUX_QUATRIEMES":
|
||||
resultatsDeadHeat.put("combinaisons", traiterDeadHeatTroisPremiersDeuxQuatriemes(resultat, paris));
|
||||
break;
|
||||
case "TROIS_PREMIERS_UN_QUATRIEME_UN_CINQUIEME":
|
||||
resultatsDeadHeat.put("combinaisons", traiterDeadHeatTroisPremiersUnQuatriemeUnCinquieme(resultat, paris));
|
||||
break;
|
||||
case "DEUX_PREMIERS_TROIS_TROISIEMES":
|
||||
resultatsDeadHeat.put("combinaisons", traiterDeadHeatDeuxPremiersTroisTroisiemes(resultat, paris));
|
||||
break;
|
||||
case "DEUX_PREMIERS_DEUX_TROISIEMES_UN_CINQUIEME":
|
||||
resultatsDeadHeat.put("combinaisons", traiterDeadHeatDeuxPremiersDeuxTroisiemesUnCinquieme(resultat, paris));
|
||||
break;
|
||||
case "DEUX_PREMIERS_UN_TROISIEME_DEUX_QUATRIEMES":
|
||||
resultatsDeadHeat.put("combinaisons", traiterDeadHeatDeuxPremiersUnTroisiemeDeuxQuatriemes(resultat, paris));
|
||||
break;
|
||||
case "DEUX_PREMIERS_UN_TROISIEME_UN_QUATRIEME_UN_CINQUIEME":
|
||||
resultatsDeadHeat.put("combinaisons", traiterDeadHeatDeuxPremiersUnTroisiemeUnQuatriemeUnCinquieme(resultat, paris));
|
||||
break;
|
||||
case "QUATRE_DEUXIEMES":
|
||||
resultatsDeadHeat.put("combinaisons", traiterDeadHeatQuatreDeuxiemes(resultat, paris));
|
||||
break;
|
||||
case "TROIS_DEUXIEMES_UN_CINQUIEME":
|
||||
resultatsDeadHeat.put("combinaisons", traiterDeadHeatTroisDeuxiemesUnCinquieme(resultat, paris));
|
||||
break;
|
||||
case "DEUX_DEUXIEMES_DEUX_QUATRIEMES":
|
||||
resultatsDeadHeat.put("combinaisons", traiterDeadHeatDeuxDeuxiemesDeuxQuatriemes(resultat, paris));
|
||||
break;
|
||||
case "DEUX_DEUXIEMES_UN_QUATRIEME_UN_CINQUIEME":
|
||||
resultatsDeadHeat.put("combinaisons", traiterDeadHeatDeuxDeuxiemesUnQuatriemeUnCinquieme(resultat, paris));
|
||||
break;
|
||||
case "TROIS_TROISIEMES":
|
||||
resultatsDeadHeat.put("combinaisons", traiterDeadHeatTroisTroisiemes(resultat, paris));
|
||||
break;
|
||||
case "DEUX_TROISIEMES_UN_CINQUIEME":
|
||||
resultatsDeadHeat.put("combinaisons", traiterDeadHeatDeuxTroisiemesUnCinquieme(resultat, paris));
|
||||
break;
|
||||
case "DEUX_QUATRIEMES":
|
||||
resultatsDeadHeat.put("combinaisons", traiterDeadHeatDeuxQuatriemes(resultat, paris));
|
||||
break;
|
||||
case "DEUX_CINQUIEMES":
|
||||
resultatsDeadHeat.put("combinaisons", traiterDeadHeatDeuxCinquiemes(resultat, paris));
|
||||
break;
|
||||
default:
|
||||
resultatsDeadHeat.put("erreur", "Type de dead-heat non reconnu");
|
||||
}
|
||||
|
||||
return resultatsDeadHeat;
|
||||
}
|
||||
|
||||
private String determinerTypeDeadHeat(Resultat resultat) {
|
||||
// Implémentation de la logique pour déterminer le type de dead-heat
|
||||
// Basé sur les positions avec dead-heat
|
||||
|
||||
List<Integer> positionsDeadHeat = resultat.getPositionsDeadHeat();
|
||||
Collections.sort(positionsDeadHeat);
|
||||
|
||||
// Simplification: détermination basée sur les positions en dead-heat
|
||||
if (positionsDeadHeat.contains(1) && positionsDeadHeat.size() >= 5) {
|
||||
return "CINQ_PREMIERS";
|
||||
} else if (positionsDeadHeat.contains(1) && positionsDeadHeat.contains(2)
|
||||
&& positionsDeadHeat.contains(3) && positionsDeadHeat.contains(4)) {
|
||||
return "QUATRE_PREMIERS_UN_CINQUIEME";
|
||||
} else if (positionsDeadHeat.contains(1) && positionsDeadHeat.contains(2)
|
||||
&& positionsDeadHeat.contains(3) && positionsDeadHeat.contains(4)
|
||||
&& positionsDeadHeat.contains(5)) {
|
||||
return "TROIS_PREMIERS_DEUX_QUATRIEMES";
|
||||
}
|
||||
// ... autres cas à implémenter selon l'article 3
|
||||
|
||||
return "AUTRE";
|
||||
}
|
||||
|
||||
// Implémentations des différents cas de dead-heat (Article 3)
|
||||
private List<Map<String, Object>> traiterDeadHeatCinqPremiers(Resultat resultat, List<Pari> paris) {
|
||||
List<Map<String, Object>> combinaisonsPayables = new ArrayList<>();
|
||||
|
||||
// Article 3a: Dead-heat de cinq chevaux ou plus classés à la première place
|
||||
// Toutes les combinaisons des chevaux classés premiers pris cinq à cinq
|
||||
List<Long> chevauxPremiers = obtenirChevauxParPosition(resultat, 1);
|
||||
|
||||
// Générer toutes les combinaisons de 5 chevaux parmi les premiers
|
||||
List<List<Long>> combinaisons = genererCombinaisons(chevauxPremiers, 5);
|
||||
|
||||
for (List<Long> combinaison : combinaisons) {
|
||||
Map<String, Object> details = new HashMap<>();
|
||||
details.put("combinaison", combinaison);
|
||||
details.put("type", "QUINTE_PLUS");
|
||||
details.put("permutations", 120); // 120 ordres possibles
|
||||
details.put("rapportUnique", true);
|
||||
|
||||
combinaisonsPayables.add(details);
|
||||
}
|
||||
|
||||
return combinaisonsPayables;
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> traiterDeadHeatQuatrePremiersUnCinquieme(Resultat resultat, List<Pari> paris) {
|
||||
List<Map<String, Object>> combinaisonsPayables = new ArrayList<>();
|
||||
|
||||
// Article 3b: Dead-heat de quatre chevaux classés à la première place et un ou plusieurs classés cinquième
|
||||
List<Long> chevauxPremiers = obtenirChevauxParPosition(resultat, 1);
|
||||
List<Long> chevauxCinquiemes = obtenirChevauxParPosition(resultat, 5);
|
||||
|
||||
// Combinaisons des quatre premiers avec un des cinquièmes
|
||||
for (Long chevalCinquieme : chevauxCinquiemes) {
|
||||
List<Long> combinaison = new ArrayList<>(chevauxPremiers);
|
||||
combinaison.add(chevalCinquieme);
|
||||
|
||||
Map<String, Object> details = new HashMap<>();
|
||||
details.put("combinaison", combinaison);
|
||||
details.put("type", "QUINTE_PLUS");
|
||||
details.put("ordreExactPermutations", 24); // 24 permutations pour l'ordre exact
|
||||
details.put("ordreInexactPermutations", 96); // 96 permutations pour l'ordre inexact
|
||||
|
||||
combinaisonsPayables.add(details);
|
||||
}
|
||||
|
||||
return combinaisonsPayables;
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> traiterDeadHeatTroisPremiersDeuxQuatriemes(Resultat resultat, List<Pari> paris) {
|
||||
List<Map<String, Object>> combinaisonsPayables = new ArrayList<>();
|
||||
|
||||
// Article 3c: Dead-heat de trois chevaux classés à la première place et deux chevaux classés à la quatrième place
|
||||
List<Long> chevauxPremiers = obtenirChevauxParPosition(resultat, 1);
|
||||
List<Long> chevauxQuatriemes = obtenirChevauxParPosition(resultat, 4);
|
||||
|
||||
// Générer toutes les combinaisons de 2 chevaux parmi les quatrièmes
|
||||
List<List<Long>> combinaisonsQuatriemes = genererCombinaisons(chevauxQuatriemes, 2);
|
||||
|
||||
for (List<Long> combinaisonQuatriemes : combinaisonsQuatriemes) {
|
||||
List<Long> combinaison = new ArrayList<>(chevauxPremiers);
|
||||
combinaison.addAll(combinaisonQuatriemes);
|
||||
|
||||
Map<String, Object> details = new HashMap<>();
|
||||
details.put("combinaison", combinaison);
|
||||
details.put("type", "QUINTE_PLUS");
|
||||
details.put("ordreExactPermutations", 12); // 12 permutations pour l'ordre exact
|
||||
details.put("ordreInexactPermutations", 108); // 108 permutations pour l'ordre inexact
|
||||
|
||||
combinaisonsPayables.add(details);
|
||||
}
|
||||
|
||||
return combinaisonsPayables;
|
||||
}
|
||||
|
||||
// Méthodes pour les autres cas de dead-heat (à implémenter selon l'article 3)
|
||||
private List<Map<String, Object>> traiterDeadHeatTroisPremiersUnQuatriemeUnCinquieme(Resultat resultat, List<Pari> paris) {
|
||||
// Article 3d: Implémentation à compléter
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> traiterDeadHeatDeuxPremiersTroisTroisiemes(Resultat resultat, List<Pari> paris) {
|
||||
// Article 3e: Implémentation à compléter
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> traiterDeadHeatDeuxPremiersDeuxTroisiemesUnCinquieme(Resultat resultat, List<Pari> paris) {
|
||||
// Article 3f: Implémentation à compléter
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> traiterDeadHeatDeuxPremiersUnTroisiemeDeuxQuatriemes(Resultat resultat, List<Pari> paris) {
|
||||
// Article 3g: Implémentation à compléter
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> traiterDeadHeatDeuxPremiersUnTroisiemeUnQuatriemeUnCinquieme(Resultat resultat, List<Pari> paris) {
|
||||
// Article 3h: Implémentation à compléter
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> traiterDeadHeatQuatreDeuxiemes(Resultat resultat, List<Pari> paris) {
|
||||
// Article 3i: Implémentation à compléter
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> traiterDeadHeatTroisDeuxiemesUnCinquieme(Resultat resultat, List<Pari> paris) {
|
||||
// Article 3j: Implémentation à compléter
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> traiterDeadHeatDeuxDeuxiemesDeuxQuatriemes(Resultat resultat, List<Pari> paris) {
|
||||
// Article 3k: Implémentation à compléter
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> traiterDeadHeatDeuxDeuxiemesUnQuatriemeUnCinquieme(Resultat resultat, List<Pari> paris) {
|
||||
// Article 3l: Implémentation à compléter
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> traiterDeadHeatTroisTroisiemes(Resultat resultat, List<Pari> paris) {
|
||||
// Article 3m: Implémentation à compléter
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> traiterDeadHeatDeuxTroisiemesUnCinquieme(Resultat resultat, List<Pari> paris) {
|
||||
// Article 3n: Implémentation à compléter
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> traiterDeadHeatDeuxQuatriemes(Resultat resultat, List<Pari> paris) {
|
||||
// Article 3o: Implémentation à compléter
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> traiterDeadHeatDeuxCinquiemes(Resultat resultat, List<Pari> paris) {
|
||||
// Article 3p: Implémentation à compléter
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// Méthodes utilitaires pour les dead-heats
|
||||
private List<Long> obtenirChevauxParPosition(Resultat resultat, int position) {
|
||||
// Implémentation simplifiée: retourne les chevaux à une position donnée
|
||||
// Dans une implémentation réelle, il faudrait gérer les dead-heats
|
||||
|
||||
List<Long> chevaux = new ArrayList<>();
|
||||
if (resultat.getIdsChevauxOrdreArrivee().size() >= position) {
|
||||
chevaux.add(resultat.getIdsChevauxOrdreArrivee().get(position - 1));
|
||||
}
|
||||
return chevaux;
|
||||
}
|
||||
|
||||
private List<List<Long>> genererCombinaisons(List<Long> elements, int k) {
|
||||
List<List<Long>> combinaisons = new ArrayList<>();
|
||||
genererCombinaisonsRecursif(elements, k, 0, new ArrayList<>(), combinaisons);
|
||||
return combinaisons;
|
||||
}
|
||||
|
||||
private void genererCombinaisonsRecursif(List<Long> elements, int k, int debut,
|
||||
List<Long> combinaisonCourante, List<List<Long>> combinaisons) {
|
||||
if (combinaisonCourante.size() == k) {
|
||||
combinaisons.add(new ArrayList<>(combinaisonCourante));
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = debut; i < elements.size(); i++) {
|
||||
combinaisonCourante.add(elements.get(i));
|
||||
genererCombinaisonsRecursif(elements, k, i + 1, combinaisonCourante, combinaisons);
|
||||
combinaisonCourante.remove(combinaisonCourante.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Méthodes pour gérer les différents types de paris (Article 7)
|
||||
public Map<String, Object> calculerCoutFormule(String typeFormule, int nombreChevauxBase, int nombreChevauxSelection) {
|
||||
Map<String, Object> resultat = new HashMap<>();
|
||||
resultat.put("typeFormule", typeFormule);
|
||||
resultat.put("nombreChevauxBase", nombreChevauxBase);
|
||||
resultat.put("nombreChevauxSelection", nombreChevauxSelection);
|
||||
|
||||
int nombreCombinaisons = 0;
|
||||
double cout = 0;
|
||||
|
||||
switch (typeFormule) {
|
||||
case "FORMULE_SIMPLIFIEE":
|
||||
nombreCombinaisons = calculerFormuleSimplifiee(nombreChevauxBase);
|
||||
cout = nombreCombinaisons * MISE_DE_BASE;
|
||||
break;
|
||||
|
||||
case "FORMULE_TOUS_ORDRES":
|
||||
nombreCombinaisons = calculerFormuleTousOrdres(nombreChevauxBase);
|
||||
cout = nombreCombinaisons * MISE_DE_BASE;
|
||||
break;
|
||||
|
||||
case "CHAMP_TOTAL_4":
|
||||
nombreCombinaisons = calculerChampTotal4(nombreChevauxBase, nombreChevauxSelection);
|
||||
cout = nombreCombinaisons * MISE_DE_BASE;
|
||||
break;
|
||||
|
||||
case "CHAMP_PARTIEL_4":
|
||||
nombreCombinaisons = calculerChampPartiel4(nombreChevauxBase, nombreChevauxSelection);
|
||||
cout = nombreCombinaisons * MISE_DE_BASE;
|
||||
break;
|
||||
|
||||
case "CHAMP_TOTAL_3":
|
||||
nombreCombinaisons = calculerChampTotal3(nombreChevauxBase, nombreChevauxSelection);
|
||||
cout = nombreCombinaisons * MISE_DE_BASE;
|
||||
break;
|
||||
|
||||
case "CHAMP_PARTIEL_3":
|
||||
nombreCombinaisons = calculerChampPartiel3(nombreChevauxBase, nombreChevauxSelection);
|
||||
cout = nombreCombinaisons * MISE_DE_BASE;
|
||||
break;
|
||||
|
||||
case "CHAMP_TOTAL_2":
|
||||
nombreCombinaisons = calculerChampTotal2(nombreChevauxBase, nombreChevauxSelection);
|
||||
cout = nombreCombinaisons * MISE_DE_BASE;
|
||||
break;
|
||||
|
||||
case "CHAMP_PARTIEL_2":
|
||||
nombreCombinaisons = calculerChampPartiel2(nombreChevauxBase, nombreChevauxSelection);
|
||||
cout = nombreCombinaisons * MISE_DE_BASE;
|
||||
break;
|
||||
|
||||
case "CHAMP_TOTAL_1":
|
||||
nombreCombinaisons = calculerChampTotal1(nombreChevauxBase, nombreChevauxSelection);
|
||||
cout = nombreCombinaisons * MISE_DE_BASE;
|
||||
break;
|
||||
|
||||
case "CHAMP_PARTIEL_1":
|
||||
nombreCombinaisons = calculerChampPartiel1(nombreChevauxBase, nombreChevauxSelection);
|
||||
cout = nombreCombinaisons * MISE_DE_BASE;
|
||||
break;
|
||||
|
||||
default:
|
||||
resultat.put("erreur", "Type de formule non reconnu");
|
||||
return resultat;
|
||||
}
|
||||
|
||||
resultat.put("nombreCombinaisons", nombreCombinaisons);
|
||||
resultat.put("coutTotal", cout);
|
||||
|
||||
return resultat;
|
||||
}
|
||||
|
||||
// Implémentation des calculs de formules selon l'article 7
|
||||
private int calculerFormuleSimplifiee(int k) {
|
||||
// Formule simplifiée: K x (K-1) x (K-2) x (K-3) x (K-4) / 120
|
||||
if (k < 5) return 0;
|
||||
return (k * (k-1) * (k-2) * (k-3) * (k-4)) / 120;
|
||||
}
|
||||
|
||||
private int calculerFormuleTousOrdres(int k) {
|
||||
// Formule tous ordres: K x (K-1) x (K-2) x (K-3) x (K-4)
|
||||
if (k < 5) return 0;
|
||||
return k * (k-1) * (k-2) * (k-3) * (k-4);
|
||||
}
|
||||
|
||||
private int calculerChampTotal4(int n, int k) {
|
||||
// Champ total de 4 chevaux: 120 x (N-4) paris en formule tous ordres
|
||||
// ou (N-4) paris en formule simplifiée
|
||||
// Ici, nous calculons la formule tous ordres
|
||||
if (n < 5) return 0;
|
||||
return 120 * (n - 4);
|
||||
}
|
||||
|
||||
private int calculerChampPartiel4(int p, int k) {
|
||||
// Champ partiel de 4 chevaux: 120 x P paris en formule tous ordres
|
||||
// ou P paris en formule simplifiée
|
||||
// Ici, nous calculons la formule tous ordres
|
||||
return 120 * p;
|
||||
}
|
||||
|
||||
private int calculerChampTotal3(int n, int k) {
|
||||
// Champ total de 3 chevaux: 60 x (N-3) x (N-4) paris en formule tous ordres
|
||||
if (n < 5) return 0;
|
||||
return 60 * (n-3) * (n-4);
|
||||
}
|
||||
|
||||
private int calculerChampPartiel3(int p, int k) {
|
||||
// Champ partiel de 3 chevaux: 60 x P x (P-1) paris en formule tous ordres
|
||||
if (p < 2) return 0;
|
||||
return 60 * p * (p-1);
|
||||
}
|
||||
|
||||
private int calculerChampTotal2(int n, int k) {
|
||||
// Champ total de 2 chevaux: 20 x (N-2) x (N-3) x (N-4) paris en formule tous ordres
|
||||
if (n < 5) return 0;
|
||||
return 20 * (n-2) * (n-3) * (n-4);
|
||||
}
|
||||
|
||||
private int calculerChampPartiel2(int p, int k) {
|
||||
// Champ partiel de 2 chevaux: 20 x P x (P-1) x (P-2) paris en formule tous ordres
|
||||
if (p < 3) return 0;
|
||||
return 20 * p * (p-1) * (p-2);
|
||||
}
|
||||
|
||||
private int calculerChampTotal1(int n, int k) {
|
||||
// Champ total d'1 cheval: 5 x (N-1) x (N-2) x (N-3) x (N-4) paris en formule tous ordres
|
||||
if (n < 5) return 0;
|
||||
return 5 * (n-1) * (n-2) * (n-3) * (n-4);
|
||||
}
|
||||
|
||||
private int calculerChampPartiel1(int p, int k) {
|
||||
// Champ partiel d'1 cheval: 5 x P x (P-1) x (P-2) x (P-3) paris en formule tous ordres
|
||||
if (p < 4) return 0;
|
||||
return 5 * p * (p-1) * (p-2) * (p-3);
|
||||
}
|
||||
|
||||
// Méthodes pour gérer les non-partants (Article 4)
|
||||
public Map<String, Object> traiterNonPartants(List<Pari> paris, Resultat resultat) {
|
||||
Map<String, Object> resultats = new HashMap<>();
|
||||
List<Pari> parisARembourser = new ArrayList<>();
|
||||
List<Pari> parisBonus4 = new ArrayList<>();
|
||||
|
||||
for (Pari pari : paris) {
|
||||
int nombreNonPartants = compterNonPartants(pari);
|
||||
|
||||
if (nombreNonPartants >= 2) {
|
||||
// Article 4a: Remboursement si 2 chevaux ou plus non partants
|
||||
parisARembourser.add(pari);
|
||||
} else if (nombreNonPartants == 1) {
|
||||
// Article 4b: Bonus 4 si un seul non-partant et les 4 autres sont dans les 4 premiers
|
||||
if (sontQuatrePremiers(pari, resultat)) {
|
||||
parisBonus4.add(pari);
|
||||
} else {
|
||||
parisARembourser.add(pari);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resultats.put("parisARembourser", parisARembourser);
|
||||
resultats.put("parisBonus4", parisBonus4);
|
||||
resultats.put("montantRemboursements", calculerMontantRemboursements(parisARembourser));
|
||||
|
||||
return resultats;
|
||||
}
|
||||
|
||||
private int compterNonPartants(Pari pari) {
|
||||
int compte = 0;
|
||||
List<Cheval> chevaux = repositoryCheval.findAllById(pari.getChevauxSelectionnes());
|
||||
for (Cheval cheval : chevaux) {
|
||||
if (cheval.isEstNonPartant()) {
|
||||
compte++;
|
||||
}
|
||||
}
|
||||
return compte;
|
||||
}
|
||||
|
||||
private boolean sontQuatrePremiers(Pari pari, Resultat resultat) {
|
||||
// Vérifier si au moins 4 chevaux du pari sont dans les 4 premiers
|
||||
List<Long> quatrePremiers = resultat.getIdsChevauxOrdreArrivee().subList(0, Math.min(4, resultat.getIdsChevauxOrdreArrivee().size()));
|
||||
List<Long> chevauxPari = pari.getChevauxSelectionnes();
|
||||
|
||||
long compte = chevauxPari.stream()
|
||||
.filter(quatrePremiers::contains)
|
||||
.count();
|
||||
|
||||
return compte >= 4;
|
||||
}
|
||||
|
||||
private double calculerMontantRemboursements(List<Pari> paris) {
|
||||
return paris.stream()
|
||||
.mapToDouble(Pari::getMise)
|
||||
.sum();
|
||||
}
|
||||
|
||||
// Méthode pour déterminer les gagnants d'une course
|
||||
public Map<String, Object> determinerGagnants(Long courseId, Resultat resultat) {
|
||||
Map<String, Object> resultats = new HashMap<>();
|
||||
List<Pari> tousParis = repositoryPari.findByCourseId(courseId);
|
||||
|
||||
List<Pari> gagnantsOrdreExact = new ArrayList<>();
|
||||
List<Pari> gagnantsOrdreInexact = new ArrayList<>();
|
||||
List<Pari> gagnantsBonus4 = new ArrayList<>();
|
||||
|
||||
for (Pari pari : tousParis) {
|
||||
if (estGagnantOrdreExact(pari, resultat)) {
|
||||
gagnantsOrdreExact.add(pari);
|
||||
} else if (estGagnantOrdreInexact(pari, resultat)) {
|
||||
gagnantsOrdreInexact.add(pari);
|
||||
} else if (estGagnantBonus4(pari, resultat)) {
|
||||
gagnantsBonus4.add(pari);
|
||||
}
|
||||
}
|
||||
|
||||
resultats.put("gagnantsOrdreExact", gagnantsOrdreExact);
|
||||
resultats.put("gagnantsOrdreInexact", gagnantsOrdreInexact);
|
||||
resultats.put("gagnantsBonus4", gagnantsBonus4);
|
||||
|
||||
return resultats;
|
||||
}
|
||||
|
||||
private boolean estGagnantOrdreExact(Pari pari, Resultat resultat) {
|
||||
// Vérifier si l'ordre prédit correspond exactement à l'ordre d'arrivée
|
||||
return pari.getOrdrePredit().equals(resultat.getIdsChevauxOrdreArrivee());
|
||||
}
|
||||
|
||||
private boolean estGagnantOrdreInexact(Pari pari, Resultat resultat) {
|
||||
// Vérifier si les 5 chevaux sont dans les 5 premiers mais pas dans le bon ordre
|
||||
List<Long> cinqPremiers = resultat.getIdsChevauxOrdreArrivee().subList(0, Math.min(5, resultat.getIdsChevauxOrdreArrivee().size()));
|
||||
List<Long> chevauxPari = pari.getChevauxSelectionnes();
|
||||
|
||||
return cinqPremiers.containsAll(chevauxPari) &&
|
||||
!pari.getOrdrePredit().equals(resultat.getIdsChevauxOrdreArrivee());
|
||||
}
|
||||
|
||||
private boolean estGagnantBonus4(Pari pari, Resultat resultat) {
|
||||
// Vérifier si au moins 4 chevaux du pari sont dans les 4 premiers
|
||||
List<Long> quatrePremiers = resultat.getIdsChevauxOrdreArrivee().subList(0, Math.min(4, resultat.getIdsChevauxOrdreArrivee().size()));
|
||||
List<Long> chevauxPari = pari.getChevauxSelectionnes();
|
||||
|
||||
long compte = chevauxPari.stream()
|
||||
.filter(quatrePremiers::contains)
|
||||
.count();
|
||||
|
||||
return compte >= 4;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
// CourseController.java
|
||||
package com.pmumali.jumeleordre.controller;
|
||||
package com.pmumali.ch4_jumeleordre.controller;
|
||||
|
||||
import com.pmumali.jumeleordre.dto.ResultatCourseDto;
|
||||
import com.pmumali.jumeleordre.exception.ResultatCourseInvalideException;
|
||||
import com.pmumali.jumeleordre.model.Course;
|
||||
import com.pmumali.jumeleordre.service.ResultatCourseService;
|
||||
import com.pmumali.jumeleordre.service.CourseService;
|
||||
import com.pmumali.ch4_jumeleordre.dto.ResultatCourseDto;
|
||||
import com.pmumali.ch4_jumeleordre.exception.ResultatCourseInvalideException;
|
||||
import com.pmumali.ch4_jumeleordre.model.Course;
|
||||
import com.pmumali.ch4_jumeleordre.service.ResultatCourseService;
|
||||
import com.pmumali.ch4_jumeleordre.service.CourseService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
@@ -1,11 +1,11 @@
|
||||
// ParisController.java
|
||||
package com.pmumali.jumeleordre.controller;
|
||||
package com.pmumali.ch4_jumeleordre.controller;
|
||||
|
||||
import com.pmumali.jumeleordre.dto.ParisDto;
|
||||
import com.pmumali.jumeleordre.dto.GainsDto;
|
||||
import com.pmumali.jumeleordre.exception.ParisInvalideException;
|
||||
import com.pmumali.jumeleordre.model.Paris;
|
||||
import com.pmumali.jumeleordre.service.ParisService;
|
||||
import com.pmumali.ch4_jumeleordre.dto.ParisDto;
|
||||
import com.pmumali.ch4_jumeleordre.dto.GainsDto;
|
||||
import com.pmumali.ch4_jumeleordre.exception.ParisInvalideException;
|
||||
import com.pmumali.ch4_jumeleordre.model.Paris;
|
||||
import com.pmumali.ch4_jumeleordre.service.ParisService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
@@ -1,5 +1,5 @@
|
||||
// GainsDto.java
|
||||
package com.pmumali.jumeleordre.dto;
|
||||
package com.pmumali.ch4_jumeleordre.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// ParisDto.java
|
||||
package com.pmumali.jumeleordre.dto;
|
||||
package com.pmumali.ch4_jumeleordre.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// ResultatCourseDto.java
|
||||
package com.pmumali.jumeleordre.dto;
|
||||
package com.pmumali.ch4_jumeleordre.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// ChevalInvalideException.java
|
||||
package com.pmumali.jumeleordre.exception;
|
||||
package com.pmumali.ch4_jumeleordre.exception;
|
||||
|
||||
public class ChevalInvalideException extends JumeleOrdreException {
|
||||
public ChevalInvalideException(String message) {
|
||||
@@ -1,5 +1,5 @@
|
||||
// CourseDejaTermineeException.java
|
||||
package com.pmumali.jumeleordre.exception;
|
||||
package com.pmumali.ch4_jumeleordre.exception;
|
||||
|
||||
public class CourseDejaTermineeException extends CourseInvalideException {
|
||||
public CourseDejaTermineeException(Long idCourse) {
|
||||
@@ -1,5 +1,5 @@
|
||||
// CourseInvalideException.java
|
||||
package com.pmumali.jumeleordre.exception;
|
||||
package com.pmumali.ch4_jumeleordre.exception;
|
||||
|
||||
public class CourseInvalideException extends JumeleOrdreException {
|
||||
public CourseInvalideException(String message) {
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.pmumali.jumeleordre.exception;
|
||||
package com.pmumali.ch4_jumeleordre.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.pmumali.jumeleordre.exception;
|
||||
package com.pmumali.ch4_jumeleordre.exception;
|
||||
|
||||
public class JumeleOrdreException extends RuntimeException {
|
||||
public JumeleOrdreException(String message) {
|
||||
@@ -1,5 +1,5 @@
|
||||
// LimiteMiseDepasseeException.java
|
||||
package com.pmumali.jumeleordre.exception;
|
||||
package com.pmumali.ch4_jumeleordre.exception;
|
||||
|
||||
public class LimiteMiseDepasseeException extends ParisInvalideException {
|
||||
private final double miseActuelle;
|
||||
@@ -1,5 +1,5 @@
|
||||
// NombreChevauxInvalideException.java
|
||||
package com.pmumali.jumeleordre.exception;
|
||||
package com.pmumali.ch4_jumeleordre.exception;
|
||||
|
||||
public class NombreChevauxInvalideException extends CourseInvalideException {
|
||||
private final int nombreChevaux;
|
||||
@@ -1,5 +1,5 @@
|
||||
// PaiementInvalideException.java
|
||||
package com.pmumali.jumeleordre.exception;
|
||||
package com.pmumali.ch4_jumeleordre.exception;
|
||||
|
||||
public class PaiementInvalideException extends JumeleOrdreException {
|
||||
public PaiementInvalideException(String message) {
|
||||
@@ -1,5 +1,5 @@
|
||||
// ParisInvalideException.java
|
||||
package com.pmumali.jumeleordre.exception;
|
||||
package com.pmumali.ch4_jumeleordre.exception;
|
||||
|
||||
public class ParisInvalideException extends JumeleOrdreException {
|
||||
public ParisInvalideException(String message) {
|
||||
@@ -1,5 +1,5 @@
|
||||
// ResultatCourseInvalideException.java
|
||||
package com.pmumali.jumeleordre.exception;
|
||||
package com.pmumali.ch4_jumeleordre.exception;
|
||||
|
||||
public class ResultatCourseInvalideException extends JumeleOrdreException {
|
||||
public ResultatCourseInvalideException(String message) {
|
||||
@@ -1,5 +1,5 @@
|
||||
// ValidationException.java
|
||||
package com.pmumali.jumeleordre.exception;
|
||||
package com.pmumali.ch4_jumeleordre.exception;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.pmumali.jumeleordre.model;
|
||||
package com.pmumali.ch4_jumeleordre.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
@@ -1,5 +1,5 @@
|
||||
// Course.java
|
||||
package com.pmumali.jumeleordre.model;
|
||||
package com.pmumali.ch4_jumeleordre.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
@@ -1,5 +1,5 @@
|
||||
// Paris.java
|
||||
package com.pmumali.jumeleordre.model;
|
||||
package com.pmumali.ch4_jumeleordre.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
@@ -1,5 +1,5 @@
|
||||
// ResultatCourse.java
|
||||
package com.pmumali.jumeleordre.model;
|
||||
package com.pmumali.ch4_jumeleordre.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
@@ -1,7 +1,7 @@
|
||||
// ChevalRepository.java
|
||||
package com.pmumali.jumeleordre.repository;
|
||||
package com.pmumali.ch4_jumeleordre.repository;
|
||||
|
||||
import com.pmumali.jumeleordre.model.Cheval;
|
||||
import com.pmumali.ch4_jumeleordre.model.Cheval;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface ChevalRepository extends JpaRepository<Cheval, Long> {
|
||||
@@ -1,7 +1,7 @@
|
||||
// CourseRepository.java
|
||||
package com.pmumali.jumeleordre.repository;
|
||||
package com.pmumali.ch4_jumeleordre.repository;
|
||||
|
||||
import com.pmumali.jumeleordre.model.Course;
|
||||
import com.pmumali.ch4_jumeleordre.model.Course;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
@@ -1,7 +1,7 @@
|
||||
// ParisRepository.java
|
||||
package com.pmumali.jumeleordre.repository;
|
||||
package com.pmumali.ch4_jumeleordre.repository;
|
||||
|
||||
import com.pmumali.jumeleordre.model.Paris;
|
||||
import com.pmumali.ch4_jumeleordre.model.Paris;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
@@ -1,7 +1,7 @@
|
||||
// ResultatCourseRepository.java
|
||||
package com.pmumali.jumeleordre.repository;
|
||||
package com.pmumali.ch4_jumeleordre.repository;
|
||||
|
||||
import com.pmumali.jumeleordre.model.ResultatCourse;
|
||||
import com.pmumali.ch4_jumeleordre.model.ResultatCourse;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface ResultatCourseRepository extends JpaRepository<ResultatCourse, Long> {
|
||||
@@ -1,16 +1,15 @@
|
||||
package com.pmumali.jumeleordre.service;
|
||||
package com.pmumali.ch4_jumeleordre.service;
|
||||
|
||||
import com.pmumali.jumeleordre.model.Course;
|
||||
import com.pmumali.jumeleordre.model.Cheval;
|
||||
import com.pmumali.jumeleordre.repository.CourseRepository;
|
||||
import com.pmumali.jumeleordre.repository.ChevalRepository;
|
||||
import com.pmumali.jumeleordre.exception.CourseInvalideException;
|
||||
import com.pmumali.ch4_jumeleordre.model.Course;
|
||||
import com.pmumali.ch4_jumeleordre.model.Cheval;
|
||||
import com.pmumali.ch4_jumeleordre.repository.CourseRepository;
|
||||
import com.pmumali.ch4_jumeleordre.repository.ChevalRepository;
|
||||
import com.pmumali.ch4_jumeleordre.exception.CourseInvalideException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class CourseService {
|
||||
@@ -1,14 +1,14 @@
|
||||
// ParisService.java
|
||||
package com.pmumali.jumeleordre.service;
|
||||
package com.pmumali.ch4_jumeleordre.service;
|
||||
|
||||
import com.pmumali.jumeleordre.dto.ParisDto;
|
||||
import com.pmumali.jumeleordre.exception.ParisInvalideException;
|
||||
import com.pmumali.jumeleordre.model.Cheval;
|
||||
import com.pmumali.jumeleordre.model.Course;
|
||||
import com.pmumali.jumeleordre.model.Paris;
|
||||
import com.pmumali.jumeleordre.repository.ChevalRepository;
|
||||
import com.pmumali.jumeleordre.repository.CourseRepository;
|
||||
import com.pmumali.jumeleordre.repository.ParisRepository;
|
||||
import com.pmumali.ch4_jumeleordre.dto.ParisDto;
|
||||
import com.pmumali.ch4_jumeleordre.exception.ParisInvalideException;
|
||||
import com.pmumali.ch4_jumeleordre.model.Cheval;
|
||||
import com.pmumali.ch4_jumeleordre.model.Course;
|
||||
import com.pmumali.ch4_jumeleordre.model.Paris;
|
||||
import com.pmumali.ch4_jumeleordre.repository.ChevalRepository;
|
||||
import com.pmumali.ch4_jumeleordre.repository.CourseRepository;
|
||||
import com.pmumali.ch4_jumeleordre.repository.ParisRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -1,16 +1,15 @@
|
||||
// ResultatCourseService.java
|
||||
package com.pmumali.jumeleordre.service;
|
||||
package com.pmumali.ch4_jumeleordre.service;
|
||||
|
||||
import com.pmumali.jumeleordre.dto.ResultatCourseDto;
|
||||
import com.pmumali.jumeleordre.exception.ResultatCourseInvalideException;
|
||||
import com.pmumali.jumeleordre.model.*;
|
||||
import com.pmumali.jumeleordre.repository.*;
|
||||
import com.pmumali.ch4_jumeleordre.dto.ResultatCourseDto;
|
||||
import com.pmumali.ch4_jumeleordre.exception.ResultatCourseInvalideException;
|
||||
import com.pmumali.ch4_jumeleordre.model.*;
|
||||
import com.pmumali.ch4_jumeleordre.repository.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class ResultatCourseService {
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.pmumali.trio.controller;
|
||||
package com.pmumali.ch5_trio.controller;
|
||||
|
||||
import com.pmumali.trio.dto.ParisTrioDto;
|
||||
import com.pmumali.trio.model.ParisTrio;
|
||||
import com.pmumali.trio.service.ParisTrioService;
|
||||
import com.pmumali.ch5_trio.dto.ParisTrioDto;
|
||||
import com.pmumali.ch5_trio.model.ParisTrio;
|
||||
import com.pmumali.ch5_trio.service.ParisTrioService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.pmumali.trio.controller;
|
||||
package com.pmumali.ch5_trio.controller;
|
||||
|
||||
import com.pmumali.trio.dto.ResultatCourseDto;
|
||||
import com.pmumali.trio.service.ResultatCourseService;
|
||||
import com.pmumali.ch5_trio.dto.ResultatCourseDto;
|
||||
import com.pmumali.ch5_trio.service.ResultatCourseService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.pmumali.trio.dto;
|
||||
package com.pmumali.ch5_trio.dto;
|
||||
|
||||
import com.pmumali.trio.model.ParisTrio.TypeFormule;
|
||||
import com.pmumali.ch5_trio.model.ParisTrio.TypeFormule;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.pmumali.trio.dto;
|
||||
package com.pmumali.ch5_trio.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.pmumali.trio.exception;
|
||||
package com.pmumali.ch5_trio.exception;
|
||||
|
||||
public class ChevalNonPartantException extends ParisTrioInvalideException {
|
||||
public ChevalNonPartantException(String nomCheval) {
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.pmumali.trio.exception;
|
||||
package com.pmumali.ch5_trio.exception;
|
||||
|
||||
public class CourseDejaTermineeException extends ParisTrioInvalideException {
|
||||
public CourseDejaTermineeException(Long idCourse) {
|
||||
@@ -1,7 +1,7 @@
|
||||
// CourseInvalideException.java
|
||||
package com.pmumali.trio.exception;
|
||||
package com.pmumali.ch5_trio.exception;
|
||||
|
||||
import com.pmumali.jumeleordre.exception.JumeleOrdreException;
|
||||
import com.pmumali.ch4_jumeleordre.exception.JumeleOrdreException;
|
||||
|
||||
public class CourseInvalideException extends JumeleOrdreException {
|
||||
public CourseInvalideException(String message) {
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.pmumali.trio.exception;
|
||||
package com.pmumali.ch5_trio.exception;
|
||||
|
||||
public class LimiteMiseDepasseeException extends ParisTrioInvalideException {
|
||||
private final double miseActuelle;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user