Implement ranking logic with SerieStandEntry, add support for streak results and binding types (Reiter+Pferd, Reiter, Pferd), update UI for detailed ranking display, and finalize Phase 10.

This commit is contained in:
2026-04-12 17:03:06 +02:00
parent 6e99bc97fd
commit a79e612693
8 changed files with 112 additions and 16 deletions
@@ -1,5 +1,7 @@
package at.mocode.series.service.application
import at.mocode.series.service.domain.Bindungstyp
import at.mocode.series.service.domain.ReglementTyp
import at.mocode.series.service.domain.Serie
import at.mocode.series.service.domain.SeriePunkt
import at.mocode.series.service.persistence.JpaSeriePunktRepository
@@ -20,17 +22,48 @@ class SeriesService(
@Transactional
fun saveSerie(serie: Serie): Serie = serieRepository.save(serie)
fun getStand(serieId: String): Map<Pair<String, String>, Double> {
fun getStand(serieId: String): List<SerieStandEntry> {
val serie = getSeriesById(serieId) ?: return emptyList()
val punkte = punkteRepository.findBySerieId(serieId)
// Aggregation pro Paar (Reiter, Pferd)
return punkte.groupBy { it.reiterId to it.pferdId }
.mapValues { (_, v) -> v.sumOf { it.punkte } }
.toList()
.sortedByDescending { it.second }
.toMap()
// Gruppierung nach Bindungstyp
val groupedPunkte = when (serie.bindungstyp) {
Bindungstyp.PAAR_BINDUNG -> punkte.groupBy { "${it.reiterId}_${it.pferdId}" }
Bindungstyp.NUR_REITER -> punkte.groupBy { it.reiterId }
Bindungstyp.NUR_PFERD -> punkte.groupBy { it.pferdId }
}
return groupedPunkte.map { (_, v) ->
val bewerbPunkte = v.map { it.punkte }.sortedDescending()
val gesamtPunkte = when (serie.reglementTyp) {
ReglementTyp.ALLES_ZAEHLT -> bewerbPunkte.sum()
ReglementTyp.STREICHER_NORMAL -> {
// Berücksichtige Streichresultate: N-m gewertete Resultate (die besten N-m zählen)
// Wenn wir m Streichresultate haben, zählen die besten (Total - m) Resultate.
val countToSum = (bewerbPunkte.size - serie.streichresultateCount).coerceAtLeast(0)
if (countToSum == 0 && bewerbPunkte.isNotEmpty()) bewerbPunkte.first() // Fallback: Zumindest eines zählt
else bewerbPunkte.take(countToSum).sum()
}
ReglementTyp.MEISTERSCHAFT -> bewerbPunkte.sum() // TODO: Spezielle Gewichtung
}
SerieStandEntry(
reiterId = v.first().reiterId,
pferdId = if (serie.bindungstyp == Bindungstyp.PAAR_BINDUNG) v.first().pferdId else null,
punkte = gesamtPunkte,
anzahlWertungen = v.size
)
}.sortedByDescending { it.punkte }
}
@Transactional
fun addPunkt(punkt: SeriePunkt): SeriePunkt = punkteRepository.save(punkt)
}
data class SerieStandEntry(
val reiterId: String,
val pferdId: String?,
val punkte: Double,
val anzahlWertungen: Int
)
@@ -19,6 +19,13 @@ class Serie(
@Column(nullable = false)
val reglementTyp: ReglementTyp = ReglementTyp.STREICHER_NORMAL,
@Column(nullable = false)
val streichresultateCount: Int = 1,
@Enumerated(EnumType.STRING)
@Column(nullable = false)
val bindungstyp: Bindungstyp = Bindungstyp.PAAR_BINDUNG,
@ElementCollection
@CollectionTable(name = "serie_bewerbe", joinColumns = [JoinColumn(name = "serie_id")])
@Column(name = "bewerb_id")
@@ -31,6 +38,12 @@ enum class ReglementTyp {
MEISTERSCHAFT // Spezielle Gewichtung (z.B. Finale doppelt)
}
enum class Bindungstyp {
PAAR_BINDUNG, // Reiter + Pferd (Standard)
NUR_REITER, // Punkte zählen nur für den Reiter
NUR_PFERD // Punkte zählen nur für das Pferd
}
@Entity
@Table(name = "serie_punkte")
class SeriePunkt(