feat: add classement match system

This commit is contained in:
2026-02-05 21:29:27 +01:00
parent e2197d0712
commit 89d9e04a6f
24 changed files with 678 additions and 127 deletions

View File

@@ -51,9 +51,9 @@ public class RCard {
return matchRepository.findById(id)
.invoke(Unchecked.consumer(o -> {
if (o == null)
throw new DNotFoundException(trad.t("matche.non.trouver"));
throw new DNotFoundException(trad.t("matche.non.trouver", connection));
if (!o.getCategory().getCompet().getUuid().equals(connection.pathParam("uuid")))
throw new DForbiddenException(trad.t("permission.denied"));
throw new DForbiddenException(trad.t("permission.denied", connection));
}));
}
@@ -103,7 +103,7 @@ public class RCard {
.firstResult()
.invoke(Unchecked.consumer(o -> {
if (o == null)
throw new DNotFoundException(trad.t("carton.non.trouver"));
throw new DNotFoundException(trad.t("carton.non.trouver", connection));
SSCard.sendRmCards(connection, List.of(o.getId()));
}))
.chain(cardModel -> Panache.withTransaction(() -> cardRepository.delete(cardModel)))

View File

@@ -1,22 +1,25 @@
package fr.titionfire.ffsaf.ws.recv;
import fr.titionfire.ffsaf.data.model.CardModel;
import fr.titionfire.ffsaf.data.model.CategoryModel;
import fr.titionfire.ffsaf.data.model.MatchModel;
import fr.titionfire.ffsaf.data.model.TreeModel;
import fr.titionfire.ffsaf.data.model.*;
import fr.titionfire.ffsaf.data.repository.*;
import fr.titionfire.ffsaf.domain.entity.MatchEntity;
import fr.titionfire.ffsaf.domain.entity.MatchModelExtend;
import fr.titionfire.ffsaf.domain.entity.TreeEntity;
import fr.titionfire.ffsaf.domain.service.CardService;
import fr.titionfire.ffsaf.domain.service.ResultService;
import fr.titionfire.ffsaf.domain.service.TradService;
import fr.titionfire.ffsaf.rest.data.PresetData;
import fr.titionfire.ffsaf.rest.data.ResultCategoryData;
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
import fr.titionfire.ffsaf.utils.TreeNode;
import fr.titionfire.ffsaf.ws.PermLevel;
import fr.titionfire.ffsaf.ws.send.SSCategorie;
import fr.titionfire.ffsaf.ws.send.SSMatch;
import io.quarkus.hibernate.reactive.panache.Panache;
import io.quarkus.hibernate.reactive.panache.common.WithSession;
import io.quarkus.runtime.annotations.RegisterForReflection;
import io.quarkus.websockets.next.UserData;
import io.quarkus.websockets.next.WebSocketConnection;
import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.unchecked.Unchecked;
@@ -25,8 +28,8 @@ import jakarta.inject.Inject;
import lombok.Data;
import org.hibernate.reactive.mutiny.Mutiny;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import java.util.stream.Stream;
@WithSession
@ApplicationScoped
@@ -49,6 +52,12 @@ public class RCategorie {
@Inject
CardService cardService;
@Inject
CardRepository cardRepository;
@Inject
ResultService resultService;
@Inject
TradService trad;
@@ -56,9 +65,9 @@ public class RCategorie {
return categoryRepository.findById(id)
.invoke(Unchecked.consumer(o -> {
if (o == null)
throw new DNotFoundException(trad.t("categorie.non.trouver"));
throw new DNotFoundException(trad.t("categorie.non.trouver", connection));
if (!o.getCompet().getUuid().equals(connection.pathParam("uuid")))
throw new DForbiddenException(trad.t("permission.denied"));
throw new DForbiddenException(trad.t("permission.denied", connection));
}));
}
@@ -78,6 +87,9 @@ public class RCategorie {
fullCategory.setName(cat.getName());
fullCategory.setLiceName(cat.getLiceName());
fullCategory.setType(cat.getType());
fullCategory.setTreeAreClassement(cat.isTreeAreClassement());
fullCategory.setFullClassement(cat.isFullClassement());
fullCategory.setPreset(PresetData.fromModel(cat.getPreset()));
})
.call(cat -> Mutiny.fetch(cat.getMatchs())
.map(matchModels -> matchModels.stream().filter(o -> o.getCategory_ord() >= 0)
@@ -116,6 +128,9 @@ public class RCategorie {
cat.setName(categorie.name);
cat.setLiceName(categorie.liceName);
cat.setType(categorie.type);
cat.setTreeAreClassement(categorie.treeAreClassement);
cat.setFullClassement(categorie.fullClassement);
// cat.setPreset(cat.getPreset()); //TODO preset update
return Panache.withTransaction(() -> categoryRepository.persist(cat));
})
.call(cat -> {
@@ -224,10 +239,149 @@ public class RCategorie {
.replaceWithVoid();
}
@WSReceiver(code = "createClassementMatchs", permission = PermLevel.TABLE)
public Uni<Void> createClassementMatchs(WebSocketConnection connection, Long categoryId) {
return getById(categoryId, connection)
.call(cat -> {
PermLevel perm = PermLevel.valueOf(connection.userData().get(UserData.TypedKey.forString("prem")));
if (perm == PermLevel.TABLE) {
return matchRepository.list("category = ?1 AND category_ord = -42", cat.getId())
.chain(l -> l.stream().anyMatch(MatchModel::isEnd) ?
Uni.createFrom().failure(
new DForbiddenException(trad.t("err.match.termine", connection))) :
Uni.createFrom().voidItem());
}
return Uni.createFrom().voidItem();
})
.call(cat -> treeRepository.list("category = ?1 AND level <= -10", cat.getId())
.map(l -> l.stream().map(o -> o.getMatch().getId()).toList())
.call(__ -> treeRepository.delete("category = ?1 AND level <= -10", cat.getId()))
.call(ids -> matchRepository.delete("id IN ?1", ids)))
.call(cat -> Mutiny.fetch(cat.getTree()))
.call(cat -> {
List<MatchModel> toSave = new ArrayList<>();
if (!cat.getTree().isEmpty()) {
for (TreeModel treeModel : cat.getTree())
cleanTree(treeModel, toSave);
}
return Panache.withTransaction(() -> matchRepository.persist(toSave))
.invoke(__ -> SSMatch.sendMatch(connection,
toSave.stream().map(MatchEntity::fromModel).toList()));
})
.chain(cat -> Mutiny.fetch(cat.getMatchs())
.chain(list -> cardRepository.list("competition = ?1", cat.getCompet())
.map(c -> list.stream().map(m -> new MatchModelExtend(m, c)).toList()))
.map(matchModels -> {
ResultCategoryData out = new ResultCategoryData();
resultService.getArray2(matchModels, null, out);
out.getRankArray().remove('-');
return out;
})
.invoke(Unchecked.consumer(result -> {
if (result.getRankArray().size() != 2) {
throw new DForbiddenException(trad.t("configuration.non.supportee", connection));
}
}))
.chain(result -> {
List<MatchModel> toSave = new ArrayList<>();
List<MatchModel> toCreate = new ArrayList<>();
List<TreeModel> toSaveTree = new ArrayList<>();
int treeSize = 0;
List<TreeModel> lastNode = new ArrayList<>();
Optional<TreeModel> tree = cat.getTree().stream().filter(t -> t.getLevel() > 0)
.min(Comparator.comparing(TreeModel::getLevel));
if (tree.isPresent()) {
tree.get().getChildrenAtDepth(tree.get().death() - 1, 0, lastNode);
treeSize = lastNode.size();
}
Iterator<List<ResultCategoryData.RankArray>> iterator = result.getRankArray().values()
.iterator();
List<ResultCategoryData.RankArray> poule1 = iterator.next();
List<ResultCategoryData.RankArray> poule2 = iterator.next();
int maxToMixFill = Math.min(poule1.size(), poule2.size());
int maxToMixTreeFill = Math.min(treeSize, maxToMixFill);
for (int i = 0; i < maxToMixTreeFill; i++) {
CombModel comb1 = poule1.get(i).getComb();
CombModel comb2 = poule2.get(treeSize - i - 1).getComb();
fillMatchComb(lastNode.get(i).getMatch(), comb1, comb2);
toSave.add(lastNode.get(i).getMatch());
}
if (cat.isFullClassement()) {
for (int i = maxToMixTreeFill; i < maxToMixFill; i++) {
MatchModel match = new MatchModel();
match.setCategory(cat);
match.setCategory_ord(-42);
match.setEnd(false);
CombModel comb1 = poule1.get(i).getComb();
CombModel comb2 = poule2.get(i).getComb();
fillMatchComb(match, comb1, comb2);
toCreate.add(match);
toSaveTree.add(new TreeModel(cat.getId(), -10 - i, match));
}
}
return Panache.withTransaction(() -> matchRepository.persist(toSave)
.call(__ -> matchRepository.create(toCreate)))
.call(__ -> toSaveTree.isEmpty() ? Uni.createFrom().voidItem()
: Panache.withTransaction(() -> treeRepository.persist(toSaveTree)))
.map(__ -> Stream.concat(toSave.stream(), toCreate.stream())
.map(MatchEntity::fromModel).toList());
})
)
.onFailure().invoke(t -> System.out.println("error: " + t.getMessage()))
.invoke(matchEntities -> SSMatch.sendMatch(connection, matchEntities))
.call(__ -> treeRepository.list("category = ?1 AND level != 0", categoryId)
.map(treeModels -> treeModels.stream().map(TreeEntity::fromModel).toList())
.invoke(trees -> SSCategorie.sendTreeCategory(connection, trees)))
.replaceWithVoid();
}
private void cleanTree(TreeModel treeModel, List<MatchModel> toSave) {
MatchModel model = treeModel.getMatch();
if (model != null) {
model.setC1_id(null);
model.setC1_guest(null);
model.setC2_id(null);
model.setC2_guest(null);
model.setEnd(false);
model.setDate(null);
model.getScores().clear();
toSave.add(model);
}
if (treeModel.getLeft() != null)
cleanTree(treeModel.getLeft(), toSave);
if (treeModel.getRight() != null)
cleanTree(treeModel.getRight(), toSave);
}
private void fillMatchComb(MatchModel match, CombModel comb1, CombModel comb2) {
if (comb1 instanceof MembreModel m)
match.setC1_id(m);
else if (comb1 instanceof CompetitionGuestModel g)
match.setC1_guest(g);
if (comb2 instanceof MembreModel m)
match.setC2_id(m);
else if (comb2 instanceof CompetitionGuestModel g)
match.setC2_guest(g);
}
@RegisterForReflection
public record JustCategorie(long id, String name, int type, String liceName) {
public record JustCategorie(long id, String name, int type, String liceName, boolean treeAreClassement,
boolean fullClassement, PresetData preset) {
public static JustCategorie from(CategoryModel m) {
return new JustCategorie(m.getId(), m.getName(), m.getType(), m.getLiceName());
return new JustCategorie(m.getId(), m.getName(), m.getType(), m.getLiceName(), m.isTreeAreClassement(),
m.isFullClassement(), PresetData.fromModel(m.getPreset()));
}
}
@@ -242,6 +396,9 @@ public class RCategorie {
String name;
int type;
String liceName;
boolean treeAreClassement = false;
boolean fullClassement = false;
PresetData preset;
List<TreeEntity> trees = null;
List<MatchEntity> matches;
List<CardModel> cards;

View File

@@ -55,9 +55,9 @@ public class RMatch {
return matchRepository.findById(id)
.invoke(Unchecked.consumer(o -> {
if (o == null)
throw new DNotFoundException(trad.t("matche.non.trouver"));
throw new DNotFoundException(trad.t("matche.non.trouver", connection));
if (!o.getCategory().getCompet().getUuid().equals(connection.pathParam("uuid")))
throw new DForbiddenException(trad.t("permission.denied"));
throw new DForbiddenException(trad.t("permission.denied", connection));
}));
}
@@ -85,9 +85,9 @@ public class RMatch {
return categoryRepository.findById(m.categorie)
.invoke(Unchecked.consumer(o -> {
if (o == null)
throw new DNotFoundException(trad.t("categorie.non.trouver"));
throw new DNotFoundException(trad.t("categorie.non.trouver", connection));
if (!o.getCompet().getUuid().equals(connection.pathParam("uuid")))
throw new DForbiddenException(trad.t("permission.denied"));
throw new DForbiddenException(trad.t("permission.denied", connection));
}))
.chain(categoryModel -> creatMatch(categoryModel, m))
.chain(mm -> Panache.withTransaction(() -> matchRepository.create(mm)))
@@ -297,9 +297,9 @@ public class RMatch {
return categoryRepository.findById(data.categorie)
.invoke(Unchecked.consumer(o -> {
if (o == null)
throw new DNotFoundException(trad.t("categorie.non.trouver"));
throw new DNotFoundException(trad.t("categorie.non.trouver", connection));
if (!o.getCompet().getUuid().equals(connection.pathParam("uuid")))
throw new DForbiddenException(trad.t("permission.denied"));
throw new DForbiddenException(trad.t("permission.denied", connection));
}))
.call(cm -> data.matchesToRemove.isEmpty() ? Uni.createFrom().voidItem() :
(Panache.withTransaction(