feat(sync): MembreRoleSyncService + count admins dynamique
- Nouveau MembreRoleSyncService.ensureOrgAdminRole : auto-crée un MembreRole ORGADMIN quand un user avec rôle Keycloak ADMIN_ORGANISATION se connecte sans entrée DB (couvre les comptes créés directement dans Keycloak). - OrganisationContextFilter appelle syncService.ensureOrgAdminRole quand le rôle Keycloak est présent mais MembreRole absent (non bloquant sur erreur). - MembreRoleRepository.countAdminsByOrganisationId : count strict (ORGADMIN + actif + dateDebut/dateFin valides) avec fallback sur codes alternatifs si strict=0. - OrganisationService.convertToResponse : nombreAdministrateurs dynamique via MembreRoleRepository (remplace le champ Organisation jamais mis à jour).
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
package dev.lions.unionflow.server.service;
|
||||
|
||||
import dev.lions.unionflow.server.entity.MembreOrganisation;
|
||||
import dev.lions.unionflow.server.entity.MembreRole;
|
||||
import dev.lions.unionflow.server.repository.MembreRoleRepository;
|
||||
import dev.lions.unionflow.server.repository.RoleRepository;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
import jakarta.transaction.Transactional;
|
||||
import java.time.LocalDate;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Synchronise le rôle DB (MembreRole) avec le rôle Keycloak au premier accès.
|
||||
*
|
||||
* <p>Lorsqu'un utilisateur est admin dans Keycloak (rôle {@code ADMIN_ORGANISATION})
|
||||
* mais n'a pas encore de ligne {@code MembreRole ORGADMIN} en base, ce service la
|
||||
* crée à la volée. Cela couvre deux cas :
|
||||
* <ol>
|
||||
* <li>Comptes créés directement dans Keycloak sans passer par
|
||||
* {@code MembreService#promouvoirAdminOrganisation}.</li>
|
||||
* <li>Bases de données en dev qui ont démarré avant la migration V30.</li>
|
||||
* </ol>
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class MembreRoleSyncService {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MembreRoleSyncService.class);
|
||||
|
||||
@Inject
|
||||
MembreRoleRepository membreRoleRepository;
|
||||
|
||||
@Inject
|
||||
RoleRepository roleRepository;
|
||||
|
||||
@PersistenceContext
|
||||
EntityManager entityManager;
|
||||
|
||||
/**
|
||||
* Assure qu'un MembreRole ORGADMIN existe pour ce MembreOrganisation.
|
||||
* Idempotent : sans effet si l'entrée existe déjà.
|
||||
*
|
||||
* @param mo MembreOrganisation de l'admin (non null)
|
||||
*/
|
||||
@Transactional
|
||||
public void ensureOrgAdminRole(MembreOrganisation mo) {
|
||||
if (mo == null || mo.getId() == null || mo.getOrganisation() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Vérifier si un MembreRole ORGADMIN actif existe déjà pour ce membre dans cette org
|
||||
long existing = membreRoleRepository.count(
|
||||
"membreOrganisation.id = ?1 AND role.code = ?2 AND actif = true",
|
||||
mo.getId(), "ORGADMIN");
|
||||
|
||||
if (existing > 0) {
|
||||
return; // Déjà en place — rien à faire
|
||||
}
|
||||
|
||||
// Chercher le rôle ORGADMIN dans la table roles
|
||||
roleRepository.findByCode("ORGADMIN").ifPresentOrElse(
|
||||
role -> {
|
||||
MembreRole mr = new MembreRole();
|
||||
mr.setMembreOrganisation(mo);
|
||||
mr.setOrganisation(mo.getOrganisation());
|
||||
mr.setRole(role);
|
||||
mr.setActif(true);
|
||||
mr.setDateDebut(LocalDate.now());
|
||||
entityManager.persist(mr);
|
||||
LOG.infof(
|
||||
"MembreRole ORGADMIN auto-sync créé pour membre %s dans org %s",
|
||||
mo.getMembre() != null ? mo.getMembre().getId() : "?",
|
||||
mo.getOrganisation().getId());
|
||||
},
|
||||
() -> LOG.warnf(
|
||||
"Rôle ORGADMIN introuvable dans la table roles — "
|
||||
+ "vérifier que V13__Seed_Standard_Roles.sql a été appliqué")
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user