From a8a41d4e3659703881865158c209bcf1b58af9ec Mon Sep 17 00:00:00 2001 From: Thibaut Valentin Date: Wed, 20 Aug 2025 21:59:26 +0200 Subject: [PATCH] feat: helloasso competition register --- .../domain/service/CompetitionService.java | 86 +++++++++++++++++-- .../ffsaf/domain/service/WebhookService.java | 17 +++- .../rest/client/dto/NotificationData.java | 35 ++++++++ .../ffsaf/rest/data/RegisterRequestData.java | 4 + 4 files changed, 134 insertions(+), 8 deletions(-) diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/CompetitionService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/CompetitionService.java index f01590a..d924d72 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/service/CompetitionService.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/CompetitionService.java @@ -8,6 +8,7 @@ import fr.titionfire.ffsaf.net2.ServerCustom; import fr.titionfire.ffsaf.net2.data.SimpleCompet; import fr.titionfire.ffsaf.net2.request.SReqCompet; import fr.titionfire.ffsaf.net2.request.SReqRegister; +import fr.titionfire.ffsaf.rest.client.dto.NotificationData; import fr.titionfire.ffsaf.rest.data.CompetitionData; import fr.titionfire.ffsaf.rest.data.RegisterRequestData; import fr.titionfire.ffsaf.rest.data.SimpleCompetData; @@ -22,13 +23,18 @@ import io.quarkus.cache.Cache; import io.quarkus.cache.CacheName; import io.quarkus.hibernate.reactive.panache.Panache; import io.quarkus.hibernate.reactive.panache.common.WithSession; +import io.quarkus.mailer.Mail; +import io.quarkus.mailer.reactive.ReactiveMailer; import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.unchecked.Unchecked; import io.vertx.mutiny.core.Vertx; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; +import jakarta.ws.rs.NotFoundException; +import jakarta.ws.rs.core.Response; import org.hibernate.reactive.mutiny.Mutiny; +import org.jboss.logging.Logger; import org.keycloak.representations.idm.UserRepresentation; import java.util.*; @@ -37,6 +43,7 @@ import java.util.stream.Stream; @WithSession @ApplicationScoped public class CompetitionService { + private static final Logger LOGGER = Logger.getLogger(CompetitionService.class); @Inject CompetitionRepository repository; @@ -65,6 +72,10 @@ public class CompetitionService { @Inject CompetPermService permService; + @SuppressWarnings("CdiInjectionPointsInspection") + @Inject + ReactiveMailer reactiveMailer; + @Inject Vertx vertx; @@ -249,7 +260,7 @@ public class CompetitionService { if ("admin".equals(source)) return permService.hasEditPerm(securityCtx, id) .chain(c -> findComb(data.getLicence(), data.getFname(), data.getLname()) - .chain(combModel -> updateRegister(id, data, c, combModel, true))) + .chain(combModel -> updateRegister(data, c, combModel, true))) .chain(r -> Mutiny.fetch(r.getMembre().getLicences()) .map(licences -> SimpleRegisterComb.fromModel(r, licences))); if ("club".equals(source)) @@ -269,7 +280,7 @@ public class CompetitionService { throw new DForbiddenException( "Vous n'avez pas le droit d'inscrire ce membre (par décision de l'administrateur de la compétition)"); })) - .chain(combModel -> updateRegister(id, data, c, combModel, false))) + .chain(combModel -> updateRegister(data, c, combModel, false))) .chain(r -> Mutiny.fetch(r.getMembre().getLicences()) .map(licences -> SimpleRegisterComb.fromModel(r, licences))); @@ -286,13 +297,13 @@ public class CompetitionService { throw new DForbiddenException( "Vous n'avez pas le droit de vous inscrire (par décision de l'administrateur de la compétition)"); })) - .chain(combModel -> updateRegister(id, data, c, combModel, false))) + .chain(combModel -> updateRegister(data, c, combModel, false))) .map(r -> SimpleRegisterComb.fromModel(r, List.of())); } - private Uni updateRegister(Long id, RegisterRequestData data, CompetitionModel c, + private Uni updateRegister(RegisterRequestData data, CompetitionModel c, MembreModel combModel, boolean admin) { - return registerRepository.find("competition.id = ?1 AND membre = ?2", id, combModel).firstResult() + return registerRepository.find("competition = ?1 AND membre = ?2", c, combModel).firstResult() .onFailure().recoverWithNull() .map(Unchecked.function(r -> { if (r != null) { @@ -480,4 +491,69 @@ public class CompetitionService { })) .call(__ -> cache.invalidate(data.getId())); } + + public Uni registerHelloAsso(NotificationData data) { + String organizationSlug = data.getOrganizationSlug(); + String formSlug = data.getFormSlug(); + RegisterRequestData req = new RegisterRequestData(null, "", "", null, 0, false); + + return repository.find("data1 = ?1 AND data2 = ?2", organizationSlug, formSlug).firstResult() + .onFailure().recoverWithNull() + .chain(cm -> { + Uni uni = Uni.createFrom().nullItem(); + List place = List.of(cm.getData3().toLowerCase().split(";")); + List fail = new ArrayList<>(); + + for (NotificationData.Item item : data.getItems()) { + if (!place.contains(item.getName().toLowerCase())) + continue; + if (item.getCustomFields() == null || item.getCustomFields().isEmpty()) { + fail.add("%s %s - licence n°???".formatted(item.getUser().getLastName(), + item.getUser().getFirstName())); + continue; + } + + Optional optional = item.getCustomFields().stream() + .filter(cf -> cf.getName().equalsIgnoreCase("Numéro de licence")).findAny().map( + NotificationData.CustomField::getAnswer).map(Long::valueOf); + + if (optional.isPresent()) { + uni = uni.call(__ -> membreService.getByLicence(optional.get()) + .invoke(Unchecked.consumer(m -> { + if (m == null) + throw new NotFoundException(); + })) + .chain(m -> updateRegister(req, cm, m, true))) + .onFailure().recoverWithItem(throwable -> { + fail.add("%s %s - licence n°%d".formatted(item.getUser().getLastName(), + item.getUser().getFirstName(), optional.get())); + return null; + }) + .replaceWithVoid(); + } else { + fail.add("%s %s - licence n°???".formatted(item.getUser().getLastName(), + item.getUser().getFirstName())); + } + } + + return uni.call(__ -> fail.isEmpty() ? Uni.createFrom().nullItem() : + reactiveMailer.send( + Mail.withText(cm.getData4(), + "FFSAF - Compétition - Erreur HelloAsso", + String.format( + """ + Bonjour, + + Une erreur a été rencontrée lors de l'enregistrement d'une inscription à votre compétition %s pour les combattants suivants: + %s + + Cordialement, + L'intranet de la FFSAF + """, cm.getName(), String.join("\r\n", fail)) + ).setFrom("FFSAF ").setReplyTo("support@ffsaf.fr") + ).onFailure().invoke(e -> LOGGER.error("Fail to send email", e))); + }) + .onFailure().invoke(Throwable::printStackTrace) + .map(__ -> Response.ok().build()); + } } diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/WebhookService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/WebhookService.java index 9e011bc..2b5becd 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/service/WebhookService.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/WebhookService.java @@ -13,13 +13,24 @@ public class WebhookService { @Inject CheckoutService checkoutService; + @Inject + CompetitionService competitionService; + @ConfigProperty(name = "helloasso.organizationSlug") String organizationSlug; public Uni helloAssoNotification(HelloassoNotification notification) { - if (notification.getEventType().equals("Payment")){ - if (notification.getData().getOrder().getOrganizationSlug().equalsIgnoreCase(organizationSlug)){ - return checkoutService.paymentStatusChange(notification.getData().getState(), notification.getMetadata()); + if (notification.getEventType().equals("Payment")) { + if (notification.getData().getOrder().getFormType().equals("Checkout")) { + if (notification.getData().getOrder().getOrganizationSlug().equalsIgnoreCase(organizationSlug)) { + return checkoutService.paymentStatusChange(notification.getData().getState(), + notification.getMetadata()); + } + } + }else if (notification.getEventType().equals("Order")){ + if (notification.getData().getFormType().equals("Event")) { + + return competitionService.registerHelloAsso(notification.getData()); } } diff --git a/src/main/java/fr/titionfire/ffsaf/rest/client/dto/NotificationData.java b/src/main/java/fr/titionfire/ffsaf/rest/client/dto/NotificationData.java index 8d79e6e..b364b87 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/client/dto/NotificationData.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/client/dto/NotificationData.java @@ -5,6 +5,8 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import java.util.List; + @Data @NoArgsConstructor @AllArgsConstructor @@ -12,11 +14,14 @@ import lombok.NoArgsConstructor; public class NotificationData { private Order order; private Integer id; + private String formSlug; + private String formType; private String organizationSlug; private String checkoutIntentId; private String oldSlugOrganization; // Pour les changements de nom d'association private String newSlugOrganization; private String state; // Pour les formulaires + private List items; @Data @@ -26,5 +31,35 @@ public class NotificationData { public static class Order { private Integer id; private String organizationSlug; + private String formSlug; + private String formType; + } + + @Data + @NoArgsConstructor + @AllArgsConstructor + @RegisterForReflection + public static class Item { + private String name; + private User user; + private List customFields; + } + + @Data + @NoArgsConstructor + @AllArgsConstructor + @RegisterForReflection + public static class User { + private String firstName; + private String lastName; + } + + @Data + @NoArgsConstructor + @AllArgsConstructor + @RegisterForReflection + public static class CustomField { + private String name; + private String answer; } } diff --git a/src/main/java/fr/titionfire/ffsaf/rest/data/RegisterRequestData.java b/src/main/java/fr/titionfire/ffsaf/rest/data/RegisterRequestData.java index eb9c50b..11fa9aa 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/data/RegisterRequestData.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/data/RegisterRequestData.java @@ -1,9 +1,13 @@ package fr.titionfire.ffsaf.rest.data; import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; @Data +@AllArgsConstructor +@NoArgsConstructor @RegisterForReflection public class RegisterRequestData { private Long licence;