feat: add guest comb to competition register form

This commit is contained in:
2025-11-30 18:38:18 +01:00
parent f6d4bb0fe4
commit 936392f8bd
7 changed files with 286 additions and 64 deletions

View File

@@ -1,9 +1,6 @@
package fr.titionfire.ffsaf.domain.service;
import fr.titionfire.ffsaf.data.model.CompetitionModel;
import fr.titionfire.ffsaf.data.model.HelloAssoRegisterModel;
import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.data.model.RegisterModel;
import fr.titionfire.ffsaf.data.model.*;
import fr.titionfire.ffsaf.data.repository.*;
import fr.titionfire.ffsaf.net2.ServerCustom;
import fr.titionfire.ffsaf.net2.data.SimpleCompet;
@@ -16,10 +13,9 @@ import fr.titionfire.ffsaf.rest.data.SimpleCompetData;
import fr.titionfire.ffsaf.rest.data.SimpleRegisterComb;
import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
import fr.titionfire.ffsaf.utils.CompetitionSystem;
import fr.titionfire.ffsaf.utils.RegisterMode;
import fr.titionfire.ffsaf.utils.SecurityCtx;
import fr.titionfire.ffsaf.utils.Utils;
import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
import fr.titionfire.ffsaf.utils.*;
import fr.titionfire.ffsaf.ws.send.SRegister;
import io.quarkus.cache.Cache;
import io.quarkus.cache.CacheName;
import io.quarkus.hibernate.reactive.panache.Panache;
@@ -64,6 +60,9 @@ public class CompetitionService {
@Inject
CombRepository combRepository;
@Inject
CompetitionGuestRepository competitionGuestRepository;
@Inject
ServerCustom serverCustom;
@@ -83,6 +82,9 @@ public class CompetitionService {
@Inject
Vertx vertx;
@Inject
SRegister sRegister;
@Inject
@CacheName("safca-config")
Cache cache;
@@ -108,7 +110,8 @@ public class CompetitionService {
}
return permService.hasAdminViewPerm(securityCtx, id)
.chain(competitionModel -> Mutiny.fetch(competitionModel.getInsc())
.map(insc -> CompetitionData.fromModel(competitionModel).addInsc(insc)))
.chain(insc -> Mutiny.fetch(competitionModel.getGuests())
.map(guest -> CompetitionData.fromModel(competitionModel).addInsc(insc, guest))))
.chain(data ->
vertx.getOrCreateContext().executeBlocking(() -> {
keycloakService.getUser(UUID.fromString(data.getOwner()))
@@ -175,6 +178,7 @@ public class CompetitionService {
model.setSystem(data.getSystem());
model.setClub(clubModel);
model.setInsc(new ArrayList<>());
model.setGuests(new ArrayList<>());
model.setUuid(UUID.randomUUID().toString());
model.setOwner(securityCtx.getSubject());
@@ -235,11 +239,18 @@ public class CompetitionService {
public Uni<List<SimpleRegisterComb>> getRegister(SecurityCtx securityCtx, Long id, String source) {
if ("admin".equals(source))
return permService.hasEditPerm(securityCtx, id)
.chain(c -> Mutiny.fetch(c.getInsc()))
.onItem().transformToMulti(Multi.createFrom()::iterable)
.onItem().call(combModel -> Mutiny.fetch(combModel.getMembre().getLicences()))
.map(combModel -> SimpleRegisterComb.fromModel(combModel, combModel.getMembre().getLicences()))
.collect().asList();
.chain(c -> {
Uni<List<SimpleRegisterComb>> uni = Mutiny.fetch(c.getInsc())
.onItem().transformToMulti(Multi.createFrom()::iterable)
.onItem().call(combModel -> Mutiny.fetch(combModel.getMembre().getLicences()))
.map(cm -> SimpleRegisterComb.fromModel(cm, cm.getMembre().getLicences()))
.collect().asList();
return uni
.call(l -> Mutiny.fetch(c.getGuests())
.map(guest -> guest.stream().map(SimpleRegisterComb::fromModel).toList())
.invoke(l::addAll));
});
if ("club".equals(source))
return Uni.createFrom().nullItem()
.invoke(Unchecked.consumer(__ -> {
@@ -262,17 +273,44 @@ public class CompetitionService {
public Uni<SimpleRegisterComb> addRegisterComb(SecurityCtx securityCtx, Long id, RegisterRequestData data,
String source) {
if ("admin".equals(source))
return permService.hasEditPerm(securityCtx, id)
.chain(c -> findComb(data.getLicence(), data.getFname(), data.getLname())
.call(combModel -> {
if (c.getBanMembre() == null)
c.setBanMembre(new ArrayList<>());
c.getBanMembre().remove(combModel.getId());
return Panache.withTransaction(() -> repository.persist(c));
})
.chain(combModel -> updateRegister(data, c, combModel, true)))
.chain(r -> Mutiny.fetch(r.getMembre().getLicences())
.map(licences -> SimpleRegisterComb.fromModel(r, licences)));
if (data.getLicence() != -1) { // not a guest
return permService.hasEditPerm(securityCtx, id)
.chain(c -> findComb(data.getLicence(), data.getFname(), data.getLname())
.call(combModel -> Mutiny.fetch(combModel.getLicences()))
.call(combModel -> {
if (c.getBanMembre() == null)
c.setBanMembre(new ArrayList<>());
c.getBanMembre().remove(combModel.getId());
return Panache.withTransaction(() -> repository.persist(c));
})
.chain(combModel -> updateRegister(data, c, combModel, true)))
.map(r -> SimpleRegisterComb.fromModel(r, r.getMembre().getLicences()));
} else {
return permService.hasEditPerm(securityCtx, id)
.chain(c -> competitionGuestRepository.findById(data.getId() * -1)
.map(g -> {
if (g != null)
return g;
CompetitionGuestModel model = new CompetitionGuestModel();
model.setCompetition(c);
return model;
}))
.chain(model -> {
model.setFname(data.getFname());
model.setLname(data.getLname());
model.setGenre(data.getGenre());
model.setClub(data.getClub());
model.setCountry(data.getCountry());
model.setWeight(data.getWeight());
model.setCategorie(data.getCategorie());
return Panache.withTransaction(() -> competitionGuestRepository.persist(model))
.call(r -> model.getCompetition().getSystem() == CompetitionSystem.INTERNAL ?
sRegister.sendRegister(model.getCompetition().getUuid(),
r) : Uni.createFrom().voidItem());
})
.map(SimpleRegisterComb::fromModel);
}
if ("club".equals(source))
return repository.findById(id)
.invoke(Unchecked.consumer(cm -> {
@@ -283,6 +321,7 @@ public class CompetitionService {
throw new DBadRequestException("Inscription fermée");
}))
.chain(c -> findComb(data.getLicence(), data.getFname(), data.getLname())
.call(combModel -> Mutiny.fetch(combModel.getLicences()))
.invoke(Unchecked.consumer(model -> {
if (!securityCtx.isInClubGroup(model.getClub().getId()))
throw new DForbiddenException();
@@ -291,8 +330,7 @@ public class CompetitionService {
"Vous n'avez pas le droit d'inscrire ce membre (par décision de l'administrateur de la compétition)");
}))
.chain(combModel -> updateRegister(data, c, combModel, false)))
.chain(r -> Mutiny.fetch(r.getMembre().getLicences())
.map(licences -> SimpleRegisterComb.fromModel(r, licences)));
.map(r -> SimpleRegisterComb.fromModel(r, r.getMembre().getLicences()));
return repository.findById(id)
.invoke(Unchecked.consumer(cm -> {
@@ -347,15 +385,18 @@ public class CompetitionService {
SReqRegister.sendIfNeed(serverCustom.clients,
new CompetitionData.SimpleRegister(r.getMembre().getId(),
r.getOverCategory(), r.getWeight(), r.getCategorie(),
(r.getClub() == null) ? null : r.getClub().getId()), c.getId());
(r.getClub() == null) ? null : r.getClub().getId(),
(r.getClub() == null) ? null : r.getClub().getName()), c.getId());
}
return r;
}))
.chain(r -> Panache.withTransaction(() -> registerRepository.persist(r)));
.chain(r -> Panache.withTransaction(() -> registerRepository.persist(r)))
.call(r -> c.getSystem() == CompetitionSystem.INTERNAL ?
sRegister.sendRegister(c.getUuid(), r) : Uni.createFrom().voidItem());
}
private Uni<MembreModel> findComb(Long licence, String fname, String lname) {
if (licence != null && licence != 0) {
if (licence != null && licence > 0) {
return combRepository.find("licence = ?1", licence).firstResult()
.invoke(Unchecked.consumer(combModel -> {
if (combModel == null)
@@ -398,30 +439,45 @@ public class CompetitionService {
if (new Date().before(cm.getStartRegister()) || new Date().after(cm.getEndRegister()))
throw new DBadRequestException("Inscription fermée");
}))
.call(cm -> membreService.getByAccountId(securityCtx.getSubject())
.call(cm -> membreService.getById(combId)
.invoke(Unchecked.consumer(model -> {
if (model == null)
throw new DNotFoundException("Membre " + combId + " n'existe pas");
if (!securityCtx.isInClubGroup(model.getClub().getId()))
throw new DForbiddenException();
})))
.chain(c -> deleteRegister(combId, c, false));
return repository.findById(id)
.invoke(Unchecked.consumer(cm -> {
if (cm.getRegisterMode() != RegisterMode.FREE)
throw new DForbiddenException();
if (new Date().before(cm.getStartRegister()) || new Date().after(cm.getEndRegister()))
throw new DBadRequestException("Inscription fermée");
}))
.call(cm -> membreService.getByAccountId(securityCtx.getSubject())
.invoke(Unchecked.consumer(model -> {
if (cm.getRegisterMode() != RegisterMode.FREE || !Objects.equals(model.getId(), combId))
throw new DForbiddenException();
if (new Date().before(cm.getStartRegister()) || new Date().after(cm.getEndRegister()))
throw new DBadRequestException("Inscription fermée");
})))
.chain(c -> deleteRegister(combId, c, false));
}
private Uni<Void> deleteRegister(Long combId, CompetitionModel c, boolean admin) {
if (admin && combId < 0) {
return competitionGuestRepository.find("competition = ?1 AND id = ?2", c, combId * -1).firstResult()
.onFailure().transform(t -> new DBadRequestException("Combattant non inscrit"))
.call(Unchecked.function(
model -> Panache.withTransaction(() -> competitionGuestRepository.delete(model))
.call(r -> c.getSystem() == CompetitionSystem.INTERNAL ?
sRegister.sendRegisterRemove(c.getUuid(), combId) : Uni.createFrom()
.voidItem())))
.replaceWithVoid();
}
return registerRepository.find("competition = ?1 AND membre.id = ?2", c, combId).firstResult()
.onFailure().transform(t -> new DBadRequestException("Combattant non inscrit"))
.call(Unchecked.function(registerModel -> {
if (!admin && registerModel.isLockEdit())
throw new DForbiddenException("Modification bloquée par l'administrateur de la compétition");
return Panache.withTransaction(() -> registerRepository.delete(registerModel));
return Panache.withTransaction(() -> registerRepository.delete(registerModel))
.call(r -> c.getSystem() == CompetitionSystem.INTERNAL ?
sRegister.sendRegisterRemove(c.getUuid(), combId) : Uni.createFrom().voidItem());
}))
.replaceWithVoid();
}
@@ -528,8 +584,13 @@ public class CompetitionService {
continue;
uni = uni.call(__ -> Panache.withTransaction(
() -> registerRepository.delete("competition = ?1 AND membre = ?2",
reg.getCompetition(), reg.getMembre())));
() -> registerRepository.delete("competition = ?1 AND membre = ?2",
reg.getCompetition(), reg.getMembre())))
.call(r -> reg.getCompetition().getSystem() == CompetitionSystem.INTERNAL ?
sRegister.sendRegisterRemove(reg.getCompetition().getUuid(),
reg.getMembre().getId()) : Uni.createFrom().voidItem()).onFailure()
.recoverWithNull()
;
}
return uni;
@@ -541,7 +602,8 @@ public class CompetitionService {
public Uni<Response> registerHelloAsso(NotificationData data) {
String organizationSlug = data.getOrganizationSlug();
String formSlug = data.getFormSlug();
RegisterRequestData req = new RegisterRequestData(null, "", "", null, 0, false);
RegisterRequestData req = new RegisterRequestData(null, "", "", null, 0, false, null, Categorie.CADET, Genre.NA,
null, "fr");
return repository.find("data1 = ?1 AND data2 = ?2", organizationSlug, formSlug).firstResult()
.onFailure().recoverWithNull()

View File

@@ -1,5 +1,6 @@
package fr.titionfire.ffsaf.rest.data;
import fr.titionfire.ffsaf.data.model.CompetitionGuestModel;
import fr.titionfire.ffsaf.data.model.CompetitionModel;
import fr.titionfire.ffsaf.data.model.RegisterModel;
import fr.titionfire.ffsaf.utils.Categorie;
@@ -11,6 +12,7 @@ import lombok.Data;
import java.util.Date;
import java.util.List;
import java.util.stream.Stream;
@Data
@AllArgsConstructor
@@ -57,9 +59,9 @@ public class CompetitionData {
model.getAdresse(), "", model.getDate(), model.getTodate(), null,
model.getRegisterMode(), model.getStartRegister(), model.getEndRegister(), model.isPublicVisible(),
null, model.getClub().getName(), "", null, false,
"","", "","");
"", "", "", "");
if (model.getRegisterMode() == RegisterMode.HELLOASSO){
if (model.getRegisterMode() == RegisterMode.HELLOASSO) {
out.setData1(model.getData1());
out.setData2(model.getData2());
}
@@ -67,10 +69,15 @@ public class CompetitionData {
return out;
}
public CompetitionData addInsc(List<RegisterModel> insc) {
this.registers = insc.stream()
.map(i -> new SimpleRegister(i.getMembre().getId(), i.getOverCategory(), i.getWeight(),
i.getCategorie(), (i.getClub() == null) ? null : i.getClub().getId())).toList();
public CompetitionData addInsc(List<RegisterModel> insc, List<CompetitionGuestModel> guests) {
this.registers = Stream.concat(
insc.stream()
.map(i -> new SimpleRegister(i.getMembre().getId(), i.getOverCategory(), i.getWeight(),
i.getCategorie(), (i.getClub() == null) ? null : i.getClub().getId(),
(i.getClub() == null) ? null : i.getClub().getName())),
guests.stream()
.map(i -> new SimpleRegister(i.getId() * -1, 0, i.getWeight(),
i.getCategorie(), null, i.getClub()))).toList();
return this;
}
@@ -83,5 +90,6 @@ public class CompetitionData {
Integer weight;
Categorie categorie;
Long club;
String club_str;
}
}

View File

@@ -1,5 +1,7 @@
package fr.titionfire.ffsaf.rest.data;
import fr.titionfire.ffsaf.utils.Categorie;
import fr.titionfire.ffsaf.utils.Genre;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.AllArgsConstructor;
import lombok.Data;
@@ -17,4 +19,11 @@ public class RegisterRequestData {
private Integer weight;
private int overCategory;
private boolean lockEdit = false;
// for guest registration only
private Long id = null;
private Categorie categorie = Categorie.CADET;
private Genre genre = Genre.NA;
private String club = null;
private String country = null;
}

View File

@@ -1,9 +1,12 @@
package fr.titionfire.ffsaf.rest.data;
import fr.titionfire.ffsaf.data.model.CompetitionGuestModel;
import fr.titionfire.ffsaf.data.model.LicenceModel;
import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.data.model.RegisterModel;
import fr.titionfire.ffsaf.net2.data.SimpleClubModel;
import fr.titionfire.ffsaf.utils.Categorie;
import fr.titionfire.ffsaf.utils.Genre;
import fr.titionfire.ffsaf.utils.Utils;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.AllArgsConstructor;
@@ -18,8 +21,9 @@ public class SimpleRegisterComb {
private long id;
private String fname;
private String lname;
private String genre;
private String categorie;
private Genre genre;
private String country;
private Categorie categorie;
private SimpleClubModel club;
private Integer licence;
private Integer weight;
@@ -30,11 +34,18 @@ public class SimpleRegisterComb {
public static SimpleRegisterComb fromModel(RegisterModel register, List<LicenceModel> licences) {
MembreModel membreModel = register.getMembre();
return new SimpleRegisterComb(membreModel.getId(), membreModel.getFname(), membreModel.getLname(),
membreModel.getGenre().name(),
(register.getCategorie() == null) ? "Catégorie inconnue" : register.getCategorie().getName(),
membreModel.getGenre(), membreModel.getCountry(),
(register.getCategorie() == null) ? null : register.getCategorie(),
SimpleClubModel.fromModel(register.getClub()), membreModel.getLicence(), register.getWeight(),
register.getOverCategory(),
licences.stream().anyMatch(l -> l.isValidate() && l.getSaison() == Utils.getSaison()),
register.isLockEdit());
}
public static SimpleRegisterComb fromModel(CompetitionGuestModel guest) {
return new SimpleRegisterComb(guest.getId() * -1, guest.getFname(), guest.getLname(),
guest.getGenre(), guest.getCountry(), guest.getCategorie(),
new SimpleClubModel(null, guest.getClub(), "fr", null),
null, guest.getWeight(), 0, false, false);
}
}

View File

@@ -0,0 +1,53 @@
package fr.titionfire.ffsaf.ws.send;
import fr.titionfire.ffsaf.data.model.CompetitionGuestModel;
import fr.titionfire.ffsaf.data.model.RegisterModel;
import fr.titionfire.ffsaf.domain.entity.CombEntity;
import fr.titionfire.ffsaf.net2.MessageType;
import fr.titionfire.ffsaf.ws.CompetitionWS;
import fr.titionfire.ffsaf.ws.MessageOut;
import fr.titionfire.ffsaf.ws.PermLevel;
import io.quarkus.runtime.annotations.RegisterForReflection;
import io.quarkus.websockets.next.OpenConnections;
import io.quarkus.websockets.next.UserData;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.util.List;
import java.util.UUID;
@ApplicationScoped
@RegisterForReflection
public class SRegister {
@SuppressWarnings("CdiInjectionPointsInspection")
@Inject
OpenConnections connections;
public Uni<Void> sendRegister(String uuid, RegisterModel registerModel) {
return send(uuid, "sendRegister", CombEntity.fromModel(registerModel));
}
public Uni<Void> sendRegister(String uuid, CompetitionGuestModel model) {
return send(uuid, "sendRegister", CombEntity.fromModel(model));
}
public Uni<Void> sendRegisterRemove(String uuid, Long combId) {
return send(uuid, "sendRegister", combId);
}
public Uni<Void> send(String uuid, String code, Object data) {
List<Uni<Void>> queue = connections.findByEndpointId(CompetitionWS.class.getCanonicalName()).stream()
.filter(c -> c.pathParam("uuid").equals(uuid) && PermLevel.valueOf(
c.userData().get(UserData.TypedKey.forString("prem"))).ordinal() >= PermLevel.ADMIN.ordinal())
.map(c -> c.sendText(
new MessageOut(UUID.randomUUID(), code, MessageType.NOTIFY, data)))
.toList();
if (queue.isEmpty())
return Uni.createFrom().voidItem();
return Uni.join().all(queue).andCollectFailures().replaceWithVoid();
}
}