package fr.titionfire.ffsaf.domain.service; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import fr.titionfire.ffsaf.data.model.AffiliationModel; import fr.titionfire.ffsaf.data.model.ClubModel; import fr.titionfire.ffsaf.data.model.MembreModel; import fr.titionfire.ffsaf.data.repository.ClubRepository; import fr.titionfire.ffsaf.data.repository.CombRepository; import fr.titionfire.ffsaf.net2.ServerCustom; import fr.titionfire.ffsaf.net2.data.SimpleClubModel; import fr.titionfire.ffsaf.net2.request.SReqClub; import fr.titionfire.ffsaf.rest.data.*; import fr.titionfire.ffsaf.rest.exception.DBadRequestException; import fr.titionfire.ffsaf.rest.exception.DForbiddenException; import fr.titionfire.ffsaf.rest.exception.DNotFoundException; import fr.titionfire.ffsaf.rest.from.FullClubForm; import fr.titionfire.ffsaf.rest.from.PartClubForm; import fr.titionfire.ffsaf.utils.*; import io.quarkus.hibernate.reactive.panache.Panache; import io.quarkus.hibernate.reactive.panache.PanacheQuery; import io.quarkus.hibernate.reactive.panache.common.WithSession; import io.quarkus.panache.common.Page; import io.quarkus.panache.common.Sort; import io.quarkus.vertx.VertxContextSupport; import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.unchecked.Unchecked; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.hibernate.reactive.mutiny.Mutiny; import java.util.*; import java.util.function.Consumer; import static fr.titionfire.ffsaf.net2.Client_Thread.MAPPER; @WithSession @ApplicationScoped public class ClubService { @Inject ClubRepository repository; @Inject ServerCustom serverCustom; @Inject CombRepository combRepository; @Inject KeycloakService keycloakService; @ConfigProperty(name = "upload_dir") String media; @Inject LoggerService ls; public SimpleClubModel findByIdOptionalClub(long id) throws Throwable { return VertxContextSupport.subscribeAndAwait( () -> Panache.withTransaction(() -> repository.findById(id).map(SimpleClubModel::fromModel))); } public Collection findAllClub() throws Throwable { return VertxContextSupport.subscribeAndAwait(() -> Panache.withTransaction( () -> repository.findAll().list() .map(clubModels -> clubModels.stream().map(SimpleClubModel::fromModel).toList()))); } public Uni> getAll() { return repository.listAll(); } public Uni setClubId(Long id, String id1) { return repository.findById(id).chain(clubModel -> { ls.logChange("KC UUID", clubModel.getClubId(), id1, clubModel); clubModel.setClubId(id1); return Panache.withTransaction(() -> repository.persist(clubModel)) .call(() -> ls.append()); }); } public Uni> search(Integer limit, int page, String search, String country) { if (search == null) search = ""; search = search + "%"; PanacheQuery query; if (country == null || country.isBlank()) query = repository.find("LOWER(name) LIKE LOWER(?1)", Sort.ascending("name"), search).page(Page.ofSize(limit)); else query = repository.find("LOWER(name) LIKE LOWER(?1) AND country LIKE ?2", Sort.ascending("name"), search, country + "%").page(Page.ofSize(limit)); return getPageResult(query, limit, page); } private Uni> getPageResult(PanacheQuery query, int limit, int page) { return Uni.createFrom().item(new PageResult()) .invoke(result -> result.setPage(page)) .invoke(result -> result.setPage_size(limit)) .call(result -> query.count().invoke(result::setResult_count)) .call(result -> query.pageCount() .invoke(Unchecked.consumer(pages -> { if (page > pages) throw new DBadRequestException("Page out of range"); })) .invoke(result::setPage_count)) .call(result -> query.page(Page.of(page, limit)).list() .map(membreModels -> membreModels.stream().map(SimpleClubList::fromModel).toList()) .invoke(result::setResult)); } public Uni getById(long id) { return repository.findById(id).call(m -> Mutiny.fetch(m.getContact())); } public Uni getByClubId(String clubId) { return repository.find("clubId", clubId).firstResult(); } public Uni getOfUser(SecurityCtx securityCtx) { return combRepository.find("userId = ?1", securityCtx.getSubject()).firstResult() .invoke(Unchecked.consumer(m -> { if (m == null || m.getClub() == null) throw new DNotFoundException("Club non trouvé"); })) .map(MembreModel::getClub) .call(club -> Mutiny.fetch(club.getContact())); } public Uni> getClubDesk(Consumer consumer, long id) { return repository.findById(id).invoke(consumer) .chain(club -> combRepository.list("club = ?1", club)) .map(combs -> combs.stream() .filter(o -> o.getRole() != null && o.getRole().level >= RoleAsso.MEMBREBUREAU.level) .sorted((o1, o2) -> o2.getRole().level - o1.getRole().level) .map(DeskMember::fromModel) .toList()); } public Uni> getMembers(SecurityCtx securityCtx) { return combRepository.find("userId = ?1", securityCtx.getSubject()).firstResult() .invoke(Unchecked.consumer(m -> { if (m == null || m.getClub() == null) throw new DNotFoundException("Club non trouvé"); if (!securityCtx.isInClubGroup(m.getClub().getId())) throw new DForbiddenException(); })) .chain(m -> combRepository.list("club = ?1", m.getClub())) .map(membreModels -> membreModels.stream() .map(m -> new VerySimpleMembre(m.getLname(), m.getFname(), m.getLicence())).toList()); } public Uni updateOfUser(SecurityCtx securityCtx, PartClubForm form) { TypeReference> typeRef = new TypeReference<>() { }; return combRepository.find("userId = ?1", securityCtx.getSubject()).firstResult() .invoke(Unchecked.consumer(m -> { if (m == null || m.getClub() == null) throw new DNotFoundException("Club non trouvé"); if (!securityCtx.isInClubGroup(m.getClub().getId())) throw new DForbiddenException(); })) .map(MembreModel::getClub) .call(club -> Mutiny.fetch(club.getContact())) .chain(Unchecked.function(club -> { ls.logChange("Contact interne", club.getContact_intern(), form.getContact_intern(), club); club.setContact_intern(form.getContact_intern()); ls.logChange("Adresse administrative", club.getAddress(), form.getAddress(), club); club.setAddress(form.getAddress()); try { if (!Objects.equals(club.getContact(), MAPPER.readValue(form.getContact(), typeRef))) ls.logUpdate("Contact(s)...", club); club.setContact(MAPPER.readValue(form.getContact(), typeRef)); } catch (JsonProcessingException e) { throw new DBadRequestException("Erreur de format des contacts"); } ls.logChange("Lieux d'entrainements", club.getTraining_location(), form.getTraining_location(), club); club.setTraining_location(form.getTraining_location()); ls.logChange("Horaires d'entrainements", club.getTraining_day_time(), form.getTraining_day_time(), club); club.setTraining_day_time(form.getTraining_day_time()); return Panache.withTransaction(() -> repository.persist(club)).call(() -> ls.append()); })) .map(__ -> "OK"); } public Uni update(long id, FullClubForm input) { return repository.findById(id).call(m -> Mutiny.fetch(m.getContact())) .onItem().transformToUni(Unchecked.function(m -> { TypeReference> typeRef = new TypeReference<>() { }; m.setName(input.getName()); m.setCountry(input.getCountry()); m.setInternational(input.isInternational()); if (!input.isInternational()) { ls.logChange("Lieux d'entrainements", m.getTraining_location(), input.getTraining_location(), m); m.setTraining_location(input.getTraining_location()); ls.logChange("Horaires d'entrainements", m.getTraining_day_time(), input.getTraining_day_time(), m); m.setTraining_day_time(input.getTraining_day_time()); ls.logChange("Contact interne", m.getContact_intern(), input.getContact_intern(), m); m.setContact_intern(input.getContact_intern()); ls.logChange("N° RNA", m.getRNA(), input.getRna(), m); m.setRNA(input.getRna()); if (input.getSiret() != null && !input.getSiret().isBlank()) { ls.logChange("N° SIRET", m.getSIRET(), input.getSiret(), m); m.setSIRET(Long.parseLong(input.getSiret())); } ls.logChange("Adresse administrative", m.getAddress(), input.getAddress(), m); m.setAddress(input.getAddress()); try { if (!Objects.equals(m.getContact(), MAPPER.readValue(input.getContact(), typeRef))) ls.logUpdate("Contact(s)...", m); m.setContact(MAPPER.readValue(input.getContact(), typeRef)); } catch (JsonProcessingException e) { throw new DBadRequestException("Erreur de format des contacts"); } } return Panache.withTransaction(() -> repository.persist(m)).call(() -> ls.append()); })) .invoke(membreModel -> SReqClub.sendIfNeed(serverCustom.clients, SimpleClubModel.fromModel(membreModel))) .map(__ -> "OK"); } public Uni add(FullClubForm input) { TypeReference> typeRef = new TypeReference<>() { }; return Uni.createFrom().nullItem() .chain(() -> { ClubModel clubModel = new ClubModel(); clubModel.setName(input.getName()); clubModel.setCountry(input.getCountry()); clubModel.setInternational(input.isInternational()); clubModel.setNo_affiliation(null); if (!input.isInternational()) { clubModel.setTraining_location(input.getTraining_location()); clubModel.setTraining_day_time(input.getTraining_day_time()); clubModel.setContact_intern(input.getContact_intern()); clubModel.setRNA(input.getRna()); if (input.getSiret() != null && !input.getSiret().isBlank()) clubModel.setSIRET(Long.parseLong(input.getSiret())); clubModel.setAddress(input.getAddress()); try { clubModel.setContact(MAPPER.readValue(input.getContact(), typeRef)); } catch (JsonProcessingException ignored) { } } return Panache.withTransaction(() -> repository.persist(clubModel)); }) .call(clubModel -> ls.logAAdd(clubModel)) .call(clubModel -> keycloakService.getGroupFromClub(clubModel)) // create group in keycloak .invoke(clubModel -> SReqClub.sendAddIfNeed(serverCustom.clients, SimpleClubModel.fromModel(clubModel))) .map(ClubModel::getId); } public Uni delete(long id) { return repository.findById(id) .chain(club -> combRepository.list("club = ?1", club) .map(combModels -> combModels.stream().peek(combModel -> { combModel.setClub(null); combModel.setRole(RoleAsso.MEMBRE); }).toList()) .call(list -> (list.isEmpty()) ? Uni.createFrom().voidItem() : Uni.join().all(list.stream().filter(m -> m.getUserId() != null) .map(m -> keycloakService.clearUser(m.getUserId())).toList()) .andCollectFailures()) .chain(list -> Panache.withTransaction(() -> combRepository.persist(list))) .map(o -> club) ) .call(clubModel -> (clubModel.getClubId() == null) ? Uni.createFrom() .voidItem() : keycloakService.removeClubGroup(clubModel.getClubId())) .invoke(membreModel -> SReqClub.sendRmIfNeed(serverCustom.clients, id)) .call(clubModel -> ls.logADelete(clubModel)) .chain(clubModel -> Panache.withTransaction(() -> repository.delete(clubModel))) .call(__ -> Utils.deleteMedia(id, media, "ppClub")) .call(__ -> Utils.deleteMedia(id, media, "clubStatus")); } public Uni getRenewData(long id, List mIds) { RenewAffData data = new RenewAffData(); return repository.findById(id) .call(clubModel -> Mutiny.fetch(clubModel.getAffiliations())) .invoke(clubModel -> { data.setName(clubModel.getName()); data.setSiret(clubModel.getSIRET()); data.setRna(clubModel.getRNA()); data.setAddress(clubModel.getAddress()); data.setSaison( clubModel.getAffiliations().stream().max(Comparator.comparing(AffiliationModel::getSaison)) .map(AffiliationModel::getSaison).map(i -> Math.min(i + 1, Utils.getSaison() + 1)) .orElse(Utils.getSaison())); }) .chain(club -> combRepository.list("id IN ?1", mIds)) .invoke(combs -> data.setMembers(combs.stream() .filter(o -> o.getRole() != null && o.getRole().level >= RoleAsso.MEMBREBUREAU.level) .sorted((o1, o2) -> o2.getRole().level - o1.getRole().level) .map(RenewAffData.RenewMember::new) .toList())) .map(o -> data); } public Uni> getMapData() { return repository.list("international", false).toMulti().flatMap(list -> Multi.createFrom().iterable(list)) .call(clubModel -> Mutiny.fetch(clubModel.getContact())) .map(clubModel -> { ClubMapData data = new ClubMapData(); data.setName(clubModel.getName()); data.setUuid(clubModel.getClubId()); if (clubModel.getTraining_location() != null) { try { MAPPER.readTree(clubModel.getTraining_location()).forEach(l -> { ClubMapData.Location loc = new ClubMapData.Location(); loc.setLat(l.get("lat").asDouble()); loc.setLng(l.get("lng").asDouble()); loc.setAddr(l.get("text").asText()); data.training_location.add(loc); }); } catch (JsonProcessingException ignored) { } } data.setTraining_day_time(clubModel.getTraining_day_time()); data.setContact(clubModel.getContact()); return data; }).collect().asList(); } }