1 Architecture-Technique
tarzzan edited this page 2026-05-20 03:04:10 +00:00

🏗️ Architecture Technique

Stack complète, modèle de données, flux WebSocket et procédures tRPC.


Stack Technologique

Couche Technologie Version Rôle
Frontend React 19 Interface utilisateur réactive
Styling Tailwind CSS 4 Utility-first CSS
Composants shadcn/ui latest Design system cohérent
Graphiques Recharts latest Visualisation analytics
Animations Framer Motion latest Transitions fluides
Routing Wouter latest Navigation SPA légère
Backend Express 4 Serveur HTTP
API tRPC 11 RPC typé end-to-end
Temps réel Socket.io 4 WebSocket bidirectionnel
ORM Drizzle latest Query builder typé
Base de données MySQL / TiDB 8+ Stockage relationnel
Auth Manus OAuth SSO avec JWT
Tests Vitest latest Tests unitaires rapides
QR Code qrcode (npm) Génération côté serveur

Modèle de Données

Diagramme Entité-Relation

erDiagram
    users ||--o{ subscriptions : "possède"
    users ||--o{ clinics : "gère"
    clinics ||--o{ queueEntries : "contient"
    clinics ||--o{ analyticsEvents : "génère"

    users {
        int id PK
        varchar openId UK
        varchar name
        varchar email
        varchar avatarUrl
        enum role
        timestamp createdAt
    }

    subscriptions {
        int id PK
        int userId FK
        varchar stripeCustomerId
        varchar stripeSubscriptionId
        enum plan
        enum status
        timestamp trialStartedAt
        timestamp trialEndsAt
        timestamp currentPeriodStart
        timestamp currentPeriodEnd
    }

    clinics {
        int id PK
        int userId FK
        varchar name
        text address
        varchar phone
        varchar color
        boolean isActive
        varchar qrToken
        timestamp qrTokenExpiresAt
        int qrRotationMinutes
        int avgConsultationMinutes
        int maxQueueSize
        boolean isQueueOpen
        int currentTicketNumber
    }

    queueEntries {
        int id PK
        int clinicId FK
        int ticketNumber
        varchar patientToken
        varchar patientName
        enum status
        int position
        timestamp joinedAt
        timestamp calledAt
        int estimatedWaitMinutes
        boolean notificationSent
        boolean isPrinted
    }

    analyticsEvents {
        int id PK
        int clinicId FK
        enum eventType
        int ticketNumber
        int waitMinutes
        int consultationMinutes
        int queueSizeAtEvent
        int hourOfDay
        int dayOfWeek
        json metadata
    }

Détail des Tables

Table Lignes estimées Description
users ~100 Comptes médecins (OAuth Manus)
subscriptions ~100 Un abonnement par utilisateur
clinics ~200 Cabinets médicaux (multi par médecin)
queue_entries ~10 000/mois Entrées dans la file (historique)
analytics_events ~50 000/mois Événements pour graphiques

Statuts des Entrées de File

                    ┌──────────┐
                    │ waiting  │ ← Patient rejoint la file
                    └────┬─────┘
                         │
              ┌──────────┼──────────┐
              │          │          │
              ▼          ▼          ▼
        ┌──────────┐ ┌──────────┐ ┌──────────┐
        │  called  │ │  absent  │ │ canceled │
        └────┬─────┘ └──────────┘ └──────────┘
             │
             ▼
     ┌───────────────────┐
     │ in_consultation   │
     └────────┬──────────┘
              │
              ▼
        ┌──────────┐
        │   done   │
        └──────────┘

Procédures tRPC

Authentification

Procédure Type Accès Description
auth.me Query Public Utilisateur courant (ou null)
auth.logout Mutation Protected Déconnexion (supprime cookie)

Abonnement

Procédure Type Accès Description
subscription.get Query Protected Statut abonnement courant
subscription.check Query Protected Vérifie si actif/trial

Cabinets

Procédure Type Accès Description
clinic.list Query Protected Liste des cabinets du médecin
clinic.get Query Protected Détails d'un cabinet
clinic.create Mutation Subscription Créer un cabinet
clinic.update Mutation Subscription Modifier un cabinet
clinic.delete Mutation Subscription Supprimer un cabinet
clinic.rotateQr Mutation Subscription Forcer rotation QR
clinic.getQrData Query Protected Données pour QR code

File d'Attente

Procédure Type Accès Description
queue.getEntries Query Protected Entrées de la file
queue.open Mutation Subscription Ouvrir la file
queue.close Mutation Subscription Fermer la file
queue.callNext Mutation Subscription Appeler le suivant
queue.markAbsent Mutation Subscription Marquer absent
queue.remove Mutation Subscription Retirer de la file
queue.reset Mutation Subscription Réinitialiser la file
queue.join Mutation Public Patient rejoint (via QR)
queue.getPatientStatus Query Public Position du patient
queue.printTicket Mutation Subscription Générer ticket

Analytics

Procédure Type Accès Description
analytics.get Query Protected Stats par cabinet
analytics.getAll Query Protected Stats globales
analytics.exportCsv Query Protected Export CSV

Middleware d'Abonnement

Le middleware subscriptionProcedure intercepte chaque requête protégée et vérifie l'état de l'abonnement :

Requête tRPC → protectedProcedure → subscriptionProcedure → Procédure
                     │                       │
                     │                       ├── trial actif ? → ✅ Continuer
                     │                       ├── abonnement actif ? → ✅ Continuer
                     │                       └── expiré/annulé ? → ❌ FORBIDDEN
                     │
                     └── Pas authentifié ? → ❌ UNAUTHORIZED

Routes Frontend

Route Composant Accès Description
/ Home.tsx Public Landing page cinématique
/dashboard Dashboard.tsx Auth Tableau de bord principal
/dashboard/clinics DoctorClinics.tsx Auth Gestion cabinets
/dashboard/queue/:id QueueManagement.tsx Auth Gestion file temps réel
/dashboard/analytics Analytics.tsx Auth Graphiques et export
/dashboard/subscription SubscriptionPage.tsx Auth Plans et paiement
/dashboard/help Help.tsx Auth Centre d'aide (FAQ)
/dashboard/onboarding Onboarding.tsx Auth Wizard 3 étapes
/display/:clinicId DisplayScreen.tsx Public Écran salle d'attente
/queue/:token PatientQueue.tsx Public Suivi patient
/ticket/:entryId PrintTicket.tsx Public Ticket imprimable
/qr-poster/:clinicId QrPoster.tsx Auth Affiche QR A4
/subscription/blocked SubscriptionBlocked.tsx Auth Page blocage

Sécurité

Couches de Protection

Couche Mécanisme Description
Authentification JWT + Cookie HttpOnly Session signée, Secure, SameSite=Lax
Autorisation Middleware tRPC protectedProcedure, subscriptionProcedure
Anti-triche Token QR rotatif Impossible de partager/falsifier sa position
Rate limiting Express middleware Protection contre les abus
Validation Zod schemas Validation côté serveur de toutes les entrées
CORS Express CORS Origines autorisées uniquement

Token QR Anti-Triche

Rotation automatique (configurable) :

  T=0min        T=30min       T=60min       T=90min
    │              │              │              │
    ▼              ▼              ▼              ▼
 [Token A]     [Token B]     [Token C]     [Token D]
    │              │              │              │
    └── Valide ────┘              │              │
                   └── Valide ────┘              │
                                  └── Valide ────┘

  Patient scan Token B à T=25min → ✅ Rejoint la file
  Partage lien Token B à T=35min → ❌ Token expiré, nouveau QR requis

Structure du Projet

queue-med/
├── client/
│   ├── public/              ← Favicon, manifest PWA
│   └── src/
│       ├── pages/           ← 12 pages (Landing, Dashboard, Queue...)
│       ├── components/      ← shadcn/ui + composants métier
│       ├── hooks/           ← useAuth, useMobile
│       ├── lib/             ← trpc client, utils
│       ├── contexts/        ← ThemeContext
│       ├── App.tsx          ← Routes + layouts
│       ├── main.tsx         ← Providers
│       └── index.css        ← Design system (glass, glow, gradients)
├── server/
│   ├── _core/              ← Framework (OAuth, Express, Socket.io)
│   ├── routers.ts          ← 20+ procédures tRPC
│   ├── db.ts               ← Helpers Drizzle
│   └── storage.ts          ← S3 helpers
├── drizzle/
│   └── schema.ts           ← 5 tables
├── docs/
│   └── schema.ts           ← Copie documentée du schéma
├── shared/                  ← Types partagés
└── tests/                   ← 13 tests Vitest