Skip to main content

Support temps réel

Vue d’ensemble

Le support temps réel d’UnikSim repose sur Socket.IO, le backend NestJS et un modèle événementiel basé sur EventEmitter. HTTP reste la source de persistance et les sockets servent à diffuser les changements aux utilisateurs et aux administrateurs.

1. Architecture temps réel

flowchart LR
    U[Utilisateur] --> FE[Frontend Angular / App]
    A[Administrateur] --> ADM[Interface admin]

    FE <--> WS[SupportGateway - Socket.IO]
    ADM <--> WS

    FE --> HTTP[API HTTP NestJS]
    ADM --> HTTP

    HTTP --> SS[SupportService]
    SS --> DB[(MySQL)]
    SS --> EVT[EventEmitter]
    EVT --> WS

    WS --> PRES[PresenceService]
    WS --> ROOMS[Rooms Socket.IO]

    ROOMS --> UC[user_userId]
    ROOMS --> AC[admins]
    ROOMS --> CC[conversation_conversationId]

À la connexion, le gateway authentifie le JWT, attache l’utilisateur au socket et enregistre son état de présence. Les conversations utilisent des rooms dédiées, tandis que les événements destinés au compte ou aux administrateurs sont envoyés dans leurs rooms respectives.

2. Envoi d’un message utilisateur

sequenceDiagram
    actor U as Utilisateur
    participant FE as Frontend
    participant API as SupportService
    participant DB as MySQL
    participant EVT as EventEmitter
    participant WS as SupportGateway

    U->>FE: Envoie un message
    FE->>API: POST message
    API->>API: Valide conversation et piece jointe
    API->>DB: Sauvegarde message USER
    DB-->>API: Message cree
    API->>EVT: MESSAGE_CREATED
    EVT->>WS: evenement
    WS-->>FE: message.created

Le message est d’abord persisté, puis diffusé. Le socket ne remplace donc pas la persistance HTTP : il propage simplement le nouvel état.

3. Réponse IA dans une conversation non escaladée

sequenceDiagram
    actor U as Utilisateur
    participant SS as SupportService
    participant AI as AIService
    participant DB as MySQL
    participant EVT as EventEmitter
    participant WS as SupportGateway

    U->>SS: Message utilisateur
    SS->>SS: Charge historique + langue + retrievalContexts
    SS->>AI: queryRAG(...)
    AI-->>SS: answer / insufficient_context

    alt answer
        SS->>DB: Sauvegarde message AI
        SS->>EVT: MESSAGE_CREATED
        EVT->>WS: evenement
        WS-->>U: message.created
    else insufficient_context
        SS->>DB: Sauvegarde suggestion d'escalade
        SS->>EVT: MESSAGE_CREATED
        EVT->>WS: evenement
        WS-->>U: Proposition de support humain
    end

4. Escalade vers un administrateur

sequenceDiagram
    actor U as Utilisateur
    participant SS as SupportService
    participant DB as MySQL
    participant AI as AIService
    participant EVT as EventEmitter
    participant WS as SupportGateway
    actor A as Administrateur

    U->>SS: Demande escalade
    SS->>DB: Transaction atomique conversation.escalated false -> true
    SS->>DB: Cree ticket OPEN
    SS->>DB: Sauvegarde message SYSTEM
    SS->>EVT: MESSAGE_CREATED + TICKET_UPDATED
    EVT->>WS: evenements
    WS-->>U: mise a jour conversation
    WS-->>A: ticket.updated
    SS->>AI: Resume asynchrone de la conversation
    AI-->>SS: Resume admin

    A->>SS: Assign / repond au ticket
    SS->>DB: Sauvegarde ADMIN message + read marker
    SS->>EVT: MESSAGE_CREATED + TICKET_UPDATED
    EVT->>WS: evenements
    WS-->>U: message.created
    WS-->>A: ticket.updated

La création du ticket est protégée par une transaction atomique afin d’éviter plusieurs tickets pour la même conversation. Le premier administrateur qui répond peut automatiquement devenir l’assigné du ticket.

5. Cycle de vie d’un ticket

stateDiagram-v2
    [*] --> OPEN: escalade
    OPEN --> IN_PROGRESS: assignTicket / premiere reponse admin
    IN_PROGRESS --> RESOLVED: resolveTicket
    OPEN --> RESOLVED: resolveTicket
    RESOLVED --> IN_PROGRESS: reopenTicket si assigne
    RESOLVED --> OPEN: reopenTicket sans assigne

Présence, typing et reconnexion

Le frontend maintient aussi plusieurs signaux temps réel :

  • typing : diffusé uniquement aux autres sockets de la conversation active.
  • online / away : dérivé de l’activité navigateur, du focus et d’un timer d’inactivité.
  • telemetry : navigateur, plateforme, résolution, langue, seulement si le consentement est accordé.
  • game presence : jeu actif, productCode et heure de démarrage.
  • reconnect : le client renvoie présence, télémétrie, conversation active et présence de jeu.

Read markers et unread counts

Les marqueurs de lecture utilisateur et administrateur sont séparés. L’utilisateur compte comme non lus les messages ADMIN postérieurs à son marqueur, alors que l’administrateur compte les messages USER postérieurs à son propre marqueur.

  • lastUserReadAt / lastUserReadMessageId
  • lastAdminReadAt / lastAdminReadMessageId

Fichiers principaux

  • uniksim-back-end/src/gateway/support.gateway.ts
  • uniksim-back-end/src/service/support.service.ts
  • uniksimFrontEnd/src/app/service/support/support-socket.service.ts

Points d’attention

  • Ne pas utiliser Socket.IO comme source de vérité : la persistance doit précéder la diffusion.
  • Vérifier l’appartenance à une conversation avant de joindre sa room.
  • Les non-admins ne doivent pouvoir vérifier que la présence des administrateurs qui leur sont assignés.
  • Après reconnexion, le client doit restaurer ses rooms et états actifs.

Architecture frontend du support

Le frontend ne traite pas Socket.IO comme une base de données. Les écritures importantes passent par l’API HTTP, puis les événements temps réel mettent l’interface à jour dès que le backend confirme le nouvel état.

flowchart LR
    UI[Composants UI]
    API[Services HTTP]
    SOCK[SupportSocketService]
    STATE[Observables / état local]
    WS[SupportGateway]
    BACK[SupportService]

    UI -->|actions utilisateur| API
    API --> BACK
    BACK -->|EventEmitter| WS
    WS -->|message.created / ticket.updated / typing / presence| SOCK
    SOCK --> STATE
    STATE --> UI

    UI -->|typing / join / leave / status| SOCK
    SOCK --> WS

SupportSocketService

uniksimFrontEnd/src/app/service/support/support-socket.service.ts centralise la connexion Socket.IO du frontend. Il expose les principaux événements sous forme de streams/observables afin que les composants n'aient pas à manipuler directement le socket.

    message.created : ajoute ou rafraîchit un message dans la conversation. ticket.updated : rafraîchit l'état du ticket et ses unread counts. typing : affiche l'indicateur de saisie. présence utilisateur/admin : met à jour les états online/away. notification.created : propage une nouvelle notification et déclenche le son associé.

    Reconnexion

    Après une reconnexion Socket.IO, le client restaure explicitement son contexte actif :

      repasse l'utilisateur en ligne; renvoie la télémétrie si le consentement est toujours autorisé; rejoint à nouveau la conversation active; renvoie la présence du jeu actif.

      Séparation HTTP / WebSocket

      HTTP WebSocket Créer/envoyer un message Recevoir message.created Assigner/résoudre/réouvrir un ticket Recevoir ticket.updated Marquer comme lu Présence, typing et changements temps réel Persistance et validation Synchronisation immédiate de l'interface