347 lines
17 KiB
Java
347 lines
17 KiB
Java
package fr.titionfire.ffsaf.ws.recv;
|
|
|
|
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.TreeEntity;
|
|
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
|
|
import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
|
|
import fr.titionfire.ffsaf.utils.ScoreEmbeddable;
|
|
import fr.titionfire.ffsaf.ws.PermLevel;
|
|
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.panache.common.Sort;
|
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
|
import io.quarkus.websockets.next.WebSocketConnection;
|
|
import io.smallrye.mutiny.Uni;
|
|
import io.smallrye.mutiny.unchecked.Unchecked;
|
|
import jakarta.enterprise.context.ApplicationScoped;
|
|
import jakarta.inject.Inject;
|
|
import org.jboss.logging.Logger;
|
|
|
|
import java.util.*;
|
|
import java.util.stream.Stream;
|
|
|
|
@WithSession
|
|
@ApplicationScoped
|
|
@RegisterForReflection
|
|
public class RMatch {
|
|
private static final Logger LOGGER = Logger.getLogger(RMatch.class);
|
|
|
|
@Inject
|
|
MatchRepository matchRepository;
|
|
|
|
@Inject
|
|
CombRepository combRepository;
|
|
|
|
@Inject
|
|
CategoryRepository categoryRepository;
|
|
|
|
@Inject
|
|
TreeRepository treeRepository;
|
|
|
|
@Inject
|
|
CompetitionGuestRepository competitionGuestRepository;
|
|
|
|
private Uni<MatchModel> getById(long id, WebSocketConnection connection) {
|
|
return matchRepository.findById(id)
|
|
.invoke(Unchecked.consumer(o -> {
|
|
if (o == null)
|
|
throw new DNotFoundException("Matche non trouver");
|
|
if (!o.getCategory().getCompet().getUuid().equals(connection.pathParam("uuid")))
|
|
throw new DForbiddenException("Permission denied");
|
|
}));
|
|
}
|
|
|
|
private Uni<MatchModel> creatMatch(CategoryModel categoryModel, AddMatch m) {
|
|
return Uni.createFrom().item(() -> {
|
|
MatchModel matchModel = new MatchModel();
|
|
|
|
matchModel.setCategory(categoryModel);
|
|
matchModel.setCategory_ord(m.categorie_ord);
|
|
matchModel.setEnd(false);
|
|
matchModel.setPoule(m.poule);
|
|
|
|
return matchModel;
|
|
})
|
|
.call(mm -> m.c1() >= 0 ?
|
|
combRepository.findById(m.c1()).invoke(mm::setC1_id) :
|
|
competitionGuestRepository.findById(m.c1() * -1).invoke(mm::setC1_guest))
|
|
.call(mm -> m.c2() >= 0 ?
|
|
combRepository.findById(m.c2()).invoke(mm::setC2_id) :
|
|
competitionGuestRepository.findById(m.c2() * -1).invoke(mm::setC2_guest));
|
|
}
|
|
|
|
@WSReceiver(code = "addMatch", permission = PermLevel.ADMIN)
|
|
public Uni<Void> addMatch(WebSocketConnection connection, AddMatch m) {
|
|
return categoryRepository.findById(m.categorie)
|
|
.invoke(Unchecked.consumer(o -> {
|
|
if (o == null)
|
|
throw new DNotFoundException("Catégorie non trouver");
|
|
if (!o.getCompet().getUuid().equals(connection.pathParam("uuid")))
|
|
throw new DForbiddenException("Permission denied");
|
|
}))
|
|
.chain(categoryModel -> creatMatch(categoryModel, m))
|
|
.chain(mm -> Panache.withTransaction(() -> matchRepository.create(mm)))
|
|
.call(mm -> SSMatch.sendMatch(connection, MatchEntity.fromModel(mm)))
|
|
.replaceWithVoid();
|
|
}
|
|
|
|
@WSReceiver(code = "updateMatchComb", permission = PermLevel.ADMIN)
|
|
public Uni<Void> updateMatchComb(WebSocketConnection connection, MatchComb match) {
|
|
return getById(match.id(), connection)
|
|
.call(mm -> match.c1() != null ?
|
|
match.c1() >= 0 ?
|
|
combRepository.findById(match.c1()).invoke(model -> {
|
|
mm.setC1_id(model);
|
|
mm.setC1_guest(null);
|
|
}) :
|
|
competitionGuestRepository.findById(match.c1() * -1).invoke(model -> {
|
|
mm.setC1_id(null);
|
|
mm.setC1_guest(model);
|
|
|
|
}) :
|
|
Uni.createFrom().nullItem().invoke(__ -> {
|
|
mm.setC1_id(null);
|
|
mm.setC1_guest(null);
|
|
}))
|
|
.call(mm -> match.c2() != null ?
|
|
match.c2() >= 0 ?
|
|
combRepository.findById(match.c2()).invoke(model -> {
|
|
mm.setC2_id(model);
|
|
mm.setC2_guest(null);
|
|
}) :
|
|
competitionGuestRepository.findById(match.c2() * -1).invoke(model -> {
|
|
mm.setC2_id(null);
|
|
mm.setC2_guest(model);
|
|
}) :
|
|
Uni.createFrom().nullItem().invoke(__ -> {
|
|
mm.setC2_id(null);
|
|
mm.setC2_guest(null);
|
|
}))
|
|
.chain(mm -> Panache.withTransaction(() -> matchRepository.persist(mm)))
|
|
.call(mm -> SSMatch.sendMatch(connection, MatchEntity.fromModel(mm)))
|
|
.replaceWithVoid();
|
|
}
|
|
|
|
@WSReceiver(code = "updateMatchOrder", permission = PermLevel.ADMIN)
|
|
public Uni<Void> updateMatchComb(WebSocketConnection connection, MatchOrder order) {
|
|
return getById(order.id(), connection)
|
|
.call(m -> matchRepository.update(
|
|
"category_ord = category_ord + 1 WHERE category_ord >= ?1 AND category_ord < ?2", order.pos,
|
|
m.getCategory_ord()))
|
|
.call(m -> matchRepository.update(
|
|
"category_ord = category_ord - 1 WHERE category_ord <= ?1 AND category_ord > ?2", order.pos,
|
|
m.getCategory_ord()))
|
|
.invoke(m -> m.setCategory_ord(order.pos))
|
|
.call(m -> Panache.withTransaction(() -> matchRepository.persist(m)))
|
|
.call(mm -> SSMatch.sendMatchOrder(connection, order))
|
|
.replaceWithVoid();
|
|
}
|
|
|
|
@WSReceiver(code = "updateMatchScore", permission = PermLevel.TABLE)
|
|
public Uni<Void> updateMatchScore(WebSocketConnection connection, MatchScore score) {
|
|
return getById(score.matchId(), connection)
|
|
.chain(matchModel -> {
|
|
int old_win = matchModel.win();
|
|
Optional<ScoreEmbeddable> optional = matchModel.getScores().stream()
|
|
.filter(s -> s.getN_round() == score.n_round()).findAny();
|
|
boolean b = score.s1() != -1000 || score.s2() != -1000;
|
|
if (optional.isPresent()) {
|
|
if (b) {
|
|
optional.get().setS1(score.s1());
|
|
optional.get().setS2(score.s2());
|
|
} else {
|
|
matchModel.getScores().remove(optional.get());
|
|
}
|
|
} else if (b) {
|
|
matchModel.getScores().add(new ScoreEmbeddable(score.n_round(), score.s1(), score.s2()));
|
|
}
|
|
|
|
return Panache.withTransaction(() -> matchRepository.persist(matchModel))
|
|
.call(mm -> {
|
|
if (mm.isEnd() && mm.win() != old_win && mm.getCategory_ord() == -42) {
|
|
return updateEndAndTree(mm, new ArrayList<>())
|
|
.call(l -> SSMatch.sendMatch(connection, l));
|
|
}
|
|
return Uni.createFrom().nullItem();
|
|
});
|
|
})
|
|
.call(mm -> SSMatch.sendMatch(connection, MatchEntity.fromModel(mm)))
|
|
.replaceWithVoid();
|
|
}
|
|
|
|
@WSReceiver(code = "updateMatchEnd", permission = PermLevel.TABLE)
|
|
public Uni<Void> updateMatchEnd(WebSocketConnection connection, MatchEnd matchEnd) {
|
|
List<MatchEntity> toSend = new ArrayList<>();
|
|
|
|
return getById(matchEnd.matchId(), connection)
|
|
.chain(mm -> {
|
|
if (mm.getCategory_ord() == -42 && mm.win() == 0) { // Tournois
|
|
mm.setDate(null);
|
|
mm.setEnd(false);
|
|
} else {
|
|
mm.setDate((matchEnd.end) ? new Date() : null);
|
|
mm.setEnd(matchEnd.end);
|
|
}
|
|
return Panache.withTransaction(() -> matchRepository.persist(mm));
|
|
})
|
|
.invoke(mm -> toSend.add(MatchEntity.fromModel(mm)))
|
|
.chain(mm -> updateEndAndTree(mm, toSend))
|
|
.call(__ -> SSMatch.sendMatch(connection, toSend))
|
|
.replaceWithVoid();
|
|
}
|
|
|
|
private Uni<List<MatchEntity>> updateEndAndTree(MatchModel mm, List<MatchEntity> toSend) {
|
|
return (mm.getCategory_ord() != -42) ?
|
|
Uni.createFrom().item(toSend) :
|
|
treeRepository.list("category = ?1 AND level != 0", Sort.ascending("level"), mm.getCategory().getId())
|
|
.chain(treeModels -> {
|
|
List<TreeModel> node = treeModels.stream().flatMap(t -> t.flat().stream()).toList();
|
|
List<TreeEntity> trees = treeModels.stream().map(TreeEntity::fromModel).toList();
|
|
for (int i = 0; i < trees.size() - 1; i++) {
|
|
TreeEntity.setAssociated(trees.get(i), trees.get(i + 1));
|
|
}
|
|
|
|
TreeEntity root = trees.stream()
|
|
.filter(t -> t.getMatchNode(mm.getId()) != null)
|
|
.findFirst()
|
|
.orElseThrow();
|
|
|
|
TreeEntity currentNode = root.getMatchNode(mm.getId());
|
|
if (currentNode == null) {
|
|
LOGGER.error(
|
|
"currentNode is empty for " + mm.getId() + " in " + mm.getCategory().getId());
|
|
return Uni.createFrom().voidItem();
|
|
}
|
|
|
|
TreeEntity parent = TreeEntity.getParent(root, currentNode);
|
|
if (parent == null) {
|
|
LOGGER.error("parent is empty for " + mm.getId() + " in " + mm.getCategory().getId());
|
|
return Uni.createFrom().voidItem();
|
|
}
|
|
|
|
int w = mm.win();
|
|
MembreModel toSetWin = null;
|
|
MembreModel toSetLose = null;
|
|
CompetitionGuestModel toSetWinGuest = null;
|
|
CompetitionGuestModel toSetLoseGuest = null;
|
|
|
|
if (mm.isEnd() && w != 0) {
|
|
toSetWin = (w > 0) ? mm.getC1_id() : mm.getC2_id();
|
|
toSetLose = (w > 0) ? mm.getC2_id() : mm.getC1_id();
|
|
toSetWinGuest = (w > 0) ? mm.getC1_guest() : mm.getC2_guest();
|
|
toSetLoseGuest = (w > 0) ? mm.getC2_guest() : mm.getC1_guest();
|
|
}
|
|
|
|
MatchModel modelWin = node.stream()
|
|
.filter(n -> Objects.equals(n.getId(), parent.getId()))
|
|
.findAny()
|
|
.map(TreeModel::getMatch)
|
|
.orElseThrow();
|
|
|
|
MatchModel modelLose = (parent.getAssociatedNode() == null) ? null : node.stream()
|
|
.filter(n -> Objects.equals(n.getId(), parent.getAssociatedNode().getId()))
|
|
.findAny()
|
|
.map(TreeModel::getMatch)
|
|
.orElseThrow();
|
|
|
|
if (currentNode.equals(parent.getLeft())) {
|
|
modelWin.setC1_id(toSetWin);
|
|
modelWin.setC1_guest(toSetWinGuest);
|
|
if (modelLose != null) {
|
|
modelLose.setC1_id(toSetLose);
|
|
modelLose.setC1_guest(toSetLoseGuest);
|
|
}
|
|
} else if (currentNode.equals(parent.getRight())) {
|
|
modelWin.setC2_id(toSetWin);
|
|
modelWin.setC2_guest(toSetWinGuest);
|
|
if (modelLose != null) {
|
|
modelLose.setC2_id(toSetLose);
|
|
modelLose.setC2_guest(toSetLoseGuest);
|
|
}
|
|
}
|
|
|
|
return Panache.withTransaction(() -> matchRepository.persist(modelWin)
|
|
.invoke(mm2 -> toSend.add(MatchEntity.fromModel(mm2)))
|
|
.call(__ -> modelLose == null ? Uni.createFrom().nullItem() :
|
|
matchRepository.persist(modelLose)
|
|
.invoke(mm2 -> toSend.add(MatchEntity.fromModel(mm2)))
|
|
)
|
|
.replaceWithVoid());
|
|
})
|
|
.map(__ -> toSend);
|
|
}
|
|
|
|
@WSReceiver(code = "deleteMatch", permission = PermLevel.ADMIN)
|
|
public Uni<Void> deleteMatch(WebSocketConnection connection, Long idMatch) {
|
|
return getById(idMatch, connection)
|
|
.chain(matchModel -> Panache.withTransaction(() -> matchRepository.delete(matchModel)))
|
|
.call(__ -> SSMatch.sendDeleteMatch(connection, idMatch))
|
|
.replaceWithVoid();
|
|
}
|
|
|
|
@WSReceiver(code = "recalculateMatch", permission = PermLevel.ADMIN)
|
|
public Uni<Void> recalculateMatch(WebSocketConnection connection, RecalculateMatch data) {
|
|
ArrayList<MatchEntity> matches = new ArrayList<>();
|
|
return categoryRepository.findById(data.categorie)
|
|
.invoke(Unchecked.consumer(o -> {
|
|
if (o == null)
|
|
throw new DNotFoundException("Catégorie non trouver");
|
|
if (!o.getCompet().getUuid().equals(connection.pathParam("uuid")))
|
|
throw new DForbiddenException("Permission denied");
|
|
}))
|
|
.call(cm -> matchRepository.delete("id IN ?1 AND category = ?2", data.matchesToRemove, cm)
|
|
.call(__ -> SSMatch.sendDeleteMatch(connection, data.matchesToRemove)))
|
|
.call(cm -> matchRepository.list("id IN ?1 AND category = ?2",
|
|
Stream.concat(data.matchOrderToUpdate.keySet().stream(),
|
|
data.matchPouleToUpdate.keySet().stream())
|
|
.distinct().toList(), cm)
|
|
.invoke(matchModels -> matchModels.forEach(model -> {
|
|
if (data.matchPouleToUpdate.containsKey(model.getId()))
|
|
model.setPoule(data.matchPouleToUpdate.get(model.getId()));
|
|
if (data.matchOrderToUpdate.containsKey(model.getId()))
|
|
model.setCategory_ord(data.matchOrderToUpdate.get(model.getId()));
|
|
}))
|
|
.call(mm -> Panache.withTransaction(() -> matchRepository.persist(mm)))
|
|
.invoke(mm -> matches.addAll(mm.stream().map(MatchEntity::fromModel).toList()))
|
|
)
|
|
.chain(categoryModel -> {
|
|
Uni<List<MatchModel>> uni = Uni.createFrom().item(new ArrayList<>());
|
|
for (AddMatch match : data.newMatch)
|
|
uni = uni.call(l -> creatMatch(categoryModel, match).invoke(l::add));
|
|
return uni;
|
|
}
|
|
)
|
|
.chain(mm -> Panache.withTransaction(() -> matchRepository.create(mm))
|
|
.invoke(__ -> matches.addAll(mm.stream().map(MatchEntity::fromModel).toList())))
|
|
.call(__ -> SSMatch.sendMatch(connection, matches))
|
|
.replaceWithVoid();
|
|
}
|
|
|
|
@RegisterForReflection
|
|
public record MatchComb(long id, Long c1, Long c2) {
|
|
}
|
|
|
|
@RegisterForReflection
|
|
public record MatchScore(long matchId, int n_round, int s1, int s2) {
|
|
}
|
|
|
|
@RegisterForReflection
|
|
public record MatchEnd(long matchId, boolean end) {
|
|
}
|
|
|
|
@RegisterForReflection
|
|
public record MatchOrder(long id, long pos) {
|
|
}
|
|
|
|
@RegisterForReflection
|
|
public record AddMatch(long categorie, long categorie_ord, char poule, long c1, long c2) {
|
|
}
|
|
|
|
public record RecalculateMatch(long categorie, List<AddMatch> newMatch, HashMap<Long, Integer> matchOrderToUpdate,
|
|
HashMap<Long, Character> matchPouleToUpdate, List<Long> matchesToRemove) {
|
|
}
|
|
}
|