diff --git a/NOTIFICATION.MD b/NOTIFICATION.MD new file mode 100644 index 0000000..5023758 --- /dev/null +++ b/NOTIFICATION.MD @@ -0,0 +1,190 @@ +# Système de notification de l'application +## Vue d'ensemble +Le système de notification de l'application permet d'informer les utilisateurs de diverse action. + +## Architecture +### Composants principaux +1. **Service de Notification** : Gère la création, l'envoi et le suivi des notifications. +2. **Interface Utilisateur** : Affiche les notifications aux utilisateurs via des pops-ups, des bannières ou des emails. +3. **Template Email** : Modèles prédéfinis pour les notifications par email. +4. **Type D'action** : Enum d'action qui déclenche des notifications (ex: nouvel utilisateur, utilisateur actif, ...). + +### Service de Notification +Le service de notification est responsable de la gestion des notifications. Il inclut les fonctionnalités suivantes : +- Création de notifications basées sur des événements spécifiques. +- Envoi de notifications via différents canaux (email, in-app). +- Suivi de l'état des notifications (envoyé, lu, etc.). + +### Interface Utilisateur +L'interface utilisateur affiche les notifications de manière conviviale. Les notifications peuvent apparaître sous forme +de pops-ups, de bannières ou d'emails. (Possibilité d'intéragir avec les notifications) + +### Template Email +Les templates email sont utilisés pour formater les notifications envoyées par email. Chaque type de notification +a son propre template pour assurer une communication claire et cohérente. + + +### Type d'action +``` +enum ActionType: String { + case NewUser = "NEW_USER"; + case ActiveUser = "ACTIVE_USER"; + case PasswordReset = "PASSWORD_RESET"; + case SubscriptionExpired = "SUBSCRIPTION_EXPIRED"; + case OrganizationInvited = "ORGANIZATION_INVITED"; + case OrganizationInactive = "ORGANIZATION_INACTIVE"; + case OrganizationDeleted = "ORGANIZATION_DELETED"; + case OrginizationUserInvited = "ORGANIZATION_USER_INVITED"; + case UserDeleted = "USER_DELETED"; +} +``` + +## Flux de travail +1. L’administrateur crée un utilisateur depuis l’interface (formulaire “Créer un utilisateur”). +2. Le contrôleur valide la requête et appelle le cas d’usage UserAdministrationService->handle(ActionType::NewUser, $admin, $payload). +3. Le service crée l’utilisateur en base avec le statut INVITED, associe l’organisation de l’admin, et génère un lien signé/jeton de setup de mot de passe (TTL). +4. Le service publie un événement de domaine UserInvitedEvent { userId, adminId, organizationId } sur Messenger (transport async). +5. Handler async A — SendUserInvitationEmailHandler: +6. Construit l’email via Symfony Mailer + Twig (emails/user_invitation.html.twig) avec le lien de définition de mot de passe. +7. Envoie le mail à l’utilisateur invité. +8. Handler async B — NotifyAdminInvitationSentHandler: +9. Crée une notification interne (Notifier, canal “in‑app”). +10. Pousse un événement temps réel via Mercure sur le topic admin/{adminId}/events avec le type INVITATION_EMAIL_SENT. +11. L’UI admin affiche un toast/bannière confirmant “Email d’invitation envoyé”. +12. L’utilisateur ouvre l’email et clique le lien de définition de mot de passe. +13. Le PasswordSetupController vérifie la signature/le jeton et la validité (TTL), affiche le formulaire, puis enregistre le nouveau mot de passe. +14. À la réussite, l’utilisateur passe au statut ACTIVE et l’action publie UserActivatedEvent { userId, adminId, organizationId } sur Messenger (async). +15. Handler async C — NotifyAdminUserActivatedHandler: +16. Crée une notification interne (Notifier, canal “in‑app”) “Compte activé”. +17. Pousse un événement Mercure sur admin/{adminId}/events avec le type USER_ACTIVATED. +18. L’UI admin met à jour la liste des membres (badge “Actif”) et affiche un toast confirmant l’activation. +19. Journalisation/Audit: +20. Chaque handler écrit une trace (succès/échec) en base ou dans un EmailLog/NotificationLog. +21. En cas d’échec d’envoi, Messenger applique la stratégie de retry puis bascule en file failed si nécessaire (tableau de bord de supervision). +22. Cas “utilisateur existant ajouté à une autre organisation”: +23. Si l’email existe déjà, on rattache l’utilisateur à la nouvelle organisation et on publie OrganizationUserInvitedEvent. +24. Handler dédié envoie un email d’information (“Vous avez été ajouté à une nouvelle organisation”) et notifie l’admin via Notifier + Mercure. +25. Cas d’actions dérivées par enum: +26. ActionType::NewUser → déclenche UserInvitedEvent (steps 3–6). +27. ActionType::ActiveUser (si activé par un flux admin) → déclenche directement UserActivatedEvent (steps 9–10). +28. ActionType::OrganizationUserInvited → flux similaire au point 12 pour la multi‑organisation. +29. Autres actions (PasswordReset, UserDeleted, etc.) suivent le même patron: contrôleur → service (match enum) → événement Messenger → handlers (Mailer/Notifier/Mercure) → UI temps réel. + +## Stack technologique +- Symfony Messenger: asynchrone, retries, découplage des I/O lents. +- Symfony Mailer + Twig: emails d’invitation et d’information. +- Symfony Notifier (canal in‑app) + Mercure: notifications persistées + push temps réel vers l’UI admin. +- Enum ActionType: routage clair dans l’application, évite la logique string‑based. + + +```mermaid +flowchart LR +%% Couche 1: Action initiale + A[User action event - Admin cree un utilisateur] --> B[HTTP controller API - Symfony] + B --> C[Domain service - UserAdministrationService] + C -->|Inspecte enum ActionType::NewUser| C1[Create user - status INVITED - liaison organisation - genere lien jeton mot de passe TTL] + C1 --> D[Dispatch UserInvitedEvent - userId adminId organizationId - vers Symfony Messenger bus] + +%% Couche 2: Messaging / Infra + D --> E[Transport async - AMQP / Redis / Doctrine] + E --> RQ[Retry queue] + E --> FQ[Failed queue - dead letter] + E --> W[Workers Messenger] + F[Supervisor / systemd] --> W + +%% Monolog transversal (logs a chaque etape) + A --> LOG_GLOBAL[Monolog - log event initial] + B --> LOG_GLOBAL + C --> LOG_GLOBAL + C1 --> LOG_GLOBAL + D --> LOG_GLOBAL + E --> LOG_GLOBAL + RQ --> LOG_GLOBAL + FQ --> LOG_GLOBAL + W --> LOG_GLOBAL + +%% Handlers pour l'invitation + W --> H1[Handler A - Symfony Mailer + Twig] + H1 --> H1o[Email d'invitation avec lien setup mot de passe] + H1 --> LOG_GLOBAL + + W --> H2[Handler B - Symfony Notifier in-app] + H2 --> UI1[Notification UI admin - Email d'invitation envoye] + H2 --> LOG_GLOBAL + + W -. optionnel .-> WH1[Webhook HTTP sortant - invitation envoyee] + WH1 --> LOG_GLOBAL + W -. optionnel .-> SMS1[SMS gateway - SMS invitation] + SMS1 --> LOG_GLOBAL + W -. optionnel .-> PUSH1[Mobile push service - notification mobile] + PUSH1 --> LOG_GLOBAL + + RQ --> METRICS[Metrics et dashboard] + FQ --> METRICS + LOG_GLOBAL --> METRICS + +%% Flux activation utilisateur + subgraph Activation du compte + UA[User action event - Invite clique le lien] --> PS[HTTP controller API - PasswordSetupController] + PS -->|Verifie signature et TTL| PSOK[Set password - user status ACTIVE] + PS --> LOG_GLOBAL + PSOK --> LOG_GLOBAL + + PSOK --> D2[Dispatch UserActivatedEvent - userId adminId organizationId - vers Messenger bus] + D2 --> E2[Transport async] + E2 --> RQ2[Retry queue] + E2 --> FQ2[Failed queue] + E2 --> W2[Workers Messenger] + F --> W2 + + D2 --> LOG_GLOBAL + E2 --> LOG_GLOBAL + RQ2 --> LOG_GLOBAL + FQ2 --> LOG_GLOBAL + W2 --> LOG_GLOBAL + + W2 --> H3[Handler C - Notifier in-app] + H3 --> UI2[Notification UI admin - Compte active] + H3 --> LOG_GLOBAL + + W2 -. optionnel .-> WH2[Webhook HTTP sortant - user active] + WH2 --> LOG_GLOBAL + W2 -. optionnel .-> MAIL2[Mailer ou SMS ou Push - confirmation utilisateur] + MAIL2 --> LOG_GLOBAL + + RQ2 --> METRICS + FQ2 --> METRICS + end + +%% Cas particulier : utilisateur existant ajoute a une nouvelle organisation + C -->|Email deja existant| SP1[Rattache a nouvelle organisation] + SP1 --> LOG_GLOBAL + SP1 --> D3[Dispatch OrganizationUserInvitedEvent] + D3 --> E3[Transport async] --> W3[Workers] + F --> W3 + D3 --> LOG_GLOBAL + E3 --> LOG_GLOBAL + W3 --> LOG_GLOBAL + + W3 --> M3[Mailer - ajoute a une nouvelle organisation] + M3 --> LOG_GLOBAL + W3 --> N3[Notifier in-app - toast admin Utilisateur ajoute] + N3 --> LOG_GLOBAL + W3 -. optionnel .-> WH3[Webhook ou SMS ou Mobile] + WH3 --> LOG_GLOBAL + + M3 --> METRICS + N3 --> METRICS + WH3 --> METRICS + +%% Styles + classDef infra fill:#e8f0fe,stroke:#5b8def,stroke-width:1px; + classDef handler fill:#dcf7e9,stroke:#2ea66a,stroke-width:1px; + classDef ui fill:#f0d9ff,stroke:#9c27b0,stroke-width:1px; + classDef audit fill:#eeeeee,stroke:#9e9e9e,stroke-width:1px; + + class E,E2,E3,RQ,FQ,RQ2,FQ2,METRICS infra; + class W,W2,W3,H1,H2,H3,M3,N3 handler; + class H1o,UI1,UI2 ui; + class LOG_GLOBAL audit; +``` \ No newline at end of file diff --git a/composer.json b/composer.json index 6e099eb..b0e1ac3 100644 --- a/composer.json +++ b/composer.json @@ -32,8 +32,9 @@ "symfony/intl": "7.2.*", "symfony/mailer": "7.2.*", "symfony/mercure-bundle": "^0.3.9", + "symfony/messenger": "7.2.*", "symfony/mime": "7.2.*", - "symfony/monolog-bundle": "^3.0", + "symfony/monolog-bundle": "^3.10", "symfony/notifier": "7.2.*", "symfony/process": "7.2.*", "symfony/property-access": "7.2.*", diff --git a/composer.lock b/composer.lock index de33137..60a8c06 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "2e4c0342c6040486970857205e1e8d75", + "content-hash": "6694f361e2e9943b29abdd66ef40904a", "packages": [ { "name": "aws/aws-crt-php",