feat(members): desactiverMembre cascade complète (Keycloak, Kafka, audit, mono-admin)
Refactor de MembreService.desactiverMembre en 8 étapes transactionnelles : 1. GARDE-FOU mono-admin : refuse 409 Conflict si le membre est le seul ORGADMIN d'au moins une org (évite l'orphelinage). 2. DB : actif=false + statutCompte='DESACTIVE'. 3. Adhésions actives → SUSPENDU + décrément nombreMembres. 4. MembreRole (ORGADMIN, TRESORIER...) → actif=false, dateFin=today. 5. Notifications pending (EN_ATTENTE, ECHEC_TEMPORAIRE) → ANNULEE. 6. Keycloak (lions-user-manager) : user.enabled=false → login bloqué. 7. Kafka : publishMemberDeactivated(membre) sur unionflow.members.events → consumers peuvent réagir (comptes épargne, inscriptions, approvals, etc.) 8. AuditLog MEMBRE_DESACTIVE : opérateur, timestamp, compteurs (RGPD/compliance). Côté liste : - listerMembres/compterMembres : filtre actif=true par défaut (SuperAdmin). - MembreRepository.findDistinctByOrganisationIdIn : idem pour OrgAdmin. Services ajoutés : - AuditService.logMembreDesactive - KafkaEventProducer.publishMemberDeactivated
This commit is contained in:
@@ -43,6 +43,9 @@ public class KafkaEventProducer {
|
||||
@Channel("contributions-events-out")
|
||||
Emitter<Record<String, String>> contributionsEventsEmitter;
|
||||
|
||||
@Channel("chat-messages-out")
|
||||
Emitter<Record<String, String>> chatMessagesEmitter;
|
||||
|
||||
/**
|
||||
* Publie un event d'approbation en attente.
|
||||
*/
|
||||
@@ -116,6 +119,28 @@ public class KafkaEventProducer {
|
||||
publishToChannel(membersEventsEmitter, memberId.toString(), event, "members-events");
|
||||
}
|
||||
|
||||
/**
|
||||
* Publie un event de désactivation de membre (soft delete).
|
||||
* Les consommateurs peuvent réagir : bloquer comptes épargne, annuler inscriptions,
|
||||
* reassigner approvals pending, nettoyer notifications, etc.
|
||||
*/
|
||||
public void publishMemberDeactivated(dev.lions.unionflow.server.entity.Membre membre) {
|
||||
if (membre == null || membre.getId() == null) return;
|
||||
Map<String, Object> data = new java.util.HashMap<>();
|
||||
data.put("membreId", membre.getId().toString());
|
||||
data.put("email", membre.getEmail());
|
||||
data.put("nomComplet", membre.getNomComplet());
|
||||
data.put("numeroMembre", membre.getNumeroMembre());
|
||||
// organisationId principal (si présent) pour routage par org
|
||||
String orgId = membre.getMembresOrganisations() != null
|
||||
&& !membre.getMembresOrganisations().isEmpty()
|
||||
&& membre.getMembresOrganisations().get(0).getOrganisation() != null
|
||||
? membre.getMembresOrganisations().get(0).getOrganisation().getId().toString()
|
||||
: "";
|
||||
var event = buildEvent("MEMBER_DEACTIVATED", orgId, data);
|
||||
publishToChannel(membersEventsEmitter, membre.getId().toString(), event, "members-events");
|
||||
}
|
||||
|
||||
/**
|
||||
* Publie un event de cotisation payée.
|
||||
*/
|
||||
@@ -124,6 +149,15 @@ public class KafkaEventProducer {
|
||||
publishToChannel(contributionsEventsEmitter, contributionId.toString(), event, "contributions-events");
|
||||
}
|
||||
|
||||
/**
|
||||
* Publie un event de nouveau message de chat.
|
||||
* Les clients WebSocket de l'organisation sont notifiés pour rafraîchir leurs messages.
|
||||
*/
|
||||
public void publishNouveauMessage(UUID conversationId, String organizationId, Map<String, Object> messageData) {
|
||||
var event = buildEvent("NOUVEAU_MESSAGE", organizationId, messageData);
|
||||
publishToChannel(chatMessagesEmitter, conversationId.toString(), event, "chat-messages");
|
||||
}
|
||||
|
||||
/**
|
||||
* Construit un event avec structure standardisée.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user