Fin intégration jumele gagnant
This commit is contained in:
@@ -0,0 +1,33 @@
|
|||||||
|
package com.pmumali.ch1_simple.controller;
|
||||||
|
|
||||||
|
import com.pmumali.ch1_simple.model.Cheval;
|
||||||
|
import com.pmumali.ch1_simple.service.ChevalService;
|
||||||
|
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/chevaux")
|
||||||
|
public class ChevalController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ChevalService chevalService;
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public ResponseEntity<Cheval> ajouterCheval(@RequestBody Cheval cheval) {
|
||||||
|
return ResponseEntity.ok(chevalService.ajouterCheval(cheval));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/course/{courseId}")
|
||||||
|
public ResponseEntity<List<Cheval>> obtenirChevauxParCourse(@PathVariable Long courseId) {
|
||||||
|
return ResponseEntity.ok(chevalService.obtenirChevauxParCourse(courseId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/ecurie/{nomEcurie}")
|
||||||
|
public ResponseEntity<List<Cheval>> obtenirChevauxParEcurie(@PathVariable String nomEcurie) {
|
||||||
|
return ResponseEntity.ok(chevalService.obtenirChevauxParEcurie(nomEcurie));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.pmumali.ch1_simple.controller;
|
||||||
|
|
||||||
|
import com.pmumali.ch1_simple.model.Course;
|
||||||
|
import com.pmumali.ch1_simple.service.CourseService;
|
||||||
|
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 CourseController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CourseService courseService;
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public ResponseEntity<Course> creerCourse(@RequestBody Course course) {
|
||||||
|
return ResponseEntity.ok(courseService.creerCourse(course));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public ResponseEntity<List<Course>> obtenirToutesCourses() {
|
||||||
|
return ResponseEntity.ok(courseService.obtenirToutesCourses());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public ResponseEntity<Course> obtenirCourseParId(@PathVariable Long id) {
|
||||||
|
Course course = courseService.obtenirCourseParId(id);
|
||||||
|
return course != null ? ResponseEntity.ok(course) : ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/terminees")
|
||||||
|
public ResponseEntity<List<Course>> obtenirCoursesTerminees() {
|
||||||
|
return ResponseEntity.ok(courseService.obtenirCoursesTerminees());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/avenir")
|
||||||
|
public ResponseEntity<List<Course>> obtenirCoursesAVenir() {
|
||||||
|
return ResponseEntity.ok(courseService.obtenirCoursesAVenir());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.pmumali.ch1_simple.controller;
|
||||||
|
|
||||||
|
import com.pmumali.ch1_simple.model.Gains;
|
||||||
|
import com.pmumali.ch1_simple.model.ResultatCourse;
|
||||||
|
import com.pmumali.ch1_simple.service.GainsService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/gains")
|
||||||
|
public class GainsController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private GainsService gainsService;
|
||||||
|
|
||||||
|
@PostMapping("/calculer/{courseId}")
|
||||||
|
public ResponseEntity<Gains> calculerGains(@PathVariable Long courseId, @RequestBody ResultatCourse resultat) {
|
||||||
|
return ResponseEntity.ok(gainsService.calculerGains(courseId, resultat));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/course/{courseId}")
|
||||||
|
public ResponseEntity<Gains> obtenirGainsParCourse(@PathVariable Long courseId) {
|
||||||
|
Gains gains = gainsService.obtenirGainsParCourse(courseId);
|
||||||
|
return gains != null ? ResponseEntity.ok(gains) : ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package com.pmumali.ch1_simple.controller;
|
||||||
|
|
||||||
|
import com.pmumali.ch1_simple.model.PariSimple;
|
||||||
|
import com.pmumali.ch1_simple.service.PariSimpleService;
|
||||||
|
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 PariController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PariSimpleService pariService;
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public ResponseEntity<PariSimple> placerPari(@RequestBody PariSimple pari) {
|
||||||
|
return ResponseEntity.ok(pariService.placerPari(pari));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/course/{courseId}")
|
||||||
|
public ResponseEntity<List<PariSimple>> obtenirParisParCourse(@PathVariable Long courseId) {
|
||||||
|
return ResponseEntity.ok(pariService.obtenirParisParCourse(courseId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/course/{courseId}/type/{typePari}")
|
||||||
|
public ResponseEntity<List<PariSimple>> obtenirParisParCourseEtType(
|
||||||
|
@PathVariable Long courseId, @PathVariable String typePari) {
|
||||||
|
return ResponseEntity.ok(pariService.obtenirParisParCourseEtType(courseId, typePari));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/cheval/{chevalId}")
|
||||||
|
public ResponseEntity<List<PariSimple>> obtenirParisParCheval(@PathVariable Long chevalId) {
|
||||||
|
return ResponseEntity.ok(pariService.obtenirParisParCheval(chevalId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
13
src/main/java/com/pmumali/ch1_simple/dto/ChevalDto.java
Normal file
13
src/main/java/com/pmumali/ch1_simple/dto/ChevalDto.java
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package com.pmumali.ch1_simple.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ChevalDto {
|
||||||
|
private Long id;
|
||||||
|
private String nom;
|
||||||
|
private int numero;
|
||||||
|
private boolean estNonPartant;
|
||||||
|
private String nomEcurie;
|
||||||
|
private Long courseId;
|
||||||
|
}
|
||||||
16
src/main/java/com/pmumali/ch1_simple/dto/CourseDto.java
Normal file
16
src/main/java/com/pmumali/ch1_simple/dto/CourseDto.java
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package com.pmumali.ch1_simple.dto;
|
||||||
|
|
||||||
|
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 int nombreChevauxInscrits;
|
||||||
|
private boolean estTerminee;
|
||||||
|
private List<Long> chevauxIds;
|
||||||
|
}
|
||||||
17
src/main/java/com/pmumali/ch1_simple/dto/GainsDto.java
Normal file
17
src/main/java/com/pmumali/ch1_simple/dto/GainsDto.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package com.pmumali.ch1_simple.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class GainsDto {
|
||||||
|
private Long id;
|
||||||
|
private Long courseId;
|
||||||
|
private double masseGagnant;
|
||||||
|
private double massePlace;
|
||||||
|
private double rapportGagnant;
|
||||||
|
private double rapportPlace;
|
||||||
|
private double montantCagnotte;
|
||||||
|
private LocalDateTime dateCalcul;
|
||||||
|
}
|
||||||
16
src/main/java/com/pmumali/ch1_simple/dto/PariSimpleDto.java
Normal file
16
src/main/java/com/pmumali/ch1_simple/dto/PariSimpleDto.java
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package com.pmumali.ch1_simple.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class PariSimpleDto {
|
||||||
|
private Long id;
|
||||||
|
private String typePari;
|
||||||
|
private double mise;
|
||||||
|
private LocalDateTime datePari;
|
||||||
|
private Long chevalId;
|
||||||
|
private Long courseId;
|
||||||
|
private boolean estPaye;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.pmumali.ch1_simple.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ResultatCourseDto {
|
||||||
|
private Long id;
|
||||||
|
private Long courseId;
|
||||||
|
private List<Long> chevauxPremiers;
|
||||||
|
private List<Long> chevauxDeuxiemes;
|
||||||
|
private List<Long> chevauxTroisiemes;
|
||||||
|
private boolean aDeadHeat;
|
||||||
|
}
|
||||||
25
src/main/java/com/pmumali/ch1_simple/model/Cheval.java
Normal file
25
src/main/java/com/pmumali/ch1_simple/model/Cheval.java
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package com.pmumali.ch1_simple.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;
|
||||||
|
private String nomEcurie;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "course_id")
|
||||||
|
private Course course;
|
||||||
|
}
|
||||||
|
|
||||||
32
src/main/java/com/pmumali/ch1_simple/model/Course.java
Normal file
32
src/main/java/com/pmumali/ch1_simple/model/Course.java
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package com.pmumali.ch1_simple.model;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
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 int nombreChevauxInscrits;
|
||||||
|
private boolean estTerminee;
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "course", cascade = CascadeType.ALL)
|
||||||
|
private List<Cheval> chevaux;
|
||||||
|
|
||||||
|
@OneToOne(mappedBy = "course", cascade = CascadeType.ALL)
|
||||||
|
private ResultatCourse resultat;
|
||||||
|
}
|
||||||
31
src/main/java/com/pmumali/ch1_simple/model/Gains.java
Normal file
31
src/main/java/com/pmumali/ch1_simple/model/Gains.java
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package com.pmumali.ch1_simple.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 masseGagnant;
|
||||||
|
private double massePlace;
|
||||||
|
|
||||||
|
private double rapportGagnant;
|
||||||
|
private double rapportPlace;
|
||||||
|
|
||||||
|
private double montantCagnotte;
|
||||||
|
|
||||||
|
private LocalDateTime dateCalcul;
|
||||||
|
}
|
||||||
31
src/main/java/com/pmumali/ch1_simple/model/PariSimple.java
Normal file
31
src/main/java/com/pmumali/ch1_simple/model/PariSimple.java
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package com.pmumali.ch1_simple.model;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Table(name = "pari_simple")
|
||||||
|
public class PariSimple {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String typePari; // GAGNANT ou PLACE
|
||||||
|
private double mise = 500.0;
|
||||||
|
private LocalDateTime datePari;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "cheval_id")
|
||||||
|
private Cheval cheval;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "course_id")
|
||||||
|
private Course course;
|
||||||
|
|
||||||
|
private boolean estPaye;
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.pmumali.ch1_simple.model;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Table(name = "resultat_course")
|
||||||
|
public class ResultatCourse {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@OneToOne
|
||||||
|
@JoinColumn(name = "course_id")
|
||||||
|
private Course course;
|
||||||
|
|
||||||
|
@ElementCollection
|
||||||
|
private List<Long> chevauxPremiers; // Pour gérer les dead-heats
|
||||||
|
|
||||||
|
@ElementCollection
|
||||||
|
private List<Long> chevauxDeuxiemes; // Pour gérer les dead-heats
|
||||||
|
|
||||||
|
@ElementCollection
|
||||||
|
private List<Long> chevauxTroisiemes; // Pour gérer les dead-heats
|
||||||
|
|
||||||
|
private boolean aDeadHeat;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.pmumali.ch1_simple.repository;
|
||||||
|
|
||||||
|
import com.pmumali.ch1_simple.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);
|
||||||
|
List<Cheval> findByNomEcurie(String nomEcurie);
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.pmumali.ch1_simple.repository;
|
||||||
|
|
||||||
|
import com.pmumali.ch1_simple.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.ch1_simple.repository;
|
||||||
|
|
||||||
|
import com.pmumali.ch1_simple.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,15 @@
|
|||||||
|
package com.pmumali.ch1_simple.repository;
|
||||||
|
|
||||||
|
import com.pmumali.ch1_simple.model.PariSimple;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface PariSimpleRepository extends JpaRepository<PariSimple, Long> {
|
||||||
|
List<PariSimple> findByCourseId(Long courseId);
|
||||||
|
List<PariSimple> findByCourseIdAndTypePari(Long courseId, String typePari);
|
||||||
|
List<PariSimple> findByChevalId(Long chevalId);
|
||||||
|
List<PariSimple> findByChevalIdAndTypePari(Long chevalId, String typePari);
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.pmumali.ch1_simple.repository;
|
||||||
|
|
||||||
|
import com.pmumali.ch1_simple.model.ResultatCourse;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface ResultatCourseRepository extends JpaRepository<ResultatCourse, Long> {
|
||||||
|
ResultatCourse findByCourseId(Long courseId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.pmumali.ch1_simple.service;
|
||||||
|
|
||||||
|
import com.pmumali.ch1_simple.model.Cheval;
|
||||||
|
import com.pmumali.ch1_simple.repository.ChevalRepository;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ChevalService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ChevalRepository chevalRepository;
|
||||||
|
|
||||||
|
public Cheval ajouterCheval(Cheval cheval) {
|
||||||
|
return chevalRepository.save(cheval);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Cheval> obtenirChevauxParCourse(Long courseId) {
|
||||||
|
return chevalRepository.findByCourseId(courseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Cheval> obtenirChevauxParEcurie(String nomEcurie) {
|
||||||
|
return chevalRepository.findByNomEcurie(nomEcurie);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.pmumali.ch1_simple.service;
|
||||||
|
|
||||||
|
|
||||||
|
import com.pmumali.ch1_simple.model.Course;
|
||||||
|
import com.pmumali.ch1_simple.repository.CourseRepository;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class CourseService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CourseRepository courseRepository;
|
||||||
|
|
||||||
|
public Course creerCourse(Course course) {
|
||||||
|
return courseRepository.save(course);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Course> obtenirToutesCourses() {
|
||||||
|
return courseRepository.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Course obtenirCourseParId(Long id) {
|
||||||
|
return courseRepository.findById(id).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Course> obtenirCoursesTerminees() {
|
||||||
|
return courseRepository.findByEstTerminee(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Course> obtenirCoursesAVenir() {
|
||||||
|
return courseRepository.findByEstTerminee(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
479
src/main/java/com/pmumali/ch1_simple/service/GainsService.java
Normal file
479
src/main/java/com/pmumali/ch1_simple/service/GainsService.java
Normal file
@@ -0,0 +1,479 @@
|
|||||||
|
package com.pmumali.ch1_simple.service;
|
||||||
|
|
||||||
|
import com.pmumali.ch1_simple.model.*;
|
||||||
|
import com.pmumali.ch1_simple.repository.*;
|
||||||
|
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.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Transactional
|
||||||
|
public class GainsService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PariSimpleRepository pariRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private GainsRepository gainsRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CourseRepository courseRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ChevalRepository chevalRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ResultatCourseRepository resultatRepository;
|
||||||
|
|
||||||
|
private static final double RAPPORT_MINIMUM = 1.1;
|
||||||
|
private static final double PRELEVEMENTS = 0.15; // 15% de prélèvements
|
||||||
|
|
||||||
|
public Gains calculerGains(Long courseId, ResultatCourse resultat) {
|
||||||
|
Course course = courseRepository.findById(courseId)
|
||||||
|
.orElseThrow(() -> new RuntimeException("Course non trouvée"));
|
||||||
|
|
||||||
|
List<PariSimple> tousParis = pariRepository.findByCourseId(courseId);
|
||||||
|
|
||||||
|
// Calcul de la recette nette (Article 5)
|
||||||
|
double totalMises = tousParis.stream().mapToDouble(PariSimple::getMise).sum();
|
||||||
|
double recetteNette = totalMises;
|
||||||
|
|
||||||
|
// Calcul des remboursements (Article 4)
|
||||||
|
double montantRemboursements = calculerRemboursements(tousParis);
|
||||||
|
|
||||||
|
// Masse à partager (Article 5)
|
||||||
|
double masseAPartager = recetteNette - montantRemboursements - (recetteNette * PRELEVEMENTS);
|
||||||
|
|
||||||
|
// Séparation des paris GAGNANT et PLACE
|
||||||
|
List<PariSimple> parisGagnant = tousParis.stream()
|
||||||
|
.filter(p -> "GAGNANT".equals(p.getTypePari()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
List<PariSimple> parisPlace = tousParis.stream()
|
||||||
|
.filter(p -> "PLACE".equals(p.getTypePari()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// Calcul des masses à partager pour chaque type de pari
|
||||||
|
double masseGagnant = masseAPartager * (parisGagnant.stream().mapToDouble(PariSimple::getMise).sum() / totalMises);
|
||||||
|
double massePlace = masseAPartager * (parisPlace.stream().mapToDouble(PariSimple::getMise).sum() / totalMises);
|
||||||
|
|
||||||
|
// Calcul des rapports
|
||||||
|
Gains gains = new Gains();
|
||||||
|
gains.setCourse(course);
|
||||||
|
gains.setMasseGagnant(masseGagnant);
|
||||||
|
gains.setMassePlace(massePlace);
|
||||||
|
gains.setDateCalcul(LocalDateTime.now());
|
||||||
|
|
||||||
|
// Calcul détaillé des gains selon les règles
|
||||||
|
calculerDetailsGains(gains, resultat, parisGagnant, parisPlace);
|
||||||
|
|
||||||
|
// Gérer la cagnotte si nécessaire (Article 6 et 7)
|
||||||
|
gererCagnotte(gains, resultat, parisGagnant, parisPlace);
|
||||||
|
|
||||||
|
return gainsRepository.save(gains);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double calculerRemboursements(List<PariSimple> paris) {
|
||||||
|
double remboursements = 0.0;
|
||||||
|
|
||||||
|
for (PariSimple pari : paris) {
|
||||||
|
if (pari.getCheval().isEstNonPartant()) {
|
||||||
|
remboursements += pari.getMise();
|
||||||
|
pari.setEstPaye(true); // Marquer comme remboursé
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return remboursements;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void calculerDetailsGains(Gains gains, ResultatCourse resultat,
|
||||||
|
List<PariSimple> parisGagnant, List<PariSimple> parisPlace) {
|
||||||
|
// Calcul des rapports GAGNANT
|
||||||
|
if (resultat.isADeadHeat()) {
|
||||||
|
calculerRapportsGagnantDeadHeat(gains, resultat, parisGagnant);
|
||||||
|
} else {
|
||||||
|
calculerRapportsGagnantNormal(gains, resultat, parisGagnant);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calcul des rapports PLACE
|
||||||
|
if (resultat.isADeadHeat()) {
|
||||||
|
calculerRapportsPlaceDeadHeat(gains, resultat, parisPlace);
|
||||||
|
} else {
|
||||||
|
calculerRapportsPlaceNormal(gains, resultat, parisPlace);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Garantir un rapport minimum de 1.1 par unité de mise (Article 5d)
|
||||||
|
gains.setRapportGagnant(Math.max(gains.getRapportGagnant(), RAPPORT_MINIMUM));
|
||||||
|
gains.setRapportPlace(Math.max(gains.getRapportPlace(), RAPPORT_MINIMUM));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void calculerRapportsGagnantNormal(Gains gains, ResultatCourse resultat, List<PariSimple> parisGagnant) {
|
||||||
|
// Article 5a: Cas d'arrivée normale - Calcul du rapport "gagnant"
|
||||||
|
if (resultat.getChevauxPremiers().isEmpty()) {
|
||||||
|
gains.setRapportGagnant(0.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gestion des écuries (Article 2)
|
||||||
|
Long chevalGagnantId = resultat.getChevauxPremiers().get(0);
|
||||||
|
Cheval chevalGagnant = chevalRepository.findById(chevalGagnantId).orElse(null);
|
||||||
|
|
||||||
|
if (chevalGagnant == null) {
|
||||||
|
gains.setRapportGagnant(0.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trouver tous les chevaux de la même écurie
|
||||||
|
List<Cheval> chevauxEcurie = Collections.singletonList(chevalGagnant);
|
||||||
|
if (chevalGagnant.getNomEcurie() != null) {
|
||||||
|
chevauxEcurie = chevalRepository.findByNomEcurie(chevalGagnant.getNomEcurie());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculer le total des mises sur les chevaux de l'écurie
|
||||||
|
double totalMisesEcurie = 0.0;
|
||||||
|
for (Cheval cheval : chevauxEcurie) {
|
||||||
|
double misesCheval = parisGagnant.stream()
|
||||||
|
.filter(p -> p.getCheval().getId().equals(cheval.getId()))
|
||||||
|
.mapToDouble(PariSimple::getMise)
|
||||||
|
.sum();
|
||||||
|
totalMisesEcurie += misesCheval;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalMisesEcurie > 0) {
|
||||||
|
gains.setRapportGagnant(gains.getMasseGagnant() / totalMisesEcurie);
|
||||||
|
} else {
|
||||||
|
gains.setRapportGagnant(0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void calculerRapportsGagnantDeadHeat(Gains gains, ResultatCourse resultat, List<PariSimple> parisGagnant) {
|
||||||
|
// Article 5a: Cas d'arrivée "dead-heat" - Calcul des rapports "gagnant"
|
||||||
|
if (resultat.getChevauxPremiers().isEmpty()) {
|
||||||
|
gains.setRapportGagnant(0.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculer le bénéfice à répartir (Article 5a)
|
||||||
|
double totalMisesPayables = 0.0;
|
||||||
|
for (Long chevalId : resultat.getChevauxPremiers()) {
|
||||||
|
double misesCheval = parisGagnant.stream()
|
||||||
|
.filter(p -> p.getCheval().getId().equals(chevalId))
|
||||||
|
.mapToDouble(PariSimple::getMise)
|
||||||
|
.sum();
|
||||||
|
totalMisesPayables += misesCheval;
|
||||||
|
}
|
||||||
|
|
||||||
|
double beneficeARepartir = gains.getMasseGagnant() - totalMisesPayables;
|
||||||
|
|
||||||
|
if (beneficeARepartir <= 0) {
|
||||||
|
gains.setRapportGagnant(0.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Diviser le bénéfice en autant de parties qu'il y a de chevaux classés premiers
|
||||||
|
double partParCheval = beneficeARepartir / resultat.getChevauxPremiers().size();
|
||||||
|
|
||||||
|
// Pour chaque cheval, calculer son rapport
|
||||||
|
double rapportTotal = 0.0;
|
||||||
|
int chevauxAvecMises = 0;
|
||||||
|
|
||||||
|
for (Long chevalId : resultat.getChevauxPremiers()) {
|
||||||
|
double misesCheval = parisGagnant.stream()
|
||||||
|
.filter(p -> p.getCheval().getId().equals(chevalId))
|
||||||
|
.mapToDouble(PariSimple::getMise)
|
||||||
|
.sum();
|
||||||
|
|
||||||
|
if (misesCheval > 0) {
|
||||||
|
double rapportCheval = (partParCheval / misesCheval) + 1;
|
||||||
|
rapportTotal += rapportCheval;
|
||||||
|
chevauxAvecMises++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Article 6: Si aucun mise sur un cheval, partager entre les autres
|
||||||
|
if (chevauxAvecMises > 0) {
|
||||||
|
gains.setRapportGagnant(rapportTotal / chevauxAvecMises);
|
||||||
|
} else {
|
||||||
|
gains.setRapportGagnant(0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void calculerRapportsPlaceNormal(Gains gains, ResultatCourse resultat, List<PariSimple> parisPlace) {
|
||||||
|
// Article 5b: Cas d'arrivée normale - Calcul du rapport "placé"
|
||||||
|
int nombreChevauxInscrits = gains.getCourse().getNombreChevauxInscrits();
|
||||||
|
List<Long> chevauxPayables = new ArrayList<>();
|
||||||
|
|
||||||
|
// Déterminer les chevaux payables selon le nombre de partants
|
||||||
|
if (nombreChevauxInscrits >= 4 && nombreChevauxInscrits <= 7) {
|
||||||
|
// 2 premiers
|
||||||
|
chevauxPayables.addAll(resultat.getChevauxPremiers());
|
||||||
|
chevauxPayables.addAll(resultat.getChevauxDeuxiemes());
|
||||||
|
} else if (nombreChevauxInscrits >= 8) {
|
||||||
|
// 3 premiers
|
||||||
|
chevauxPayables.addAll(resultat.getChevauxPremiers());
|
||||||
|
chevauxPayables.addAll(resultat.getChevauxDeuxiemes());
|
||||||
|
chevauxPayables.addAll(resultat.getChevauxTroisiemes());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chevauxPayables.isEmpty()) {
|
||||||
|
gains.setRapportPlace(0.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculer le bénéfice à répartir (Article 5b)
|
||||||
|
double totalMisesPayables = 0.0;
|
||||||
|
for (Long chevalId : chevauxPayables) {
|
||||||
|
double misesCheval = parisPlace.stream()
|
||||||
|
.filter(p -> p.getCheval().getId().equals(chevalId))
|
||||||
|
.mapToDouble(PariSimple::getMise)
|
||||||
|
.sum();
|
||||||
|
totalMisesPayables += misesCheval;
|
||||||
|
}
|
||||||
|
|
||||||
|
double beneficeARepartir = gains.getMassePlace() - totalMisesPayables;
|
||||||
|
|
||||||
|
if (beneficeARepartir <= 0) {
|
||||||
|
gains.setRapportPlace(0.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Diviser le bénéfice en autant de parties qu'il y a de chevaux payables
|
||||||
|
double partParCheval = beneficeARepartir / chevauxPayables.size();
|
||||||
|
|
||||||
|
// Pour chaque cheval, calculer son rapport
|
||||||
|
double rapportTotal = 0.0;
|
||||||
|
int chevauxAvecMises = 0;
|
||||||
|
|
||||||
|
for (Long chevalId : chevauxPayables) {
|
||||||
|
double misesCheval = parisPlace.stream()
|
||||||
|
.filter(p -> p.getCheval().getId().equals(chevalId))
|
||||||
|
.mapToDouble(PariSimple::getMise)
|
||||||
|
.sum();
|
||||||
|
|
||||||
|
if (misesCheval > 0) {
|
||||||
|
double rapportCheval = (partParCheval / misesCheval) + 1;
|
||||||
|
rapportTotal += rapportCheval;
|
||||||
|
chevauxAvecMises++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Article 6: Si aucun mise sur un cheval, partager entre les autres
|
||||||
|
if (chevauxAvecMises > 0) {
|
||||||
|
gains.setRapportPlace(rapportTotal / chevauxAvecMises);
|
||||||
|
} else {
|
||||||
|
gains.setRapportPlace(0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void calculerRapportsPlaceDeadHeat(Gains gains, ResultatCourse resultat, List<PariSimple> parisPlace) {
|
||||||
|
// Article 5b et 5c: Cas d'arrivée "dead-heat" - Calcul des rapports "placé"
|
||||||
|
int nombreChevauxInscrits = gains.getCourse().getNombreChevauxInscrits();
|
||||||
|
|
||||||
|
// Déterminer les chevaux payables selon le nombre de partants et le type de dead-heat
|
||||||
|
List<Long> chevauxPayables = determinerChevauxPayablesPlace(resultat, nombreChevauxInscrits);
|
||||||
|
|
||||||
|
if (chevauxPayables.isEmpty()) {
|
||||||
|
gains.setRapportPlace(0.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculer le bénéfice à répartir
|
||||||
|
double totalMisesPayables = 0.0;
|
||||||
|
for (Long chevalId : chevauxPayables) {
|
||||||
|
double misesCheval = parisPlace.stream()
|
||||||
|
.filter(p -> p.getCheval().getId().equals(chevalId))
|
||||||
|
.mapToDouble(PariSimple::getMise)
|
||||||
|
.sum();
|
||||||
|
totalMisesPayables += misesCheval;
|
||||||
|
}
|
||||||
|
|
||||||
|
double beneficeARepartir = gains.getMassePlace() - totalMisesPayables;
|
||||||
|
|
||||||
|
if (beneficeARepartir <= 0) {
|
||||||
|
gains.setRapportPlace(0.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Application des règles spécifiques pour les dead-heats (Article 5c)
|
||||||
|
if (nombreChevauxInscrits < 8) {
|
||||||
|
beneficeARepartir = appliquerReglesPlaceMoinsHuit(resultat, beneficeARepartir);
|
||||||
|
} else {
|
||||||
|
beneficeARepartir = appliquerReglesPlaceHuitPlus(resultat, beneficeARepartir);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Diviser le bénéfice entre les chevaux payables
|
||||||
|
double partParCheval = beneficeARepartir / chevauxPayables.size();
|
||||||
|
|
||||||
|
// Pour chaque cheval, calculer son rapport
|
||||||
|
double rapportTotal = 0.0;
|
||||||
|
int chevauxAvecMises = 0;
|
||||||
|
|
||||||
|
for (Long chevalId : chevauxPayables) {
|
||||||
|
double misesCheval = parisPlace.stream()
|
||||||
|
.filter(p -> p.getCheval().getId().equals(chevalId))
|
||||||
|
.mapToDouble(PariSimple::getMise)
|
||||||
|
.sum();
|
||||||
|
|
||||||
|
if (misesCheval > 0) {
|
||||||
|
double rapportCheval = (partParCheval / misesCheval) + 1;
|
||||||
|
rapportTotal += rapportCheval;
|
||||||
|
chevauxAvecMises++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Article 6: Si aucun mise sur un cheval, partager entre les autres
|
||||||
|
if (chevauxAvecMises > 0) {
|
||||||
|
gains.setRapportPlace(rapportTotal / chevauxAvecMises);
|
||||||
|
} else {
|
||||||
|
gains.setRapportPlace(0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Long> determinerChevauxPayablesPlace(ResultatCourse resultat, int nombreChevauxInscrits) {
|
||||||
|
List<Long> chevauxPayables = new ArrayList<>();
|
||||||
|
|
||||||
|
// Article 3: Déterminer les chevaux payables selon le nombre de partants
|
||||||
|
if (nombreChevauxInscrits >= 4 && nombreChevauxInscrits <= 7) {
|
||||||
|
// 2 premiers
|
||||||
|
chevauxPayables.addAll(resultat.getChevauxPremiers());
|
||||||
|
chevauxPayables.addAll(resultat.getChevauxDeuxiemes());
|
||||||
|
} else if (nombreChevauxInscrits >= 8) {
|
||||||
|
// 3 premiers
|
||||||
|
chevauxPayables.addAll(resultat.getChevauxPremiers());
|
||||||
|
chevauxPayables.addAll(resultat.getChevauxDeuxiemes());
|
||||||
|
chevauxPayables.addAll(resultat.getChevauxTroisiemes());
|
||||||
|
}
|
||||||
|
|
||||||
|
return chevauxPayables;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double appliquerReglesPlaceMoinsHuit(ResultatCourse resultat, double beneficeARepartir) {
|
||||||
|
// Article 5c: Règles pour les courses de moins de 8 chevaux avec dead-heat
|
||||||
|
int nbPremiers = resultat.getChevauxPremiers().size();
|
||||||
|
int nbDeuxiemes = resultat.getChevauxDeuxiemes().size();
|
||||||
|
|
||||||
|
if (nbPremiers > 1) {
|
||||||
|
// Plus d'un cheval classé premier
|
||||||
|
return beneficeARepartir / nbPremiers;
|
||||||
|
} else if (nbDeuxiemes > 1) {
|
||||||
|
// Plus d'un cheval classé deuxième
|
||||||
|
double partPremier = beneficeARepartir / 2;
|
||||||
|
double partDeuxiemes = beneficeARepartir / 2;
|
||||||
|
return partPremier + (partDeuxiemes / nbDeuxiemes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return beneficeARepartir;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double appliquerReglesPlaceHuitPlus(ResultatCourse resultat, double beneficeARepartir) {
|
||||||
|
// Article 5c: Règles pour les courses de 8 chevaux et plus avec dead-heat
|
||||||
|
int nbPremiers = resultat.getChevauxPremiers().size();
|
||||||
|
int nbDeuxiemes = resultat.getChevauxDeuxiemes().size();
|
||||||
|
int nbTroisiemes = resultat.getChevauxTroisiemes().size();
|
||||||
|
|
||||||
|
if (nbPremiers == 1 && nbDeuxiemes == 1) {
|
||||||
|
// Un seul premier et un seul deuxième, plusieurs troisièmes
|
||||||
|
double partPremier = beneficeARepartir / 3;
|
||||||
|
double partDeuxieme = beneficeARepartir / 3;
|
||||||
|
double partTroisiemes = beneficeARepartir / 3;
|
||||||
|
return partPremier + partDeuxieme + (partTroisiemes / nbTroisiemes);
|
||||||
|
} else if (nbPremiers == 1 && nbDeuxiemes > 1) {
|
||||||
|
// Un seul premier, plusieurs deuxièmes
|
||||||
|
double partPremier = beneficeARepartir / 3;
|
||||||
|
double partDeuxiemes = beneficeARepartir * 2 / 3;
|
||||||
|
return partPremier + (partDeuxiemes / nbDeuxiemes);
|
||||||
|
} else if (nbPremiers == 2) {
|
||||||
|
// Deux chevaux classés premiers
|
||||||
|
double partParPremier = beneficeARepartir / 3;
|
||||||
|
double partTroisiemes = beneficeARepartir / 3;
|
||||||
|
return (2 * partParPremier) + (partTroisiemes / nbTroisiemes);
|
||||||
|
} else if (nbPremiers > 2) {
|
||||||
|
// Plus de deux chevaux classés premiers
|
||||||
|
return beneficeARepartir / nbPremiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
return beneficeARepartir;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void gererCagnotte(Gains gains, ResultatCourse resultat,
|
||||||
|
List<PariSimple> parisGagnant, List<PariSimple> parisPlace) {
|
||||||
|
// Article 6 et 7: Gestion de la cagnotte (tirelire)
|
||||||
|
double montantCagnotte = 0.0;
|
||||||
|
|
||||||
|
// Vérifier les conditions pour la cagnotte GAGNANT
|
||||||
|
boolean cagnotteGagnant = false;
|
||||||
|
if (resultat.getChevauxPremiers().isEmpty()) {
|
||||||
|
// Aucun cheval classé premier
|
||||||
|
cagnotteGagnant = true;
|
||||||
|
} else {
|
||||||
|
// Vérifier s'il n'y a aucune mise sur les chevaux premiers
|
||||||
|
boolean aucuneMiseGagnant = true;
|
||||||
|
for (Long chevalId : resultat.getChevauxPremiers()) {
|
||||||
|
double mises = parisGagnant.stream()
|
||||||
|
.filter(p -> p.getCheval().getId().equals(chevalId))
|
||||||
|
.mapToDouble(PariSimple::getMise)
|
||||||
|
.sum();
|
||||||
|
if (mises > 0) {
|
||||||
|
aucuneMiseGagnant = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cagnotteGagnant = aucuneMiseGagnant;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cagnotteGagnant) {
|
||||||
|
montantCagnotte += gains.getMasseGagnant();
|
||||||
|
gains.setMasseGagnant(0.0);
|
||||||
|
gains.setRapportGagnant(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier les conditions pour la cagnotte PLACE
|
||||||
|
boolean cagnottePlace = false;
|
||||||
|
int nombreChevauxInscrits = gains.getCourse().getNombreChevauxInscrits();
|
||||||
|
|
||||||
|
// Article 6.3: Si moins de 4 chevaux ont pris part à la course
|
||||||
|
if (nombreChevauxInscrits < 4) {
|
||||||
|
cagnottePlace = true;
|
||||||
|
} else {
|
||||||
|
List<Long> chevauxPayables = determinerChevauxPayablesPlace(resultat, nombreChevauxInscrits);
|
||||||
|
|
||||||
|
if (chevauxPayables.isEmpty()) {
|
||||||
|
cagnottePlace = true;
|
||||||
|
} else {
|
||||||
|
// Vérifier s'il n'y a aucune mise sur les chevaux payables
|
||||||
|
boolean aucuneMisePlace = true;
|
||||||
|
for (Long chevalId : chevauxPayables) {
|
||||||
|
double mises = parisPlace.stream()
|
||||||
|
.filter(p -> p.getCheval().getId().equals(chevalId))
|
||||||
|
.mapToDouble(PariSimple::getMise)
|
||||||
|
.sum();
|
||||||
|
if (mises > 0) {
|
||||||
|
aucuneMisePlace = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cagnottePlace = aucuneMisePlace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cagnottePlace) {
|
||||||
|
montantCagnotte += gains.getMassePlace();
|
||||||
|
gains.setMassePlace(0.0);
|
||||||
|
gains.setRapportPlace(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
gains.setMontantCagnotte(montantCagnotte);
|
||||||
|
|
||||||
|
// TODO: Implémenter la logique de report de la cagnotte selon l'article 7
|
||||||
|
}
|
||||||
|
|
||||||
|
public Gains obtenirGainsParCourse(Long courseId) {
|
||||||
|
return gainsRepository.findByCourseId(courseId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package com.pmumali.ch1_simple.service;
|
||||||
|
|
||||||
|
import com.pmumali.ch1_simple.model.Course;
|
||||||
|
import com.pmumali.ch1_simple.model.PariSimple;
|
||||||
|
import com.pmumali.ch1_simple.repository.ChevalRepository;
|
||||||
|
import com.pmumali.ch1_simple.repository.CourseRepository;
|
||||||
|
import com.pmumali.ch1_simple.repository.PariSimpleRepository;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class PariSimpleService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PariSimpleRepository pariRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CourseRepository courseRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ChevalRepository chevalRepository;
|
||||||
|
|
||||||
|
private static final double MISE_DE_BASE = 500.0;
|
||||||
|
|
||||||
|
public PariSimple placerPari(PariSimple pari) {
|
||||||
|
// Vérifier que le type de pari est valide pour la course
|
||||||
|
Course course = pari.getCourse();
|
||||||
|
if ("GAGNANT".equals(pari.getTypePari()) && course.getNombreChevauxInscrits() < 2) {
|
||||||
|
throw new IllegalArgumentException("Pari GAGNANT impossible: moins de 2 chevaux");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("PLACE".equals(pari.getTypePari()) && course.getNombreChevauxInscrits() < 3) {
|
||||||
|
throw new IllegalArgumentException("Pari PLACE impossible: moins de 3 chevaux");
|
||||||
|
}
|
||||||
|
|
||||||
|
return pariRepository.save(pari);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PariSimple> obtenirParisParCourse(Long courseId) {
|
||||||
|
return pariRepository.findByCourseId(courseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PariSimple> obtenirParisParCourseEtType(Long courseId, String typePari) {
|
||||||
|
return pariRepository.findByCourseIdAndTypePari(courseId, typePari);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PariSimple> obtenirParisParCheval(Long chevalId) {
|
||||||
|
return pariRepository.findByChevalId(chevalId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.pmumali.ch2_jumelegagnant.controller;
|
||||||
|
|
||||||
|
import com.pmumali.ch2_jumelegagnant.model.Cheval;
|
||||||
|
import com.pmumali.ch2_jumelegagnant.service.ChevalService;
|
||||||
|
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/chevaux")
|
||||||
|
public class ChevalController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ChevalService chevalService;
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public ResponseEntity<Cheval> ajouterCheval(@RequestBody Cheval cheval) {
|
||||||
|
return ResponseEntity.ok(chevalService.ajouterCheval(cheval));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/course/{courseId}")
|
||||||
|
public ResponseEntity<List<Cheval>> obtenirChevauxParCourse(@PathVariable Long courseId) {
|
||||||
|
return ResponseEntity.ok(chevalService.obtenirChevauxParCourse(courseId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/non-partants")
|
||||||
|
public ResponseEntity<List<Cheval>> obtenirChevauxNonPartants() {
|
||||||
|
return ResponseEntity.ok(chevalService.obtenirChevauxNonPartants());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package com.pmumali.ch2_jumelegagnant.controller;
|
||||||
|
|
||||||
|
import com.pmumali.ch2_jumelegagnant.model.Course;
|
||||||
|
import com.pmumali.ch2_jumelegagnant.service.CourseService;
|
||||||
|
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 CourseController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CourseService courseService;
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public ResponseEntity<Course> creerCourse(@RequestBody Course course) {
|
||||||
|
return ResponseEntity.ok(courseService.creerCourse(course));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public ResponseEntity<List<Course>> obtenirToutesCourses() {
|
||||||
|
return ResponseEntity.ok(courseService.obtenirToutesCourses());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public ResponseEntity<Course> obtenirCourseParId(@PathVariable Long id) {
|
||||||
|
Course course = courseService.obtenirCourseParId(id);
|
||||||
|
return course != null ? ResponseEntity.ok(course) : ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/terminees")
|
||||||
|
public ResponseEntity<List<Course>> obtenirCoursesTerminees() {
|
||||||
|
return ResponseEntity.ok(courseService.obtenirCoursesTerminees());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/avenir")
|
||||||
|
public ResponseEntity<List<Course>> obtenirCoursesAVenir() {
|
||||||
|
return ResponseEntity.ok(courseService.obtenirCoursesAVenir());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/annulees")
|
||||||
|
public ResponseEntity<List<Course>> obtenirCoursesAnnulees() {
|
||||||
|
return ResponseEntity.ok(courseService.obtenirCoursesAnnulees());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.pmumali.ch2_jumelegagnant.controller;
|
||||||
|
|
||||||
|
import com.pmumali.ch2_jumelegagnant.model.Gains;
|
||||||
|
import com.pmumali.ch2_jumelegagnant.model.ResultatCourse;
|
||||||
|
import com.pmumali.ch2_jumelegagnant.service.GainsService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/gains")
|
||||||
|
public class GainsController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private GainsService gainsService;
|
||||||
|
|
||||||
|
@PostMapping("/calculer/{courseId}")
|
||||||
|
public ResponseEntity<Gains> calculerGains(@PathVariable Long courseId, @RequestBody ResultatCourse resultat) {
|
||||||
|
return ResponseEntity.ok(gainsService.calculerGains(courseId, resultat));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/course/{courseId}")
|
||||||
|
public ResponseEntity<Gains> obtenirGainsParCourse(@PathVariable Long courseId) {
|
||||||
|
Gains gains = gainsService.obtenirGainsParCourse(courseId);
|
||||||
|
return gains != null ? ResponseEntity.ok(gains) : ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.pmumali.ch2_jumelegagnant.controller;
|
||||||
|
|
||||||
|
import com.pmumali.ch2_jumelegagnant.model.PariJumeleGagnant;
|
||||||
|
import com.pmumali.ch2_jumelegagnant.service.PariJumeleService;
|
||||||
|
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-jumele")
|
||||||
|
public class PariJumeleController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PariJumeleService pariService;
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public ResponseEntity<PariJumeleGagnant> placerPari(@RequestBody PariJumeleGagnant pari) {
|
||||||
|
return ResponseEntity.ok(pariService.placerPari(pari));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/course/{courseId}")
|
||||||
|
public ResponseEntity<List<PariJumeleGagnant>> obtenirParisParCourse(@PathVariable Long courseId) {
|
||||||
|
return ResponseEntity.ok(pariService.obtenirParisParCourse(courseId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/cheval/{chevalId}")
|
||||||
|
public ResponseEntity<List<PariJumeleGagnant>> obtenirParisParCheval(@PathVariable Long chevalId) {
|
||||||
|
return ResponseEntity.ok(pariService.obtenirParisParCheval(chevalId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/calculer-formule-combinee/{nombreChevaux}")
|
||||||
|
public ResponseEntity<Double> calculerCoutFormuleCombinee(@PathVariable int nombreChevaux) {
|
||||||
|
return ResponseEntity.ok(pariService.calculerCoutFormuleCombinee(nombreChevaux));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/calculer-formule-champ")
|
||||||
|
public ResponseEntity<Double> calculerCoutFormuleChamp(
|
||||||
|
@RequestParam int nombreChevauxPartants,
|
||||||
|
@RequestParam int nombreChevauxSelectionnes,
|
||||||
|
@RequestParam boolean estChampTotal) {
|
||||||
|
return ResponseEntity.ok(pariService.calculerCoutFormuleChamp(
|
||||||
|
nombreChevauxPartants, nombreChevauxSelectionnes, estChampTotal));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.pmumali.ch2_jumelegagnant.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ChevalDto {
|
||||||
|
private Long id;
|
||||||
|
private String nom;
|
||||||
|
private int numero;
|
||||||
|
private boolean estNonPartant;
|
||||||
|
private Long courseId;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.pmumali.ch2_jumelegagnant.dto;
|
||||||
|
|
||||||
|
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 int nombreChevauxInscrits;
|
||||||
|
private boolean estTerminee;
|
||||||
|
private boolean estAnnulee;
|
||||||
|
private List<Long> chevauxIds;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.pmumali.ch2_jumelegagnant.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class GainsDto {
|
||||||
|
private Long id;
|
||||||
|
private Long courseId;
|
||||||
|
private double masseAPartager;
|
||||||
|
private double montantCagnotte;
|
||||||
|
private List<Double> rapports;
|
||||||
|
private LocalDateTime dateCalcul;
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.pmumali.ch2_jumelegagnant.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class PariJumeleDto {
|
||||||
|
private Long id;
|
||||||
|
private String typePari;
|
||||||
|
private double mise;
|
||||||
|
private LocalDateTime datePari;
|
||||||
|
private Long cheval1Id;
|
||||||
|
private Long cheval2Id;
|
||||||
|
private Long courseId;
|
||||||
|
private boolean estPaye;
|
||||||
|
private boolean estRembourse;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.pmumali.ch2_jumelegagnant.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ResultatCourseDto {
|
||||||
|
private Long id;
|
||||||
|
private Long courseId;
|
||||||
|
private List<Long> chevauxPremiers;
|
||||||
|
private List<Long> chevauxDeuxiemes;
|
||||||
|
private boolean aDeadHeat;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.pmumali.ch2_jumelegagnant.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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.pmumali.ch2_jumelegagnant.model;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.*;
|
||||||
|
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 int nombreChevauxInscrits;
|
||||||
|
private boolean estTerminee;
|
||||||
|
private boolean estAnnulee;
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "course", cascade = CascadeType.ALL)
|
||||||
|
private List<Cheval> chevaux;
|
||||||
|
|
||||||
|
@OneToOne(mappedBy = "course", cascade = CascadeType.ALL)
|
||||||
|
private ResultatCourse resultat;
|
||||||
|
}
|
||||||
30
src/main/java/com/pmumali/ch2_jumelegagnant/model/Gains.java
Normal file
30
src/main/java/com/pmumali/ch2_jumelegagnant/model/Gains.java
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package com.pmumali.ch2_jumelegagnant.model;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@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 masseAPartager;
|
||||||
|
private double montantCagnotte;
|
||||||
|
|
||||||
|
@ElementCollection
|
||||||
|
private List<Double> rapports; // Rapports pour chaque combinaison payable
|
||||||
|
|
||||||
|
private LocalDateTime dateCalcul;
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.pmumali.ch2_jumelegagnant.model;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Table(name = "pari_jumele_gagnant")
|
||||||
|
public class PariJumeleGagnant {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String typePari; // UNITAIRE, COMBINE, CHAMP_TOTAL, CHAMP_PARTIEL
|
||||||
|
private double mise = 500.0;
|
||||||
|
private LocalDateTime datePari;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "cheval1_id")
|
||||||
|
private Cheval cheval1;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "cheval2_id")
|
||||||
|
private Cheval cheval2;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "course_id")
|
||||||
|
private Course course;
|
||||||
|
|
||||||
|
private boolean estPaye;
|
||||||
|
private boolean estRembourse;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.pmumali.ch2_jumelegagnant.model;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Table(name = "resultat_course")
|
||||||
|
public class ResultatCourse {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@OneToOne
|
||||||
|
@JoinColumn(name = "course_id")
|
||||||
|
private Course course;
|
||||||
|
|
||||||
|
@ElementCollection
|
||||||
|
private List<Long> chevauxPremiers; // Pour gérer les dead-heats
|
||||||
|
|
||||||
|
@ElementCollection
|
||||||
|
private List<Long> chevauxDeuxiemes; // Pour gérer les dead-heats
|
||||||
|
|
||||||
|
private boolean aDeadHeat;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.pmumali.ch2_jumelegagnant.repository;
|
||||||
|
|
||||||
|
import com.pmumali.ch2_jumelegagnant.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);
|
||||||
|
List<Cheval> findByEstNonPartant(boolean estNonPartant);
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.pmumali.ch2_jumelegagnant.repository;
|
||||||
|
|
||||||
|
import com.pmumali.ch2_jumelegagnant.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);
|
||||||
|
List<Course> findByEstAnnulee(boolean estAnnulee);
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.pmumali.ch2_jumelegagnant.repository;
|
||||||
|
|
||||||
|
import com.pmumali.ch2_jumelegagnant.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,14 @@
|
|||||||
|
package com.pmumali.ch2_jumelegagnant.repository;
|
||||||
|
|
||||||
|
import com.pmumali.ch2_jumelegagnant.model.PariJumeleGagnant;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface PariJumeleRepository extends JpaRepository<PariJumeleGagnant, Long> {
|
||||||
|
List<PariJumeleGagnant> findByCourseId(Long courseId);
|
||||||
|
List<PariJumeleGagnant> findByCheval1IdOrCheval2Id(Long cheval1Id, Long cheval2Id);
|
||||||
|
List<PariJumeleGagnant> findByEstRembourse(boolean estRembourse);
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.pmumali.ch2_jumelegagnant.repository;
|
||||||
|
|
||||||
|
import com.pmumali.ch2_jumelegagnant.model.ResultatCourse;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface ResultatCourseRepository extends JpaRepository<ResultatCourse, Long> {
|
||||||
|
ResultatCourse findByCourseId(Long courseId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.pmumali.ch2_jumelegagnant.service;
|
||||||
|
|
||||||
|
import com.pmumali.ch2_jumelegagnant.model.Cheval;
|
||||||
|
import com.pmumali.ch2_jumelegagnant.repository.ChevalRepository;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ChevalService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ChevalRepository chevalRepository;
|
||||||
|
|
||||||
|
public Cheval ajouterCheval(Cheval cheval) {
|
||||||
|
return chevalRepository.save(cheval);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Cheval> obtenirChevauxParCourse(Long courseId) {
|
||||||
|
return chevalRepository.findByCourseId(courseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Cheval> obtenirChevauxNonPartants() {
|
||||||
|
return chevalRepository.findByEstNonPartant(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.pmumali.ch2_jumelegagnant.service;
|
||||||
|
|
||||||
|
import com.pmumali.ch2_jumelegagnant.model.Course;
|
||||||
|
import com.pmumali.ch2_jumelegagnant.repository.CourseRepository;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class CourseService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CourseRepository courseRepository;
|
||||||
|
|
||||||
|
public Course creerCourse(Course course) {
|
||||||
|
return courseRepository.save(course);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Course> obtenirToutesCourses() {
|
||||||
|
return courseRepository.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Course obtenirCourseParId(Long id) {
|
||||||
|
return courseRepository.findById(id).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Course> obtenirCoursesTerminees() {
|
||||||
|
return courseRepository.findByEstTerminee(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Course> obtenirCoursesAVenir() {
|
||||||
|
return courseRepository.findByEstTerminee(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Course> obtenirCoursesAnnulees() {
|
||||||
|
return courseRepository.findByEstAnnulee(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,329 @@
|
|||||||
|
package com.pmumali.ch2_jumelegagnant.service;
|
||||||
|
|
||||||
|
import com.pmumali.ch2_jumelegagnant.model.Course;
|
||||||
|
import com.pmumali.ch2_jumelegagnant.model.Gains;
|
||||||
|
import com.pmumali.ch2_jumelegagnant.model.PariJumeleGagnant;
|
||||||
|
import com.pmumali.ch2_jumelegagnant.model.ResultatCourse;
|
||||||
|
import com.pmumali.ch2_jumelegagnant.repository.*;
|
||||||
|
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.*;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Transactional
|
||||||
|
public class GainsService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PariJumeleRepository pariRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private GainsRepository gainsRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CourseRepository courseRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ChevalRepository chevalRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ResultatCourseRepository resultatRepository;
|
||||||
|
|
||||||
|
private static final double RAPPORT_MINIMUM = 1.1;
|
||||||
|
private static final double PRELEVEMENTS = 0.15; // 15% de prélèvements
|
||||||
|
|
||||||
|
public Gains calculerGains(Long courseId, ResultatCourse resultat) {
|
||||||
|
Course course = courseRepository.findById(courseId)
|
||||||
|
.orElseThrow(() -> new RuntimeException("Course non trouvée"));
|
||||||
|
|
||||||
|
List<PariJumeleGagnant> tousParis = pariRepository.findByCourseId(courseId);
|
||||||
|
|
||||||
|
// Calcul de la recette nette (Article 5)
|
||||||
|
double totalMises = tousParis.stream().mapToDouble(PariJumeleGagnant::getMise).sum();
|
||||||
|
double recetteNette = totalMises;
|
||||||
|
|
||||||
|
// Calcul des remboursements (Article 4)
|
||||||
|
double montantRemboursements = calculerRemboursements(tousParis);
|
||||||
|
|
||||||
|
// Masse à partager (Article 5)
|
||||||
|
double masseAPartager = recetteNette - montantRemboursements - (recetteNette * PRELEVEMENTS);
|
||||||
|
|
||||||
|
// Déterminer les combinaisons payables
|
||||||
|
List<CombinaisonPayable> combinaisonsPayables = determinerCombinaisonsPayables(resultat);
|
||||||
|
|
||||||
|
// Calcul des rapports
|
||||||
|
Gains gains = new Gains();
|
||||||
|
gains.setCourse(course);
|
||||||
|
gains.setMasseAPartager(masseAPartager);
|
||||||
|
gains.setDateCalcul(LocalDateTime.now());
|
||||||
|
|
||||||
|
// Calcul détaillé des gains selon les règles
|
||||||
|
if (resultat.isADeadHeat()) {
|
||||||
|
calculerRapportsDeadHeat(gains, combinaisonsPayables, tousParis);
|
||||||
|
} else {
|
||||||
|
calculerRapportsNormaux(gains, combinaisonsPayables, tousParis);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gérer la cagnotte si nécessaire (Article 8 et 9)
|
||||||
|
gererCagnotte(gains, combinaisonsPayables);
|
||||||
|
|
||||||
|
return gainsRepository.save(gains);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double calculerRemboursements(List<PariJumeleGagnant> paris) {
|
||||||
|
double remboursements = 0.0;
|
||||||
|
|
||||||
|
for (PariJumeleGagnant pari : paris) {
|
||||||
|
// Article 4: Remboursement si un ou deux chevaux non partants
|
||||||
|
if (pari.getCheval1().isEstNonPartant() || pari.getCheval2().isEstNonPartant()) {
|
||||||
|
remboursements += pari.getMise();
|
||||||
|
pari.setEstRembourse(true);
|
||||||
|
pariRepository.save(pari);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return remboursements;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<CombinaisonPayable> determinerCombinaisonsPayables(ResultatCourse resultat) {
|
||||||
|
List<CombinaisonPayable> combinaisons = new ArrayList<>();
|
||||||
|
|
||||||
|
// Article 1: Le pari est payable si les deux chevaux occupent les deux premières places
|
||||||
|
// Article 3: Gestion des dead-heats
|
||||||
|
|
||||||
|
if (resultat.isADeadHeat()) {
|
||||||
|
// Cas de dead-heat
|
||||||
|
if (resultat.getChevauxPremiers().size() >= 2) {
|
||||||
|
// Article 3a: Dead-heat à la première place
|
||||||
|
// Toutes les combinaisons des chevaux classés premiers pris deux à deux
|
||||||
|
for (int i = 0; i < resultat.getChevauxPremiers().size(); i++) {
|
||||||
|
for (int j = i + 1; j < resultat.getChevauxPremiers().size(); j++) {
|
||||||
|
combinaisons.add(new CombinaisonPayable(
|
||||||
|
resultat.getChevauxPremiers().get(i),
|
||||||
|
resultat.getChevauxPremiers().get(j)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (resultat.getChevauxDeuxiemes().size() >= 2) {
|
||||||
|
// Article 3b: Dead-heat à la deuxième place
|
||||||
|
// Combinaisons du premier avec chaque deuxième
|
||||||
|
for (Long chevalPremier : resultat.getChevauxPremiers()) {
|
||||||
|
for (Long chevalDeuxieme : resultat.getChevauxDeuxiemes()) {
|
||||||
|
combinaisons.add(new CombinaisonPayable(chevalPremier, chevalDeuxieme));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Cas normal
|
||||||
|
if (!resultat.getChevauxPremiers().isEmpty() && !resultat.getChevauxDeuxiemes().isEmpty()) {
|
||||||
|
combinaisons.add(new CombinaisonPayable(
|
||||||
|
resultat.getChevauxPremiers().get(0),
|
||||||
|
resultat.getChevauxDeuxiemes().get(0)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return combinaisons;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void calculerRapportsNormaux(Gains gains, List<CombinaisonPayable> combinaisonsPayables,
|
||||||
|
List<PariJumeleGagnant> tousParis) {
|
||||||
|
// Article 5a: Cas d'arrivée normale
|
||||||
|
if (combinaisonsPayables.isEmpty()) {
|
||||||
|
gains.setMontantCagnotte(gains.getMasseAPartager());
|
||||||
|
gains.setMasseAPartager(0.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalement, une seule combinaison payable en cas d'arrivée normale
|
||||||
|
CombinaisonPayable combinaisonPayable = combinaisonsPayables.get(0);
|
||||||
|
|
||||||
|
// Calculer le total des mises sur cette combinaison
|
||||||
|
double totalMises = calculerMisesSurCombinaison(combinaisonPayable, tousParis);
|
||||||
|
|
||||||
|
if (totalMises > 0) {
|
||||||
|
double rapport = gains.getMasseAPartager() / totalMises;
|
||||||
|
gains.setRapports(Collections.singletonList(Math.max(rapport, RAPPORT_MINIMUM)));
|
||||||
|
} else {
|
||||||
|
// Article 8a: Aucune mise sur la combinaison payable
|
||||||
|
gains.setMontantCagnotte(gains.getMasseAPartager());
|
||||||
|
gains.setMasseAPartager(0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void calculerRapportsDeadHeat(Gains gains, List<CombinaisonPayable> combinaisonsPayables,
|
||||||
|
List<PariJumeleGagnant> tousParis) {
|
||||||
|
// Article 5b: Cas d'arrivée "dead heat"
|
||||||
|
if (combinaisonsPayables.isEmpty()) {
|
||||||
|
gains.setMontantCagnotte(gains.getMasseAPartager());
|
||||||
|
gains.setMasseAPartager(0.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculer le total des mises sur toutes les combinaisons payables
|
||||||
|
double totalMisesPayables = 0.0;
|
||||||
|
Map<CombinaisonPayable, Double> misesParCombinaison = new HashMap<>();
|
||||||
|
|
||||||
|
for (CombinaisonPayable combinaison : combinaisonsPayables) {
|
||||||
|
double mises = calculerMisesSurCombinaison(combinaison, tousParis);
|
||||||
|
misesParCombinaison.put(combinaison, mises);
|
||||||
|
totalMisesPayables += mises;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculer le bénéfice à répartir
|
||||||
|
double beneficeARepartir = gains.getMasseAPartager() - totalMisesPayables;
|
||||||
|
|
||||||
|
if (beneficeARepartir <= 0) {
|
||||||
|
// Article 8b: Gestion des cas où il n'y a pas de bénéfice
|
||||||
|
gererCasParticuliersDeadHeat(gains, combinaisonsPayables, misesParCombinaison);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Diviser le bénéfice en autant de parties qu'il y a de combinaisons payables
|
||||||
|
double partParCombinaison = beneficeARepartir / combinaisonsPayables.size();
|
||||||
|
|
||||||
|
// Calculer les rapports pour chaque combinaison
|
||||||
|
List<Double> rapports = new ArrayList<>();
|
||||||
|
for (CombinaisonPayable combinaison : combinaisonsPayables) {
|
||||||
|
double mises = misesParCombinaison.get(combinaison);
|
||||||
|
if (mises > 0) {
|
||||||
|
double rapport = (partParCombinaison / mises) + 1;
|
||||||
|
rapports.add(Math.max(rapport, RAPPORT_MINIMUM));
|
||||||
|
} else {
|
||||||
|
// Article 8b1: Aucune mise sur une combinaison payable
|
||||||
|
// Répartir le bénéfice entre les autres combinaisons
|
||||||
|
double partSupplementaire = partParCombinaison / (combinaisonsPayables.size() - 1);
|
||||||
|
// Nous ajouterons cette part aux autres combinaisons plus tard
|
||||||
|
rapports.add(0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ajuster les rapports pour les combinaisons sans mises
|
||||||
|
ajusterRapportsSansMises(rapports, combinaisonsPayables, misesParCombinaison, partParCombinaison);
|
||||||
|
|
||||||
|
gains.setRapports(rapports);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void gererCasParticuliersDeadHeat(Gains gains, List<CombinaisonPayable> combinaisonsPayables,
|
||||||
|
Map<CombinaisonPayable, Double> misesParCombinaison) {
|
||||||
|
// Article 8b: Gestion des cas particuliers de dead-heat
|
||||||
|
boolean aucuneMise = true;
|
||||||
|
for (Double mises : misesParCombinaison.values()) {
|
||||||
|
if (mises > 0) {
|
||||||
|
aucuneMise = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aucuneMise) {
|
||||||
|
// Article 8b2, 8b3, 8b4: Aucune mise sur aucune combinaison payable
|
||||||
|
gains.setMontantCagnotte(gains.getMasseAPartager());
|
||||||
|
gains.setMasseAPartager(0.0);
|
||||||
|
} else {
|
||||||
|
// Répartir la masse entre les combinaisons avec des mises
|
||||||
|
double totalMises = misesParCombinaison.values().stream().mapToDouble(Double::doubleValue).sum();
|
||||||
|
List<Double> rapports = new ArrayList<>();
|
||||||
|
|
||||||
|
for (CombinaisonPayable combinaison : combinaisonsPayables) {
|
||||||
|
double mises = misesParCombinaison.get(combinaison);
|
||||||
|
if (mises > 0) {
|
||||||
|
double rapport = gains.getMasseAPartager() / totalMises;
|
||||||
|
rapports.add(Math.max(rapport, RAPPORT_MINIMUM));
|
||||||
|
} else {
|
||||||
|
rapports.add(0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gains.setRapports(rapports);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ajusterRapportsSansMises(List<Double> rapports, List<CombinaisonPayable> combinaisonsPayables,
|
||||||
|
Map<CombinaisonPayable, Double> misesParCombinaison, double partParCombinaison) {
|
||||||
|
// Compter le nombre de combinaisons sans mises
|
||||||
|
int combinaisonsSansMises = 0;
|
||||||
|
for (Double mises : misesParCombinaison.values()) {
|
||||||
|
if (mises == 0) {
|
||||||
|
combinaisonsSansMises++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (combinaisonsSansMises > 0) {
|
||||||
|
double partSupplementaire = partParCombinaison / (combinaisonsPayables.size() - combinaisonsSansMises);
|
||||||
|
|
||||||
|
for (int i = 0; i < rapports.size(); i++) {
|
||||||
|
if (rapports.get(i) > 0) {
|
||||||
|
rapports.set(i, rapports.get(i) + partSupplementaire / misesParCombinaison.get(combinaisonsPayables.get(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private double calculerMisesSurCombinaison(CombinaisonPayable combinaison, List<PariJumeleGagnant> tousParis) {
|
||||||
|
double totalMises = 0.0;
|
||||||
|
|
||||||
|
for (PariJumeleGagnant pari : tousParis) {
|
||||||
|
if ((pari.getCheval1().getId().equals(combinaison.getCheval1Id()) &&
|
||||||
|
pari.getCheval2().getId().equals(combinaison.getCheval2Id())) ||
|
||||||
|
(pari.getCheval1().getId().equals(combinaison.getCheval2Id()) &&
|
||||||
|
pari.getCheval2().getId().equals(combinaison.getCheval1Id()))) {
|
||||||
|
totalMises += pari.getMise();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalMises;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void gererCagnotte(Gains gains, List<CombinaisonPayable> combinaisonsPayables) {
|
||||||
|
// Article 8 et 9: Gestion de la cagnotte
|
||||||
|
if (combinaisonsPayables.isEmpty()) {
|
||||||
|
gains.setMontantCagnotte(gains.getMasseAPartager());
|
||||||
|
gains.setMasseAPartager(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Article 8c: Course annulée ou reportée
|
||||||
|
if (gains.getCourse().isEstAnnulee()) {
|
||||||
|
gains.setMontantCagnotte(gains.getMasseAPartager());
|
||||||
|
gains.setMasseAPartager(0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Classe interne pour représenter une combinaison payable
|
||||||
|
private static class CombinaisonPayable {
|
||||||
|
private Long cheval1Id;
|
||||||
|
private Long cheval2Id;
|
||||||
|
|
||||||
|
public CombinaisonPayable(Long cheval1Id, Long cheval2Id) {
|
||||||
|
this.cheval1Id = cheval1Id;
|
||||||
|
this.cheval2Id = cheval2Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getCheval1Id() { return cheval1Id; }
|
||||||
|
public Long getCheval2Id() { return cheval2Id; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
CombinaisonPayable that = (CombinaisonPayable) o;
|
||||||
|
return (Objects.equals(cheval1Id, that.cheval1Id) &&
|
||||||
|
Objects.equals(cheval2Id, that.cheval2Id)) ||
|
||||||
|
(Objects.equals(cheval1Id, that.cheval2Id) &&
|
||||||
|
Objects.equals(cheval2Id, that.cheval1Id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
// Hash code qui ne dépend pas de l'ordre des chevaux
|
||||||
|
long id1 = Math.min(cheval1Id, cheval2Id);
|
||||||
|
long id2 = Math.max(cheval1Id, cheval2Id);
|
||||||
|
return Objects.hash(id1, id2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Gains obtenirGainsParCourse(Long courseId) {
|
||||||
|
return gainsRepository.findByCourseId(courseId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package com.pmumali.ch2_jumelegagnant.service;
|
||||||
|
|
||||||
|
import com.pmumali.ch2_jumelegagnant.model.PariJumeleGagnant;
|
||||||
|
import com.pmumali.ch2_jumelegagnant.repository.ChevalRepository;
|
||||||
|
import com.pmumali.ch2_jumelegagnant.repository.CourseRepository;
|
||||||
|
import com.pmumali.ch2_jumelegagnant.repository.PariJumeleRepository;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class PariJumeleService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PariJumeleRepository pariRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CourseRepository courseRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ChevalRepository chevalRepository;
|
||||||
|
|
||||||
|
private static final double MISE_DE_BASE = 500.0;
|
||||||
|
private static final double MISE_MAXIMALE = 20 * MISE_DE_BASE;
|
||||||
|
|
||||||
|
public PariJumeleGagnant placerPari(PariJumeleGagnant 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
|
||||||
|
}
|
||||||
|
|
||||||
|
return pariRepository.save(pari);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PariJumeleGagnant> obtenirParisParCourse(Long courseId) {
|
||||||
|
return pariRepository.findByCourseId(courseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PariJumeleGagnant> obtenirParisParCheval(Long chevalId) {
|
||||||
|
return pariRepository.findByCheval1IdOrCheval2Id(chevalId, chevalId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Méthode pour calculer le coût d'une formule combinée (Article 6a)
|
||||||
|
public double calculerCoutFormuleCombinee(int nombreChevauxSelectionnes) {
|
||||||
|
if (nombreChevauxSelectionnes < 2) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int nombreCombinaisons = (nombreChevauxSelectionnes * (nombreChevauxSelectionnes - 1)) / 2;
|
||||||
|
return nombreCombinaisons * MISE_DE_BASE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Méthode pour calculer le coût d'une formule champ (Article 6b)
|
||||||
|
public double calculerCoutFormuleChamp(int nombreChevauxPartants, int nombreChevauxSelectionnes, boolean estChampTotal) {
|
||||||
|
if (estChampTotal) {
|
||||||
|
return (nombreChevauxPartants - 1) * MISE_DE_BASE;
|
||||||
|
} else {
|
||||||
|
return nombreChevauxSelectionnes * MISE_DE_BASE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package com.pmumali.simple.controller;
|
|
||||||
|
|
||||||
import com.pmumali.simple.model.Cheval;
|
|
||||||
import com.pmumali.simple.model.Course;
|
|
||||||
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class CourseController {
|
|
||||||
|
|
||||||
private CourseDetailsDto convertToDetailsDto(Course course) {
|
|
||||||
CourseDetailsDto dto = new CourseDetailsDto();
|
|
||||||
dto.setId(course.getId());
|
|
||||||
dto.setNom(course.getNom());
|
|
||||||
dto.setDateCourse(course.getDateCourse());
|
|
||||||
dto.setChevaux(course.getChevaux().stream()
|
|
||||||
.map(this::convertChevalDto)
|
|
||||||
.collect(Collectors.toList()));
|
|
||||||
return dto;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ChevalDto convertChevalDto(Cheval cheval) {
|
|
||||||
ChevalDto dto = new ChevalDto();
|
|
||||||
dto.setId(cheval.getId());
|
|
||||||
dto.setNom(cheval.getNom());
|
|
||||||
dto.setNonPartant(cheval.isEstNonPartant());
|
|
||||||
return dto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
package com.pmumali.simple.controller;
|
|
||||||
|
|
||||||
import com.pmu.jumele.dto.PariRequest;
|
|
||||||
import com.pmu.mali.model.ResultatCourse;
|
|
||||||
import com.pmumali.simple.dto.PariResponse;
|
|
||||||
import com.pmumali.simple.dto.ResultatCourseDto;
|
|
||||||
import com.pmumali.simple.model.Pari;
|
|
||||||
import com.pmumali.simple.service.PariService;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/api/paris")
|
|
||||||
public class PariController {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private PariService pariService;
|
|
||||||
|
|
||||||
@PostMapping("/jumelé")
|
|
||||||
public ResponseEntity<Pari> placerPari(
|
|
||||||
@RequestBody PariRequest pariRequest) {
|
|
||||||
|
|
||||||
Pari pari = pariService.placerPariJumele(
|
|
||||||
pariRequest.getClientId(),
|
|
||||||
pariRequest.getCourseId(),
|
|
||||||
pariRequest.getChevauxIds(),
|
|
||||||
pariRequest.getMontantMise()
|
|
||||||
);
|
|
||||||
|
|
||||||
return ResponseEntity.ok(pari);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/resultats/{courseId}")
|
|
||||||
public ResponseEntity<ResultatCourse> calculerResultats(
|
|
||||||
@PathVariable Long courseId) {
|
|
||||||
return ResponseEntity.ok(pariService.calculerResultats(courseId));
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/jumele-place")
|
|
||||||
public ResponseEntity<PariResponse> placerPariJumelePlace(
|
|
||||||
@Valid @RequestBody PariRequest pariRequest) {
|
|
||||||
|
|
||||||
Pari pari = pariService.placerPariJumelePlace(pariRequest);
|
|
||||||
return ResponseEntity.ok(convertToResponse(pari));
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/resultats/jumele-place/{courseId}")
|
|
||||||
public ResponseEntity<ResultatCourseDto> getResultatsJumelePlace(
|
|
||||||
@PathVariable Long courseId) {
|
|
||||||
Pari
|
|
||||||
ResultatCourseDto resultat = pariService.calculerResultatsJumelePlace(courseId);
|
|
||||||
return ResponseEntity.ok(resultat);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private PariResponse convertToResponse(Pari pari) {
|
|
||||||
PariResponse response = new PariResponse();
|
|
||||||
response.setId(pari.getId());
|
|
||||||
response.setMontantMise(pari.getMontantMise());
|
|
||||||
response.setDatePari(pari.getDatePari());
|
|
||||||
response.setChevaux(pari.getChevauxJumeles().stream()
|
|
||||||
.map(Cheval::getNom)
|
|
||||||
.collect(Collectors.toList()));
|
|
||||||
response.setCourseNom(pari.getCourse().getNom());
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package com.pmumali.simple.dto;
|
|
||||||
|
|
||||||
import com.pmumali.simple.model.Cheval;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class ChevalDto {
|
|
||||||
private Long id;
|
|
||||||
private String nom;
|
|
||||||
private int numero;
|
|
||||||
private boolean estNonPartant;
|
|
||||||
|
|
||||||
// Constructeurs, Getters, Setters
|
|
||||||
|
|
||||||
public static ChevalDto fromEntity(Cheval cheval) {
|
|
||||||
ChevalDto dto = new ChevalDto();
|
|
||||||
dto.setId(cheval.getId());
|
|
||||||
dto.setNom(cheval.getNom());
|
|
||||||
dto.setNumero(cheval.getNumero());
|
|
||||||
dto.setEstNonPartant(cheval.isEstNonPartant());
|
|
||||||
return dto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package com.pmumali.simple.dto;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class CombinaisonDto {
|
|
||||||
private String cheval1;
|
|
||||||
private String cheval2;
|
|
||||||
private int nombreMises;
|
|
||||||
|
|
||||||
// Getters, setters
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package com.pmumali.simple.dto;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class PariRequest {
|
|
||||||
private Long clientId;
|
|
||||||
private Long courseId;
|
|
||||||
private List<Long> chevauxIds;
|
|
||||||
private double montantMise;
|
|
||||||
|
|
||||||
// Getters, setters
|
|
||||||
}
|
|
||||||
@@ -1,211 +0,0 @@
|
|||||||
package com.pmumali.simple.dto;
|
|
||||||
|
|
||||||
import com.pmumali.simple.model.Cheval;
|
|
||||||
import com.pmumali.simple.model.Pari;
|
|
||||||
import com.pmumali.simple.model.enums.TypePari;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DTO pour la réponse API des paris
|
|
||||||
* Contient toutes les informations à exposer au client
|
|
||||||
*/
|
|
||||||
public class PariResponse {
|
|
||||||
private Long id;
|
|
||||||
private String numeroPari;
|
|
||||||
private TypePari typePari;
|
|
||||||
private double montantMise;
|
|
||||||
|
|
||||||
@JsonFormat(pattern = "dd/MM/yyyy HH:mm:ss")
|
|
||||||
private LocalDateTime datePari;
|
|
||||||
|
|
||||||
private String statut; // EN_COURS, GAGNANT, PERDANT, REMBOURSE
|
|
||||||
private Double gainsPotentiels;
|
|
||||||
private boolean estPaye;
|
|
||||||
|
|
||||||
// Informations course
|
|
||||||
private Long courseId;
|
|
||||||
private String courseNom;
|
|
||||||
@JsonFormat(pattern = "dd/MM/yyyy HH:mm")
|
|
||||||
private LocalDateTime dateCourse;
|
|
||||||
|
|
||||||
// Chevaux sélectionnés
|
|
||||||
private List<ChevalDto> chevaux;
|
|
||||||
|
|
||||||
// Informations client (simplifiées)
|
|
||||||
private String clientNomComplet;
|
|
||||||
private String clientNumero;
|
|
||||||
|
|
||||||
// Constructeurs
|
|
||||||
public PariResponse() {}
|
|
||||||
|
|
||||||
public PariResponse(Long id, String numeroPari, TypePari typePari, double montantMise,
|
|
||||||
LocalDateTime datePari, String statut, Double gainsPotentiels,
|
|
||||||
boolean estPaye, Long courseId, String courseNom,
|
|
||||||
LocalDateTime dateCourse) {
|
|
||||||
this.id = id;
|
|
||||||
this.numeroPari = numeroPari;
|
|
||||||
this.typePari = typePari;
|
|
||||||
this.montantMise = montantMise;
|
|
||||||
this.datePari = datePari;
|
|
||||||
this.statut = statut;
|
|
||||||
this.gainsPotentiels = gainsPotentiels;
|
|
||||||
this.estPaye = estPaye;
|
|
||||||
this.courseId = courseId;
|
|
||||||
this.courseNom = courseNom;
|
|
||||||
this.dateCourse = dateCourse;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Getters et Setters
|
|
||||||
public Long getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setId(Long id) {
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getNumeroPari() {
|
|
||||||
return numeroPari;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setNumeroPari(String numeroPari) {
|
|
||||||
this.numeroPari = numeroPari;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TypePari getTypePari() {
|
|
||||||
return typePari;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTypePari(TypePari typePari) {
|
|
||||||
this.typePari = typePari;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getMontantMise() {
|
|
||||||
return montantMise;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMontantMise(double montantMise) {
|
|
||||||
this.montantMise = montantMise;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LocalDateTime getDatePari() {
|
|
||||||
return datePari;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDatePari(LocalDateTime datePari) {
|
|
||||||
this.datePari = datePari;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getStatut() {
|
|
||||||
return statut;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setStatut(String statut) {
|
|
||||||
this.statut = statut;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Double getGainsPotentiels() {
|
|
||||||
return gainsPotentiels;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setGainsPotentiels(Double gainsPotentiels) {
|
|
||||||
this.gainsPotentiels = gainsPotentiels;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEstPaye() {
|
|
||||||
return estPaye;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEstPaye(boolean estPaye) {
|
|
||||||
this.estPaye = estPaye;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getCourseId() {
|
|
||||||
return courseId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCourseId(Long courseId) {
|
|
||||||
this.courseId = courseId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCourseNom() {
|
|
||||||
return courseNom;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCourseNom(String courseNom) {
|
|
||||||
this.courseNom = courseNom;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LocalDateTime getDateCourse() {
|
|
||||||
return dateCourse;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDateCourse(LocalDateTime dateCourse) {
|
|
||||||
this.dateCourse = dateCourse;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<ChevalDto> getChevaux() {
|
|
||||||
return chevaux;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setChevaux(List<ChevalDto> chevaux) {
|
|
||||||
this.chevaux = chevaux;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getClientNomComplet() {
|
|
||||||
return clientNomComplet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setClientNomComplet(String clientNomComplet) {
|
|
||||||
this.clientNomComplet = clientNomComplet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getClientNumero() {
|
|
||||||
return clientNumero;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setClientNumero(String clientNumero) {
|
|
||||||
this.clientNumero = clientNumero;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Méthode utilitaire de conversion depuis l'entité
|
|
||||||
public static PariResponse fromEntity(Pari pari) {
|
|
||||||
PariResponse response = new PariResponse(
|
|
||||||
pari.getId(),
|
|
||||||
"PMU-" + pari.getId(),
|
|
||||||
pari.getTypePari(),
|
|
||||||
pari.getMontantMise(),
|
|
||||||
pari.getDatePari(),
|
|
||||||
determinerStatut(pari),
|
|
||||||
pari.getGains(),
|
|
||||||
pari.isEstPaye(),
|
|
||||||
pari.getCourse().getId(),
|
|
||||||
pari.getCourse().getNom(),
|
|
||||||
pari.getCourse().getDateCourse()
|
|
||||||
);
|
|
||||||
|
|
||||||
response.setChevaux(pari.getChevauxJumeles().stream()
|
|
||||||
.map(ChevalDto::fromEntity)
|
|
||||||
.collect(Collectors.toList()));
|
|
||||||
|
|
||||||
response.setClientNomComplet(pari.getClient().getPrenom() + " " + pari.getClient().getNom());
|
|
||||||
response.setClientNumero(pari.getClient().getNumeroClient());
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String determinerStatut(Pari pari) {
|
|
||||||
if (pari.getChevauxJumeles().stream().anyMatch(Cheval::isEstNonPartant)) {
|
|
||||||
return "REMBOURSE";
|
|
||||||
}
|
|
||||||
if (pari.isEstPaye()) {
|
|
||||||
return "GAGNANT";
|
|
||||||
}
|
|
||||||
if (pari.getCourse().isTerminee() && !pari.isEstPaye()) {
|
|
||||||
return "PERDANT";
|
|
||||||
}
|
|
||||||
return "EN_COURS";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package com.pmumali.simple.dto;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class ResultatCourseDto {
|
|
||||||
private Long courseId;
|
|
||||||
private List<CombinaisonDto> combinaisonsPayables;
|
|
||||||
private Map<String, Double> rapports;
|
|
||||||
private boolean tirelire;
|
|
||||||
|
|
||||||
// Getters, setters
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
package com.pmumali.simple.exception;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exception métier pour les erreurs spécifiques aux paris PMU Mali
|
|
||||||
* conforme aux règles du "Jumelé Placé"
|
|
||||||
*/
|
|
||||||
public class PariException extends RuntimeException {
|
|
||||||
|
|
||||||
private final ErrorCode errorCode;
|
|
||||||
private final String details;
|
|
||||||
|
|
||||||
// Codes d'erreur standardisés
|
|
||||||
public enum ErrorCode {
|
|
||||||
// Article 1 - Règles de base
|
|
||||||
MISE_MINIMALE_NON_ATTEINTE("La mise minimale est de 500 FCFA"),
|
|
||||||
|
|
||||||
// Article 2 - Limitation des enjeux
|
|
||||||
LIMITE_MISE_DEPASSEE("Limite de mise dépassée (20x500 FCFA maximum)"),
|
|
||||||
|
|
||||||
// Article 3 - Dead Heat
|
|
||||||
CALCUL_RAPPORT_IMPOSSIBLE("Impossible de calculer les rapports pour ce Dead Heat"),
|
|
||||||
|
|
||||||
// Article 4 - Non-partants
|
|
||||||
CHEVAL_NON_PARTANT("Pari invalide : cheval non-partant"),
|
|
||||||
|
|
||||||
// Article 5 - Calcul des rapports
|
|
||||||
RAPPORT_INVALIDE("Rapport calculé invalide (<1.1)"),
|
|
||||||
|
|
||||||
// Article 6 - Formules
|
|
||||||
FORMULE_INVALIDE("Formule de pari invalide"),
|
|
||||||
|
|
||||||
// Article 8 - Cas particuliers
|
|
||||||
COURSE_ANNULEE("Course annulée - paris remboursés"),
|
|
||||||
|
|
||||||
// Validations générales
|
|
||||||
PARI_INVALIDE("Pari invalide"),
|
|
||||||
SOLDE_INSUFFISANT("Solde insuffisant pour placer ce pari"),
|
|
||||||
CLIENT_BLOQUE("Compte client bloqué");
|
|
||||||
|
|
||||||
private final String message;
|
|
||||||
|
|
||||||
ErrorCode(String message) {
|
|
||||||
this.message = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMessage() {
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constructeurs
|
|
||||||
public PariException(ErrorCode errorCode) {
|
|
||||||
super(errorCode.getMessage());
|
|
||||||
this.errorCode = errorCode;
|
|
||||||
this.details = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PariException(ErrorCode errorCode, String details) {
|
|
||||||
super(errorCode.getMessage() + " : " + details);
|
|
||||||
this.errorCode = errorCode;
|
|
||||||
this.details = details;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PariException(ErrorCode errorCode, Throwable cause) {
|
|
||||||
super(errorCode.getMessage(), cause);
|
|
||||||
this.errorCode = errorCode;
|
|
||||||
this.details = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Getters
|
|
||||||
public ErrorCode getErrorCode() {
|
|
||||||
return errorCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDetails() {
|
|
||||||
return details;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Méthode utilitaire pour construire les messages
|
|
||||||
public static String buildLimiteMiseMessage(double limite) {
|
|
||||||
return String.format("Limite de mise dépassée (max %,.0f FCFA par course selon Article 2)", limite);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
package com.pmumali.simple.model;
|
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
public class Cheval {
|
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
private String nom;
|
|
||||||
private int numero;
|
|
||||||
|
|
||||||
private boolean estNonPartant;
|
|
||||||
private Integer positionArrivee;
|
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.LAZY)
|
|
||||||
private Course course;
|
|
||||||
|
|
||||||
|
|
||||||
public int getNumero() {
|
|
||||||
return numero;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setNumero(int numero) {
|
|
||||||
this.numero = numero;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setId(Long id) {
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getNom() {
|
|
||||||
return nom;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setNom(String nom) {
|
|
||||||
this.nom = nom;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEstNonPartant() {
|
|
||||||
return estNonPartant;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEstNonPartant(boolean estNonPartant) {
|
|
||||||
this.estNonPartant = estNonPartant;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getPositionArrivee() {
|
|
||||||
return positionArrivee;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPositionArrivee(Integer positionArrivee) {
|
|
||||||
this.positionArrivee = positionArrivee;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Course getCourse() {
|
|
||||||
return course;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCourse(Course course) {
|
|
||||||
this.course = course;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,287 +0,0 @@
|
|||||||
package com.pmumali.simple.model;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
|
||||||
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
@Table(name = "clients")
|
|
||||||
public class Client {
|
|
||||||
|
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
@Column(nullable = false, length = 50)
|
|
||||||
private String nom;
|
|
||||||
|
|
||||||
@Column(nullable = false, length = 50)
|
|
||||||
private String prenom;
|
|
||||||
|
|
||||||
@Column(nullable = false, unique = true, length = 30)
|
|
||||||
private String numeroClient;
|
|
||||||
|
|
||||||
@Column(nullable = false, unique = true, length = 50)
|
|
||||||
private String email;
|
|
||||||
|
|
||||||
@Column(nullable = false, length = 20)
|
|
||||||
private String telephone;
|
|
||||||
|
|
||||||
@Column(name = "date_naissance")
|
|
||||||
private LocalDate dateNaissance;
|
|
||||||
|
|
||||||
@Column(nullable = false, length = 1)
|
|
||||||
private String sexe; // M ou F
|
|
||||||
|
|
||||||
@Column(name = "piece_identite", nullable = false, length = 50)
|
|
||||||
private String pieceIdentite; // Numéro de CNI/Passeport
|
|
||||||
|
|
||||||
@Column(name = "type_piece", length = 20)
|
|
||||||
private String typePieceIdentite; // CNI, PASSEPORT, PERMIS
|
|
||||||
|
|
||||||
@Column(name = "adresse_postale", length = 100)
|
|
||||||
private String adressePostale;
|
|
||||||
|
|
||||||
@Column(name = "code_postal", length = 10)
|
|
||||||
private String codePostal;
|
|
||||||
|
|
||||||
@Column(length = 50)
|
|
||||||
private String ville;
|
|
||||||
|
|
||||||
@Column(length = 50)
|
|
||||||
private String pays = "Mali";
|
|
||||||
|
|
||||||
@Column(name = "date_inscription", nullable = false)
|
|
||||||
private LocalDate dateInscription = LocalDate.now();
|
|
||||||
|
|
||||||
@Column(name = "est_verifie", nullable = false)
|
|
||||||
private boolean estVerifie = false;
|
|
||||||
|
|
||||||
@Column(name = "est_bloque", nullable = false)
|
|
||||||
private boolean estBloque = false;
|
|
||||||
|
|
||||||
@Column(name = "solde_compte", nullable = false)
|
|
||||||
private double soldeCompte = 0.0;
|
|
||||||
|
|
||||||
@Column(name = "limite_mise", nullable = false)
|
|
||||||
private double limiteMise = 10000.0; // 20 x 500 FCFA (Article 2)
|
|
||||||
|
|
||||||
@OneToMany(mappedBy = "client", cascade = CascadeType.ALL, orphanRemoval = true)
|
|
||||||
private List<Pari> paris = new ArrayList<>();
|
|
||||||
|
|
||||||
@OneToMany(mappedBy = "client", cascade = CascadeType.ALL)
|
|
||||||
private List<Transaction> transactions = new ArrayList<>();
|
|
||||||
|
|
||||||
// Constructeurs
|
|
||||||
public Client() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public Client(String nom, String prenom, String numeroClient, String email, String telephone) {
|
|
||||||
this.nom = nom;
|
|
||||||
this.prenom = prenom;
|
|
||||||
this.numeroClient = numeroClient;
|
|
||||||
this.email = email;
|
|
||||||
this.telephone = telephone;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Getters et Setters
|
|
||||||
public Long getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getNom() {
|
|
||||||
return nom;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setNom(String nom) {
|
|
||||||
this.nom = nom;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPrenom() {
|
|
||||||
return prenom;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPrenom(String prenom) {
|
|
||||||
this.prenom = prenom;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getNumeroClient() {
|
|
||||||
return numeroClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setNumeroClient(String numeroClient) {
|
|
||||||
this.numeroClient = numeroClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getEmail() {
|
|
||||||
return email;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEmail(String email) {
|
|
||||||
this.email = email;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTelephone() {
|
|
||||||
return telephone;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTelephone(String telephone) {
|
|
||||||
this.telephone = telephone;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LocalDate getDateNaissance() {
|
|
||||||
return dateNaissance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDateNaissance(LocalDate dateNaissance) {
|
|
||||||
this.dateNaissance = dateNaissance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSexe() {
|
|
||||||
return sexe;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSexe(String sexe) {
|
|
||||||
this.sexe = sexe;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPieceIdentite() {
|
|
||||||
return pieceIdentite;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPieceIdentite(String pieceIdentite) {
|
|
||||||
this.pieceIdentite = pieceIdentite;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTypePieceIdentite() {
|
|
||||||
return typePieceIdentite;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTypePieceIdentite(String typePieceIdentite) {
|
|
||||||
this.typePieceIdentite = typePieceIdentite;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAdressePostale() {
|
|
||||||
return adressePostale;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAdressePostale(String adressePostale) {
|
|
||||||
this.adressePostale = adressePostale;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCodePostal() {
|
|
||||||
return codePostal;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCodePostal(String codePostal) {
|
|
||||||
this.codePostal = codePostal;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getVille() {
|
|
||||||
return ville;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVille(String ville) {
|
|
||||||
this.ville = ville;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPays() {
|
|
||||||
return pays;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPays(String pays) {
|
|
||||||
this.pays = pays;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LocalDate getDateInscription() {
|
|
||||||
return dateInscription;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDateInscription(LocalDate dateInscription) {
|
|
||||||
this.dateInscription = dateInscription;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEstVerifie() {
|
|
||||||
return estVerifie;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEstVerifie(boolean estVerifie) {
|
|
||||||
this.estVerifie = estVerifie;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEstBloque() {
|
|
||||||
return estBloque;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEstBloque(boolean estBloque) {
|
|
||||||
this.estBloque = estBloque;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getSoldeCompte() {
|
|
||||||
return soldeCompte;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSoldeCompte(double soldeCompte) {
|
|
||||||
this.soldeCompte = soldeCompte;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getLimiteMise() {
|
|
||||||
return limiteMise;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLimiteMise(double limiteMise) {
|
|
||||||
this.limiteMise = limiteMise;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Pari> getParis() {
|
|
||||||
return paris;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setParis(List<Pari> paris) {
|
|
||||||
this.paris = paris;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Transaction> getTransactions() {
|
|
||||||
return transactions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTransactions(List<Transaction> transactions) {
|
|
||||||
this.transactions = transactions;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Méthodes utilitaires
|
|
||||||
public void ajouterPari(Pari pari) {
|
|
||||||
paris.add(pari);
|
|
||||||
pari.setClient(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void retirerPari(Pari pari) {
|
|
||||||
paris.remove(pari);
|
|
||||||
pari.setClient(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void crediterCompte(double montant) {
|
|
||||||
this.soldeCompte += montant;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void debiterCompte(double montant) {
|
|
||||||
if (this.soldeCompte < montant) {
|
|
||||||
throw new IllegalStateException("Solde insuffisant");
|
|
||||||
}
|
|
||||||
this.soldeCompte -= montant;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "Client{" +
|
|
||||||
"id=" + id +
|
|
||||||
", nom='" + nom + '\'' +
|
|
||||||
", prenom='" + prenom + '\'' +
|
|
||||||
", numeroClient='" + numeroClient + '\'' +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
package com.pmumali.simple.model;
|
|
||||||
|
|
||||||
public class Combinaison {
|
|
||||||
private final Cheval cheval1;
|
|
||||||
private final Cheval cheval2;
|
|
||||||
private int nombreMises;
|
|
||||||
|
|
||||||
public Combinaison(Cheval cheval1, Cheval cheval2) {
|
|
||||||
this.cheval1 = cheval1;
|
|
||||||
this.cheval2 = cheval2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Getters
|
|
||||||
public Cheval getCheval1() { return cheval1; }
|
|
||||||
public Cheval getCheval2() { return cheval2; }
|
|
||||||
public int getNombreMises() { return nombreMises; }
|
|
||||||
|
|
||||||
public void setNombreMises(int nombreMises) {
|
|
||||||
this.nombreMises = nombreMises;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (!(o instanceof Combinaison)) return false;
|
|
||||||
Combinaison that = (Combinaison) o;
|
|
||||||
return (cheval1.equals(that.cheval1) && cheval2.equals(that.cheval2)) ||
|
|
||||||
(cheval1.equals(that.cheval2) && cheval2.equals(that.cheval1));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return cheval1.hashCode() + cheval2.hashCode(); // Ordre non important
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
package com.pmumali.simple.model;
|
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
|
|
||||||
public class Course {
|
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
private String nom;
|
|
||||||
private LocalDateTime dateCourse;
|
|
||||||
private boolean estAnnulee;
|
|
||||||
private boolean deadHeat;
|
|
||||||
|
|
||||||
private boolean isTerminee=false;
|
|
||||||
|
|
||||||
public boolean isTerminee() {
|
|
||||||
return isTerminee;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTerminee(boolean terminee) {
|
|
||||||
isTerminee = terminee;
|
|
||||||
}
|
|
||||||
|
|
||||||
@OneToMany(mappedBy = "course", cascade = CascadeType.ALL)
|
|
||||||
private List<Cheval> chevaux = new ArrayList<>();
|
|
||||||
|
|
||||||
@OneToMany(mappedBy = "course")
|
|
||||||
private List<Pari> paris = new ArrayList<>();
|
|
||||||
|
|
||||||
public Long getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setId(Long id) {
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getNom() {
|
|
||||||
return nom;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setNom(String nom) {
|
|
||||||
this.nom = nom;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LocalDateTime getDateCourse() {
|
|
||||||
return dateCourse;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDateCourse(LocalDateTime dateCourse) {
|
|
||||||
this.dateCourse = dateCourse;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEstAnnulee() {
|
|
||||||
return estAnnulee;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEstAnnulee(boolean estAnnulee) {
|
|
||||||
this.estAnnulee = estAnnulee;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isDeadHeat() {
|
|
||||||
return deadHeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDeadHeat(boolean deadHeat) {
|
|
||||||
this.deadHeat = deadHeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Cheval> getChevaux() {
|
|
||||||
return chevaux;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setChevaux(List<Cheval> chevaux) {
|
|
||||||
this.chevaux = chevaux;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Pari> getParis() {
|
|
||||||
return paris;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setParis(List<Pari> paris) {
|
|
||||||
this.paris = paris;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
package com.pmumali.simple.model;
|
|
||||||
|
|
||||||
import com.pmumali.simple.model.enums.TypePari;
|
|
||||||
import jakarta.persistence.*;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
public class Pari {
|
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
private double montantMise;
|
|
||||||
private LocalDateTime datePari;
|
|
||||||
private boolean estPaye;
|
|
||||||
private double gains;
|
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.LAZY)
|
|
||||||
private Client client;
|
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.LAZY)
|
|
||||||
private Course course;
|
|
||||||
|
|
||||||
@ManyToMany
|
|
||||||
@JoinTable(
|
|
||||||
name = "pari_cheval",
|
|
||||||
joinColumns = @JoinColumn(name = "pari_id"),
|
|
||||||
inverseJoinColumns = @JoinColumn(name = "cheval_id"))
|
|
||||||
private List<Cheval> chevauxJumeles = new ArrayList<>();
|
|
||||||
|
|
||||||
public TypePari getTypePari() {
|
|
||||||
return typePari;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTypePari(TypePari typePari) {
|
|
||||||
this.typePari = typePari;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Enumerated(EnumType.STRING)
|
|
||||||
private TypePari typePari; // JUMELEC_GAGNANT ou JUMELEC_PLACE
|
|
||||||
|
|
||||||
public Long getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setId(Long id) {
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getMontantMise() {
|
|
||||||
return montantMise;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMontantMise(double montantMise) {
|
|
||||||
this.montantMise = montantMise;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LocalDateTime getDatePari() {
|
|
||||||
return datePari;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDatePari(LocalDateTime datePari) {
|
|
||||||
this.datePari = datePari;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEstPaye() {
|
|
||||||
return estPaye;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEstPaye(boolean estPaye) {
|
|
||||||
this.estPaye = estPaye;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getGains() {
|
|
||||||
return gains;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setGains(double gains) {
|
|
||||||
this.gains = gains;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Client getClient() {
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setClient(Client client) {
|
|
||||||
this.client = client;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Course getCourse() {
|
|
||||||
return course;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCourse(Course course) {
|
|
||||||
this.course = course;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Cheval> getChevauxJumeles() {
|
|
||||||
return chevauxJumeles;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setChevauxJumeles(List<Cheval> chevauxJumeles) {
|
|
||||||
this.chevauxJumeles = chevauxJumeles;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,192 +0,0 @@
|
|||||||
package com.pmumali.simple.model;
|
|
||||||
|
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
@Table(name = "resultats_courses")
|
|
||||||
public class ResultatCourse {
|
|
||||||
|
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
@OneToOne
|
|
||||||
@JoinColumn(name = "course_id", nullable = false, unique = true)
|
|
||||||
private Course course;
|
|
||||||
|
|
||||||
@Column(name = "date_publication", nullable = false)
|
|
||||||
private LocalDateTime datePublication = LocalDateTime.now();
|
|
||||||
|
|
||||||
@Column(name = "est_officiel", nullable = false)
|
|
||||||
private boolean estOfficiel = false;
|
|
||||||
|
|
||||||
@Column(name = "masse_a_partager", nullable = false)
|
|
||||||
private double masseAPartager;
|
|
||||||
|
|
||||||
@Column(name = "tirelire_active", nullable = false)
|
|
||||||
private boolean tirelireActive = false;
|
|
||||||
|
|
||||||
@Column(name = "montant_tirelire")
|
|
||||||
private Double montantTirelire;
|
|
||||||
|
|
||||||
@ElementCollection
|
|
||||||
@CollectionTable(name = "resultats_combinaisons", joinColumns = @JoinColumn(name = "resultat_id"))
|
|
||||||
@MapKeyColumn(name = "combinaison")
|
|
||||||
@Column(name = "rapport")
|
|
||||||
private Map<String, Double> rapports = new HashMap<>();
|
|
||||||
|
|
||||||
@ElementCollection
|
|
||||||
@CollectionTable(name = "resultats_non_partants", joinColumns = @JoinColumn(name = "resultat_id"))
|
|
||||||
@Column(name = "cheval_id")
|
|
||||||
private Set<Long> chevauxNonPartants = new HashSet<>();
|
|
||||||
|
|
||||||
@Enumerated(EnumType.STRING)
|
|
||||||
@Column(name = "type_resultat", nullable = false)
|
|
||||||
private TypeResultat typeResultat;
|
|
||||||
|
|
||||||
@Version
|
|
||||||
private Long version;
|
|
||||||
|
|
||||||
// Enumération pour les types de résultats
|
|
||||||
public enum TypeResultat {
|
|
||||||
NORMAL,
|
|
||||||
DEAD_HEAT_PREMIERS,
|
|
||||||
DEAD_HEAT_DEUXIEMES,
|
|
||||||
DEAD_HEAT_TROISIEMES,
|
|
||||||
COURSE_ANNULEE
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constructeurs
|
|
||||||
public ResultatCourse() {}
|
|
||||||
|
|
||||||
public ResultatCourse(Course course) {
|
|
||||||
this.course = course;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Getters et Setters
|
|
||||||
public Long getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Course getCourse() {
|
|
||||||
return course;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCourse(Course course) {
|
|
||||||
this.course = course;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LocalDateTime getDatePublication() {
|
|
||||||
return datePublication;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDatePublication(LocalDateTime datePublication) {
|
|
||||||
this.datePublication = datePublication;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEstOfficiel() {
|
|
||||||
return estOfficiel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEstOfficiel(boolean estOfficiel) {
|
|
||||||
this.estOfficiel = estOfficiel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getMasseAPartager() {
|
|
||||||
return masseAPartager;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMasseAPartager(double masseAPartager) {
|
|
||||||
this.masseAPartager = masseAPartager;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isTirelireActive() {
|
|
||||||
return tirelireActive;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTirelireActive(boolean tirelireActive) {
|
|
||||||
this.tirelireActive = tirelireActive;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Double getMontantTirelire() {
|
|
||||||
return montantTirelire;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMontantTirelire(Double montantTirelire) {
|
|
||||||
this.montantTirelire = montantTirelire;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, Double> getRapports() {
|
|
||||||
return rapports;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRapports(Map<String, Double> rapports) {
|
|
||||||
this.rapports = rapports;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<Long> getChevauxNonPartants() {
|
|
||||||
return chevauxNonPartants;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setChevauxNonPartants(Set<Long> chevauxNonPartants) {
|
|
||||||
this.chevauxNonPartants = chevauxNonPartants;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TypeResultat getTypeResultat() {
|
|
||||||
return typeResultat;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTypeResultat(TypeResultat typeResultat) {
|
|
||||||
this.typeResultat = typeResultat;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getVersion() {
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Méthodes métiers
|
|
||||||
public void ajouterRapport(String combinaison, double rapport) {
|
|
||||||
this.rapports.put(combinaison, rapport);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void marquerNonPartant(Long chevalId) {
|
|
||||||
this.chevauxNonPartants.add(chevalId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean estCombinaisonPayable(String combinaison) {
|
|
||||||
return this.rapports.containsKey(combinaison);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void activerTirelire(double montant) {
|
|
||||||
this.tirelireActive = true;
|
|
||||||
this.montantTirelire = montant;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (!(o instanceof ResultatCourse)) return false;
|
|
||||||
ResultatCourse that = (ResultatCourse) o;
|
|
||||||
return Objects.equals(id, that.id) &&
|
|
||||||
Objects.equals(course, that.course);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(id, course);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "ResultatCourse{" +
|
|
||||||
"id=" + id +
|
|
||||||
", course=" + course.getId() +
|
|
||||||
", typeResultat=" + typeResultat +
|
|
||||||
", rapports=" + rapports.size() +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
package com.pmumali.simple.model;
|
|
||||||
|
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
@Table(name = "transactions")
|
|
||||||
public class Transaction {
|
|
||||||
|
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
@Column(nullable = false)
|
|
||||||
private double montant;
|
|
||||||
|
|
||||||
@Column(nullable = false)
|
|
||||||
private LocalDateTime dateHeure = LocalDateTime.now();
|
|
||||||
|
|
||||||
@Column(nullable = false, length = 20)
|
|
||||||
private String type; // DÉPÔT, RETRAIT, GAIN, REMBOURSEMENT
|
|
||||||
|
|
||||||
@Column(length = 100)
|
|
||||||
private String description;
|
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.LAZY)
|
|
||||||
@JoinColumn(name = "client_id")
|
|
||||||
private Client client;
|
|
||||||
|
|
||||||
// Getters, setters et constructeurs
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
package com.pmumali.simple.model.enums;
|
|
||||||
|
|
||||||
public enum TypePari {
|
|
||||||
JUMELEC_GAGNANT,
|
|
||||||
JUMELEC_PLACE
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package com.pmumali.simple.repository;
|
|
||||||
|
|
||||||
import com.pmumali.simple.model.Cheval;
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
|
||||||
|
|
||||||
public interface ChevalRepository extends JpaRepository<Cheval,Long> {
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package com.pmumali.simple.repository;
|
|
||||||
|
|
||||||
import com.pmumali.simple.model.Course;
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
|
||||||
|
|
||||||
public interface CourseRepository extends JpaRepository<Course,Long> {
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
package com.pmumali.simple.repository;
|
|
||||||
|
|
||||||
import com.pmumali.simple.model.Pari;
|
|
||||||
import com.pmumali.simple.model.Client;
|
|
||||||
import com.pmumali.simple.model.Course;
|
|
||||||
import com.pmumali.simple.model.enums.TypePari;
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
|
||||||
import org.springframework.data.jpa.repository.Query;
|
|
||||||
import org.springframework.data.repository.query.Param;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public interface PariRepository extends JpaRepository<Pari, Long> {
|
|
||||||
|
|
||||||
double sumByClientAndCourse(Long clientID, Long courseId);
|
|
||||||
|
|
||||||
// Trouver tous les paris d'un client
|
|
||||||
List<Pari> findByClient(Client client);
|
|
||||||
|
|
||||||
// Trouver les paris par course
|
|
||||||
List<Pari> findByCourse(Course course);
|
|
||||||
|
|
||||||
// Trouver les paris par type (JUMELEC_PLACE ou JUMELEC_GAGNANT)
|
|
||||||
List<Pari> findByTypePari(TypePari typePari);
|
|
||||||
|
|
||||||
// Somme des mises d'un client pour une course (Article 2 - Limitation des enjeux)
|
|
||||||
@Query("SELECT COALESCE(SUM(p.montantMise), 0) FROM Pari p WHERE p.client = :client AND p.course = :course")
|
|
||||||
double sumMisesByClientAndCourse(@Param("client") Client client, @Param("course") Course course);
|
|
||||||
|
|
||||||
// Trouver les paris gagnants pour une combinaison donnée
|
|
||||||
@Query("SELECT p FROM Pari p JOIN p.chevauxJumeles c WHERE p.course = :course " +
|
|
||||||
"AND c.id IN (:cheval1Id, :cheval2Id) GROUP BY p HAVING COUNT(c) = 2")
|
|
||||||
List<Pari> findParisGagnantsForCombinaison(
|
|
||||||
@Param("course") Course course,
|
|
||||||
@Param("cheval1Id") Long cheval1Id,
|
|
||||||
@Param("cheval2Id") Long cheval2Id
|
|
||||||
);
|
|
||||||
|
|
||||||
// Nombre de mises pour une combinaison spécifique
|
|
||||||
@Query("SELECT COUNT(p) FROM Pari p JOIN p.chevauxJumeles c WHERE p.course = :course " +
|
|
||||||
"AND c.id IN (:cheval1Id, :cheval2Id) GROUP BY p HAVING COUNT(c) = 2")
|
|
||||||
long countMisesForCombinaison(
|
|
||||||
@Param("course") Course course,
|
|
||||||
@Param("cheval1Id") Long cheval1Id,
|
|
||||||
@Param("cheval2Id") Long cheval2Id
|
|
||||||
);
|
|
||||||
|
|
||||||
// Trouver les paris contenant un cheval non-partant (Article 4)
|
|
||||||
@Query("SELECT DISTINCT p FROM Pari p JOIN p.chevauxJumeles c WHERE p.course = :course AND c.estNonPartant = true")
|
|
||||||
List<Pari> findParisAvecNonPartants(@Param("course") Course course);
|
|
||||||
|
|
||||||
// Statistiques pour le dashboard
|
|
||||||
@Query("SELECT NEW map(p.typePari as type, COUNT(p) as nombre, SUM(p.montantMise) as totalMises) " +
|
|
||||||
"FROM Pari p WHERE p.datePari BETWEEN :start AND :end GROUP BY p.typePari")
|
|
||||||
List<Map<String, Object>> getStatistiquesParType(
|
|
||||||
@Param("start") LocalDateTime startDate,
|
|
||||||
@Param("end") LocalDateTime endDate
|
|
||||||
);
|
|
||||||
|
|
||||||
// Trouver les paris non payés pour une course
|
|
||||||
List<Pari> findByCourseAndEstPayeFalse(Course course);
|
|
||||||
|
|
||||||
// Méthode optimisée pour le calcul des rapports
|
|
||||||
@Query("SELECT NEW map(c1.id as cheval1Id, c2.id as cheval2Id, COUNT(p) as mises) " +
|
|
||||||
"FROM Pari p JOIN p.chevauxJumeles c1 JOIN p.chevauxJumeles c2 " +
|
|
||||||
"WHERE p.course = :course AND c1.id < c2.id " +
|
|
||||||
"GROUP BY c1.id, c2.id")
|
|
||||||
List<Map<String, Object>> getMisesParCombinaison(@Param("course") Course course);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
package com.pmumali.simple.service;
|
|
||||||
|
|
||||||
import com.pmumali.simple.model.Cheval;
|
|
||||||
import com.pmumali.simple.model.Combinaison;
|
|
||||||
import com.pmumali.simple.model.Course;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class CalculRapportService {
|
|
||||||
|
|
||||||
public Map<Combinaison, Double> calculerRapports(
|
|
||||||
List<Combinaison> combinaisonsPayables,
|
|
||||||
double masseAPartager) {
|
|
||||||
|
|
||||||
Map<Combinaison, Double> rapports = new HashMap<>();
|
|
||||||
|
|
||||||
if (combinaisonsPayables.size() == 1) {
|
|
||||||
// Cas arrivée normale
|
|
||||||
double rapport = masseAPartager / combinaisonsPayables.get(0).getNombreMises();
|
|
||||||
rapports.put(combinaisonsPayables.get(0), Math.max(1.1, rapport));
|
|
||||||
} else {
|
|
||||||
// Cas Dead-Heat
|
|
||||||
double beneficeParCombinaison = masseAPartager / combinaisonsPayables.size();
|
|
||||||
|
|
||||||
for (Combinaison combinaison : combinaisonsPayables) {
|
|
||||||
double rapport = beneficeParCombinaison / combinaison.getNombreMises();
|
|
||||||
rapports.put(combinaison, Math.max(1.1, rapport));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rapports;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Vérifie si une combinaison est gagnante selon les positions d'arrivée
|
|
||||||
*/
|
|
||||||
public boolean isCombinaisonGagnante(Combinaison combinaison, List<Cheval> classement) {
|
|
||||||
if (classement.size() < 2) return false;
|
|
||||||
|
|
||||||
Cheval c1 = combinaison.getCheval1();
|
|
||||||
Cheval c2 = combinaison.getCheval2();
|
|
||||||
|
|
||||||
// Les deux chevaux doivent être dans les 2 premiers (ordre quelconque)
|
|
||||||
return classement.subList(0, 2).contains(c1) &&
|
|
||||||
classement.subList(0, 2).contains(c2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calcule le nombre de mises pour une combinaison donnée
|
|
||||||
*/
|
|
||||||
public long compterMisesCombinaison(Course course, Combinaison combinaison) {
|
|
||||||
return course.getParis().stream()
|
|
||||||
.filter(p -> p.getChevauxJumeles().containsAll(
|
|
||||||
List.of(combinaison.getCheval1(), combinaison.getCheval2())
|
|
||||||
))
|
|
||||||
.count();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Map<Combinaison, Double> calculerRapportsJumelePlace(
|
|
||||||
List<Combinaison> combinaisonsPayables,
|
|
||||||
double masseAPartager) {
|
|
||||||
|
|
||||||
Map<Combinaison, Double> rapports = new HashMap<>();
|
|
||||||
|
|
||||||
if (combinaisonsPayables.isEmpty()) {
|
|
||||||
return rapports;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Article 5a: Arrivée normale - division en 3 parts égales
|
|
||||||
if (!combinaisonsPayables.get(0).getCourse().isDeadHeat()) {
|
|
||||||
double part = masseAPartager / 3;
|
|
||||||
|
|
||||||
for (Combinaison combinaison : combinaisonsPayables) {
|
|
||||||
double rapport = part / compterMisesCombinaison(combinaison);
|
|
||||||
rapports.put(combinaison, Math.max(RAPPORT_MINIMUM, rapport));
|
|
||||||
}
|
|
||||||
|
|
||||||
return rapports;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Article 5b: Dead-Heat
|
|
||||||
Course course = combinaisonsPayables.get(0).getCourse();
|
|
||||||
Map<Integer, List<Cheval>> parPosition = course.getChevaux().stream()
|
|
||||||
.filter(c -> !c.isEstNonPartant())
|
|
||||||
.collect(Collectors.groupingBy(Cheval::getPositionArrivee));
|
|
||||||
|
|
||||||
List<Cheval> premiers = parPosition.getOrDefault(1, Collections.emptyList());
|
|
||||||
List<Cheval> deuxiemes = parPosition.getOrDefault(2, Collections.emptyList());
|
|
||||||
List<Cheval> troisiemes = parPosition.getOrDefault(3, Collections.emptyList());
|
|
||||||
|
|
||||||
// Article 5b1: Dead-Heat 3+ premiers
|
|
||||||
if (premiers.size() >= 3) {
|
|
||||||
double part = masseAPartager / combinaisonsPayables.size();
|
|
||||||
for (Combinaison combinaison : combinaisonsPayables) {
|
|
||||||
double rapport = part / compterMisesCombinaison(combinaison);
|
|
||||||
rapports.put(combinaison, Math.max(RAPPORT_MINIMUM, rapport));
|
|
||||||
}
|
|
||||||
return rapports;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Article 5b2: Dead-Heat 2 premiers + 1+ troisième
|
|
||||||
if (premiers.size() == 2 && !troisiemes.isEmpty()) {
|
|
||||||
// Répartition en 3 tiers
|
|
||||||
double tiers = masseAPartager / 3;
|
|
||||||
|
|
||||||
// 1er tiers: combinaison des 2 premiers
|
|
||||||
Combinaison combinaisonPremiers = new Combinaison(premiers.get(0), premiers.get(1));
|
|
||||||
double rapportPremiers = tiers / compterMisesCombinaison(combinaisonPremiers);
|
|
||||||
rapports.put(combinaisonPremiers, Math.max(RAPPORT_MINIMUM, rapportPremiers));
|
|
||||||
|
|
||||||
// 2ème tiers: premier1 avec troisièmes
|
|
||||||
double partParCombinaison = tiers / troisiemes.size();
|
|
||||||
for (Cheval troisieme : troisiemes) {
|
|
||||||
Combinaison combinaison = new Combinaison(premiers.get(0), troisieme);
|
|
||||||
double rapport = partParCombinaison / compterMisesCombinaison(combinaison);
|
|
||||||
rapports.put(combinaison, Math.max(RAPPORT_MINIMUM, rapport));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3ème tiers: premier2 avec troisièmes
|
|
||||||
for (Cheval troisieme : troisiemes) {
|
|
||||||
Combinaison combinaison = new Combinaison(premiers.get(1), troisieme);
|
|
||||||
double rapport = partParCombinaison / compterMisesCombinaison(combinaison);
|
|
||||||
rapports.put(combinaison, Math.max(RAPPORT_MINIMUM, rapport));
|
|
||||||
}
|
|
||||||
|
|
||||||
return rapports;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ... autres cas Dead-Heat (implémenter de manière similaire)
|
|
||||||
|
|
||||||
return rapports;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package com.pmumali.simple.service;
|
|
||||||
|
|
||||||
import com.pmumali.simple.model.Cheval;
|
|
||||||
import com.pmumali.simple.model.Combinaison;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
public class FormulaireService {
|
|
||||||
|
|
||||||
public double calculerCoutFormule(int nombreChevaux, boolean formuleComplete) {
|
|
||||||
// Article 7: Tableaux des combinaisons
|
|
||||||
int nbCombinaisons = formuleComplete ?
|
|
||||||
nombreChevaux * (nombreChevaux - 1) / 2 :
|
|
||||||
nombreChevaux;
|
|
||||||
|
|
||||||
return nbCombinaisons * 500; // 500 FCFA par combinaison
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Combinaison> genererCombinaisonsFormule(
|
|
||||||
List<Cheval> chevauxSelectionnes,
|
|
||||||
boolean formuleComplete) {
|
|
||||||
|
|
||||||
List<Combinaison> combinaisons = new ArrayList<>();
|
|
||||||
|
|
||||||
if (formuleComplete) {
|
|
||||||
// Toutes les combinaisons 2 à 2
|
|
||||||
for (int i = 0; i < chevauxSelectionnes.size(); i++) {
|
|
||||||
for (int j = i + 1; j < chevauxSelectionnes.size(); j++) {
|
|
||||||
combinaisons.add(new Combinaison(
|
|
||||||
chevauxSelectionnes.get(i),
|
|
||||||
chevauxSelectionnes.get(j)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Formule simplifiée (champ total/partiel)
|
|
||||||
Cheval base = chevauxSelectionnes.get(0);
|
|
||||||
for (int i = 1; i < chevauxSelectionnes.size(); i++) {
|
|
||||||
combinaisons.add(new Combinaison(base, chevauxSelectionnes.get(i)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return combinaisons;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,309 +0,0 @@
|
|||||||
package com.pmumali.simple.service;
|
|
||||||
|
|
||||||
import com.pmumali.simple.dto.CombinaisonDto;
|
|
||||||
import com.pmumali.model.*;
|
|
||||||
import com.pmumali.simple.repository.ChevalRepository;
|
|
||||||
import com.pmumali.simple.repository.CourseRepository;
|
|
||||||
import com.pmumali.simple.repository.PariRepository;
|
|
||||||
import com.pmumali.simple.model.*;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
public class PariService {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private PariRepository pariRepository;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private CourseRepository courseRepository;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ChevalRepository chevalRepository;
|
|
||||||
|
|
||||||
|
|
||||||
public Pari placerPariJumele(Long clientId, Long courseId,
|
|
||||||
List<Long> chevauxIds, double montant) {
|
|
||||||
// Vérification mise minimum
|
|
||||||
if (montant < 500) {
|
|
||||||
throw new IllegalArgumentException("La mise minimale est de 500 FCFA");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vérification limite de paris (20x mise min = 10 000 FCFA)
|
|
||||||
double totalParisClient = pariRepository
|
|
||||||
.sumByClientAndCourse(clientId, courseId);
|
|
||||||
|
|
||||||
if (totalParisClient + montant > 10000) {
|
|
||||||
throw new IllegalArgumentException("Limite de mise dépassée");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Création du pari
|
|
||||||
Pari pari = new Pari();
|
|
||||||
// ... initialisation
|
|
||||||
|
|
||||||
return pariRepository.save(pari);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ResultatCourse calculerResultats(Long courseId) throws Exception {
|
|
||||||
Course course = courseRepository.findById(courseId)
|
|
||||||
.orElseThrow(() -> new Exception("Course non trouvée"));
|
|
||||||
|
|
||||||
// Implémentation des règles de calcul
|
|
||||||
return calculerResultatsSelonReglement(course);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ResultatCourse calculerResultatsSelonReglement(Course course) {
|
|
||||||
ResultatCourse resultat = new ResultatCourse();
|
|
||||||
|
|
||||||
// 1. Vérifier les non-partants (Article 4)
|
|
||||||
List<Cheval> nonPartants = course.getChevaux().stream()
|
|
||||||
.filter(Cheval::isEstNonPartant)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
// 2. Calcul selon Dead-Heat (Article 3)
|
|
||||||
if (course.isDeadHeat()) {
|
|
||||||
calculerDeadHeat(resultat, course);
|
|
||||||
} else {
|
|
||||||
calculerArriveeNormale(resultat, course);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Appliquer tirelire si nécessaire (Article 9)
|
|
||||||
if (resultat.getCombinaisonsPayables().isEmpty()) {
|
|
||||||
resultat.setTirelire(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultat;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Détermine les combinaisons payables selon les règles du PMU Mali
|
|
||||||
*/
|
|
||||||
private List<Combinaison> determinerCombinaisonsPayables(Course course) {
|
|
||||||
List<Cheval> chevauxArrives = course.getChevaux().stream()
|
|
||||||
.filter(c -> !c.isEstNonPartant())
|
|
||||||
.sorted(Comparator.comparingInt(Cheval::getPositionArrivee))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
// Article 4: Remboursement si non-partant
|
|
||||||
if (course.getChevaux().stream().anyMatch(Cheval::isEstNonPartant)) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Article 3: Gestion Dead-Heat
|
|
||||||
if (course.isDeadHeat()) {
|
|
||||||
return calculerCombinaisonsDeadHeat(chevauxArrives);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Article 5: Arrivée normale
|
|
||||||
return calculerCombinaisonsNormales(chevauxArrives);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implémentation de l'article 3 - Cas Dead-Heat
|
|
||||||
*/
|
|
||||||
private List<Combinaison> calculerCombinaisonsDeadHeat(List<Cheval> chevauxArrives) {
|
|
||||||
List<Combinaison> combinaisons = new ArrayList<>();
|
|
||||||
|
|
||||||
// Groupes par position d'arrivée
|
|
||||||
Map<Integer, List<Cheval>> parPosition = chevauxArrives.stream()
|
|
||||||
.collect(Collectors.groupingBy(Cheval::getPositionArrivee));
|
|
||||||
|
|
||||||
List<Cheval> premiers = parPosition.getOrDefault(1, Collections.emptyList());
|
|
||||||
List<Cheval> deuxiemes = parPosition.getOrDefault(2, Collections.emptyList());
|
|
||||||
|
|
||||||
// Cas Dead-Heat premier place (Article 3a)
|
|
||||||
if (premiers.size() >= 2) {
|
|
||||||
for (int i = 0; i < premiers.size(); i++) {
|
|
||||||
for (int j = i + 1; j < premiers.size(); j++) {
|
|
||||||
combinaisons.add(new Combinaison(premiers.get(i), premiers.get(j)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Cas Dead-Heat deuxième place (Article 3b)
|
|
||||||
else if (deuxiemes.size() >= 2) {
|
|
||||||
Cheval premier = premiers.get(0);
|
|
||||||
for (Cheval deuxieme : deuxiemes) {
|
|
||||||
combinaisons.add(new Combinaison(premier, deuxieme));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return combinaisons;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implémentation de l'article 5 - Arrivée normale
|
|
||||||
*/
|
|
||||||
private List<Combinaison> calculerCombinaisonsNormales(List<Cheval> chevauxArrives) {
|
|
||||||
if (chevauxArrives.size() < 2) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
Cheval premier = chevauxArrives.get(0);
|
|
||||||
Cheval deuxieme = chevauxArrives.get(1);
|
|
||||||
|
|
||||||
return List.of(new Combinaison(premier, deuxieme));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calcule la masse à partager selon l'article 5
|
|
||||||
*/
|
|
||||||
private double calculerMasseAPartager(Course course, List<Combinaison> combinaisonsPayables) {
|
|
||||||
double totalEnjeux = course.getParis().stream()
|
|
||||||
.mapToDouble(Pari::getMontantMise)
|
|
||||||
.sum();
|
|
||||||
|
|
||||||
double montantRembourse = course.getParis().stream()
|
|
||||||
.filter(p -> p.getChevauxJumeles().stream().anyMatch(Cheval::isEstNonPartant))
|
|
||||||
.mapToDouble(Pari::getMontantMise)
|
|
||||||
.sum();
|
|
||||||
|
|
||||||
// Article 5: MAP = RNET - MREMB - PRELEV
|
|
||||||
double prelevementsLegaux = totalEnjeux * 0.15; // Exemple: 15% de prélèvement
|
|
||||||
return totalEnjeux - montantRembourse - prelevementsLegaux;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convertit les combinaisons en DTO pour la réponse API
|
|
||||||
*/
|
|
||||||
private List<CombinaisonDto> convertToDto(List<Combinaison> combinaisons) {
|
|
||||||
return combinaisons.stream()
|
|
||||||
.map(c -> new CombinaisonDto(
|
|
||||||
c.getCheval1().getNom(),
|
|
||||||
c.getCheval2().getNom(),
|
|
||||||
c.getNombreMises()
|
|
||||||
))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convertit les rapports en format lisible
|
|
||||||
*/
|
|
||||||
private Map<String, Double> convertRapports(Map<Combinaison, Double> rapports) {
|
|
||||||
return rapports.entrySet().stream()
|
|
||||||
.collect(Collectors.toMap(
|
|
||||||
e -> e.getKey().getCheval1().getNom() + "-" + e.getKey().getCheval2().getNom(),
|
|
||||||
Map.Entry::getValue
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Combinaison> determinerCombinaisonsPayablesJumelePlace(Course course) {
|
|
||||||
List<Cheval> chevauxArrives = course.getChevaux().stream()
|
|
||||||
.filter(c -> !c.isEstNonPartant())
|
|
||||||
.sorted(Comparator.comparingInt(Cheval::getPositionArrivee))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
// Article 4: Remboursement si non-partant
|
|
||||||
if (course.getChevaux().stream().anyMatch(Cheval::isEstNonPartant)) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Article 8: Moins de 3 chevaux arrivés
|
|
||||||
if (chevauxArrives.size() < 3) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Article 3: Gestion Dead-Heat
|
|
||||||
if (course.isDeadHeat()) {
|
|
||||||
return calculerCombinaisonsDeadHeatJumelePlace(chevauxArrives);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cas normal - Article 1
|
|
||||||
return calculerCombinaisonsNormalesJumelePlace(chevauxArrives);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Combinaison> calculerCombinaisonsNormalesJumelePlace(List<Cheval> chevauxArrives) {
|
|
||||||
List<Combinaison> combinaisons = new ArrayList<>();
|
|
||||||
Cheval premier = chevauxArrives.get(0);
|
|
||||||
Cheval deuxieme = chevauxArrives.get(1);
|
|
||||||
Cheval troisieme = chevauxArrives.get(2);
|
|
||||||
|
|
||||||
// Toutes les combinaisons 2 parmi 3
|
|
||||||
combinaisons.add(new Combinaison(premier, deuxieme));
|
|
||||||
combinaisons.add(new Combinaison(premier, troisieme));
|
|
||||||
combinaisons.add(new Combinaison(deuxieme, troisieme));
|
|
||||||
|
|
||||||
return combinaisons;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Combinaison> calculerCombinaisonsDeadHeatJumelePlace(List<Cheval> chevauxArrives) {
|
|
||||||
List<Combinaison> combinaisons = new ArrayList<>();
|
|
||||||
Map<Integer, List<Cheval>> parPosition = chevauxArrives.stream()
|
|
||||||
.collect(Collectors.groupingBy(Cheval::getPositionArrivee));
|
|
||||||
|
|
||||||
List<Cheval> premiers = parPosition.getOrDefault(1, Collections.emptyList());
|
|
||||||
List<Cheval> deuxiemes = parPosition.getOrDefault(2, Collections.emptyList());
|
|
||||||
List<Cheval> troisiemes = parPosition.getOrDefault(3, Collections.emptyList());
|
|
||||||
|
|
||||||
// Article 3a: Dead-Heat à la première place (3+ chevaux)
|
|
||||||
if (premiers.size() >= 3) {
|
|
||||||
for (int i = 0; i < premiers.size(); i++) {
|
|
||||||
for (int j = i + 1; j < premiers.size(); j++) {
|
|
||||||
combinaisons.add(new Combinaison(premiers.get(i), premiers.get(j)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return combinaisons;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Article 3b: Dead-Heat 2 premiers + 1+ troisième
|
|
||||||
if (premiers.size() == 2 && !troisiemes.isEmpty()) {
|
|
||||||
// Combinaison des deux premiers
|
|
||||||
combinaisons.add(new Combinaison(premiers.get(0), premiers.get(1)));
|
|
||||||
|
|
||||||
// Combinaisons premier1 avec troisièmes
|
|
||||||
for (Cheval troisieme : troisiemes) {
|
|
||||||
combinaisons.add(new Combinaison(premiers.get(0), troisieme));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Combinaisons premier2 avec troisièmes
|
|
||||||
for (Cheval troisieme : troisiemes) {
|
|
||||||
combinaisons.add(new Combinaison(premiers.get(1), troisieme));
|
|
||||||
}
|
|
||||||
return combinaisons;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Article 3c: Dead-Heat à la deuxième place
|
|
||||||
if (!deuxiemes.isEmpty() && deuxiemes.size() >= 2) {
|
|
||||||
Cheval premier = premiers.get(0);
|
|
||||||
|
|
||||||
// Combinaisons premier avec deuxièmes
|
|
||||||
for (Cheval deuxieme : deuxiemes) {
|
|
||||||
combinaisons.add(new Combinaison(premier, deuxieme));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Combinaisons deuxièmes entre eux
|
|
||||||
for (int i = 0; i < deuxiemes.size(); i++) {
|
|
||||||
for (int j = i + 1; j < deuxiemes.size(); j++) {
|
|
||||||
combinaisons.add(new Combinaison(deuxiemes.get(i), deuxiemes.get(j)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return combinaisons;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Article 3d: Dead-Heat à la troisième place
|
|
||||||
if (!troisiemes.isEmpty() && troisiemes.size() >= 2) {
|
|
||||||
Cheval premier = premiers.get(0);
|
|
||||||
Cheval deuxieme = deuxiemes.get(0);
|
|
||||||
|
|
||||||
// Combinaison premier-deuxième
|
|
||||||
combinaisons.add(new Combinaison(premier, deuxieme));
|
|
||||||
|
|
||||||
// Combinaisons premier avec troisièmes
|
|
||||||
for (Cheval troisieme : troisiemes) {
|
|
||||||
combinaisons.add(new Combinaison(premier, troisieme));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Combinaisons deuxième avec troisièmes
|
|
||||||
for (Cheval troisieme : troisiemes) {
|
|
||||||
combinaisons.add(new Combinaison(deuxieme, troisieme));
|
|
||||||
}
|
|
||||||
return combinaisons;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
package com.pmumali.simple.service;
|
|
||||||
|
|
||||||
import com.pmumali.simple.model.Cheval;
|
|
||||||
import com.pmumali.simple.model.Combinaison;
|
|
||||||
import com.pmumali.simple.model.Course;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class PariServiceTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCombinaisonsPayables_Normal() {
|
|
||||||
Course course = new Course();
|
|
||||||
course.setDeadHeat(false);
|
|
||||||
|
|
||||||
Cheval c1 = new Cheval(); c1.setPositionArrivee(1);
|
|
||||||
Cheval c2 = new Cheval(); c2.setPositionArrivee(2);
|
|
||||||
Cheval c3 = new Cheval(); c3.setPositionArrivee(3);
|
|
||||||
course.setChevaux(List.of(c1, c2, c3));
|
|
||||||
|
|
||||||
List<Combinaison> result = pariService.determinerCombinaisonsPayablesJumelePlace(course);
|
|
||||||
|
|
||||||
assertEquals(3, result.size());
|
|
||||||
assertTrue(result.contains(new Combinaison(c1, c2)));
|
|
||||||
assertTrue(result.contains(new Combinaison(c1, c3)));
|
|
||||||
assertTrue(result.contains(new Combinaison(c2, c3)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDeadHeat_TroisPremiers() {
|
|
||||||
Course course = new Course();
|
|
||||||
course.setDeadHeat(true);
|
|
||||||
|
|
||||||
Cheval c1 = new Cheval(); c1.setPositionArrivee(1);
|
|
||||||
Cheval c2 = new Cheval(); c2.setPositionArrivee(1);
|
|
||||||
Cheval c3 = new Cheval(); c3.setPositionArrivee(1);
|
|
||||||
course.setChevaux(List.of(c1, c2, c3));
|
|
||||||
|
|
||||||
List<Combinaison> result = pariService.determinerCombinaisonsPayablesJumelePlace(course);
|
|
||||||
|
|
||||||
assertEquals(3, result.size()); // C(3,2) = 3 combinaisons
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
package com.pmumali.simple.service;
|
|
||||||
|
|
||||||
import com.pmumali.simple.dto.PariRequest;
|
|
||||||
import com.pmumali.simple.exception.PariException;
|
|
||||||
import com.pmumali.simple.model.Cheval;
|
|
||||||
import com.pmumali.simple.model.Client;
|
|
||||||
import com.pmumali.simple.model.Course;
|
|
||||||
import com.pmumali.simple.model.Pari;
|
|
||||||
import com.pmumali.simple.model.enums.TypePari;
|
|
||||||
import com.pmumali.simple.repository.PariRepository;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
public class ValidationPariService {
|
|
||||||
@Autowired
|
|
||||||
private PariRepository pariRepository;
|
|
||||||
|
|
||||||
public void validerPari(Pari pari) {
|
|
||||||
// Article 2: Limitation des enjeux
|
|
||||||
validerLimiteEnjeux(pari);
|
|
||||||
|
|
||||||
// Article 4: Chevaux non-partants
|
|
||||||
validerChevauxPartants(pari.getCourse(), pari.getChevauxJumeles());
|
|
||||||
|
|
||||||
// Article 6: Formules combinées
|
|
||||||
validerFormule(pari);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void validerLimiteEnjeux(Pari pari) {
|
|
||||||
double totalMises = pariRepository
|
|
||||||
.sumByClientAndCourse(pari.getClient().getId(), pari.getCourse().getId());
|
|
||||||
|
|
||||||
if (totalMises + pari.getMontantMise() > 10000) {
|
|
||||||
throw new PariException(PariException.ErrorCode.LIMITE_MISE_DEPASSEE, "Limite de mise dépassée (20x500 FCFA maximum)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Valide la requête de pari selon les règles métier
|
|
||||||
*/
|
|
||||||
public void validerPariRequest(PariRequest request) {
|
|
||||||
if (request.getMontantMise() < 500) {
|
|
||||||
throw new PariException(PariException.ErrorCode.MISE_MINIMALE_NON_ATTEINTE,"La mise minimale est de 500 FCFA (Article 1)");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.getChevauxIds() == null || request.getChevauxIds().size() != 2) {
|
|
||||||
throw new PariException(PariException.ErrorCode.PARI_INVALIDE,"Un pari Jumelé Gagnant doit porter sur exactement 2 chevaux");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Valide le pari selon toutes les règles du règlement
|
|
||||||
*/
|
|
||||||
public void validerPari(Client client, Course course, List<Cheval> chevaux, double montantMise) {
|
|
||||||
// Article 2: Limitation des enjeux
|
|
||||||
validerLimiteEnjeux(client, course, montantMise);
|
|
||||||
|
|
||||||
// Article 4: Chevaux non-partants
|
|
||||||
validerChevauxPartants(course, chevaux);
|
|
||||||
|
|
||||||
// Article 10: Course non annulée
|
|
||||||
if (course.isEstAnnulee()) {
|
|
||||||
throw new PariException(PariException.ErrorCode.COURSE_ANNULEE,"Course annulée - tous les paris seront remboursés (Article 8)");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vérifie que les chevaux appartiennent bien à la course
|
|
||||||
if (chevaux.stream().anyMatch(c -> !c.getCourse().equals(course))) {
|
|
||||||
throw new PariException(PariException.ErrorCode.CHEVAL_NON_PARTANT,"Un ou plusieurs chevaux ne font pas partie de cette course");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implémentation de l'article 2 - Limitation des enjeux
|
|
||||||
*/
|
|
||||||
private void validerLimiteEnjeux(Client client, Course course, double nouvelleMise) {
|
|
||||||
double totalMises = pariRepository.sumByClientAndCourse(client.getId(), course.getId());
|
|
||||||
double limite = 20 * 500; // 20 fois la mise de base (500 FCFA)
|
|
||||||
|
|
||||||
if (totalMises + nouvelleMise > limite) {
|
|
||||||
throw new PariException( PariException.ErrorCode.LIMITE_MISE_DEPASSEE,
|
|
||||||
String.format("Limite de mise dépassée (max %,.0f FCFA par course selon Article 2)", limite)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implémentation de l'article 4 - Chevaux non-partants
|
|
||||||
*/
|
|
||||||
private void validerChevauxPartants(Course course, List<Cheval> chevaux) {
|
|
||||||
if (chevaux.stream().anyMatch(Cheval::isEstNonPartant)) {
|
|
||||||
throw new PariException(PariException.ErrorCode.CHEVAL_NON_PARTANT,
|
|
||||||
"Pari non valide : un ou plusieurs chevaux sont non-partants (Article 4)"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void validerPariJumelePlace(Pari pari) {
|
|
||||||
// Article 1: Vérification que c'est bien un Jumelé Placé
|
|
||||||
if (pari.getTypePari() != TypePari.JUMELEC_PLACE) {
|
|
||||||
throw new PariException(PariException.ErrorCode.FORMULE_INVALIDE ,"Ce n'est pas un pari Jumelé Placé");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Article 2: Limitation des enjeux (identique)
|
|
||||||
validerLimiteEnjeux(pari.getClient(), pari.getCourse(), pari.getMontantMise());
|
|
||||||
|
|
||||||
// Article 4: Chevaux non-partants
|
|
||||||
if (pari.getChevauxJumeles().stream().anyMatch(Cheval::isEstNonPartant)) {
|
|
||||||
throw new PariException(PariException.ErrorCode.CHEVAL_NON_PARTANT,"Pari non valide: cheval non-partant (Article 4)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user