Files
geo/app/docs/QUEUE-APP-API.md

21 KiB

QUEUE-APP-API.md

Système de File d'Attente pour les Requêtes API Hors Ligne

🚀 État actuel de l'implémentation (29/08/2025)

Phases complétées

  • Phase 1 : Infrastructure de base - COMPLÉTÉ
  • Phase 2 : Modification ApiService - COMPLÉTÉ
  • Phase 3 : Intégration ConnectivityService - COMPLÉTÉ
  • Phase 5 : Alertes utilisateur - COMPLÉTÉ
  • Phase 6 : Adaptation des Repositories CRUD - COMPLÉTÉ (tous les 8 repositories)

⚠️ Décisions d'architecture importantes

  1. Pas d'IDs temporaires : La base de données MariaDB utilise des int unsigned, donc pas de valeurs négatives possibles
  2. Stratégie sans stockage local pour CREATE :
    • CREATE offline → Requête en queue, pas de stockage Hive, message "Création en attente"
    • UPDATE offline → Modification locale avec isSynced: false + requête en queue
    • DELETE offline → Suppression locale immédiate + requête en queue
  3. Pas d'expiration automatique : Les requêtes persistent indéfiniment jusqu'à leur envoi
  4. Ordre FIFO strict : Basé sur createdAt pour garantir la cohérence

📦 Repositories adaptés (8/8)

Repository CREATE offline UPDATE offline DELETE offline Notes
PassageRepository Queue + Dialog + Pas de stockage Stockage local + Flag Suppression immédiate Priorité haute terrain
ClientRepository Queue + Dialog + Pas de stockage Stockage local Suppression immédiate Adhérents terrain
MembreRepository Queue + Dialog + Retourne null Stockage local Suppression immédiate Gestion équipe
SectorRepository Queue + Dialog + Status "queued" Stockage local Suppression + passages Gère aussi les passages
UserRepository N/A Stockage avec isSynced=false N/A Profil utilisateur connecté uniquement
AmicaleRepository Queue + Dialog + Pas de stockage Stockage local Suppression immédiate Organisation
OperationRepository Queue + Dialog + Pas de stockage Stockage avec isSynced=false Suppression + données liées Campagnes

📊 Réponses API en mode offline

Toutes les requêtes stockées en pending retournent :

{
  "queued": true
}

🔄 Prochain travail

  • Implémenter la gestion des conflits de synchronisation
  • Ajouter la limite de queue (1000 requêtes max)
  • Créer le système d'export des données en attente
  • Ajouter un dialog de confirmation lors de la déconnexion avec données en attente
  • Tester le système complet en mode avion

📋 Vue d'ensemble

Objectif

Créer un système robuste de gestion des requêtes API en mode hors ligne pour permettre à l'application de fonctionner sans connexion Internet et de synchroniser automatiquement les données dès que la connexion est rétablie.

Problématique actuelle

  • Les requêtes API échouent immédiatement sans connexion
  • Aucun mécanisme de file d'attente pour les requêtes en attente
  • Les messages du chat sont perdus si envoyés hors ligne
  • Les modifications CRUD ne sont pas persistées localement pour envoi ultérieur

Solution proposée

Modifier ApiService pour qu'il gère automatiquement la file d'attente : stockage dans Hive si hors ligne, envoi direct si en ligne, et traitement automatique de la queue au retour de connexion.

🏗️ Architecture

Flux de données

Application (UI/Repository)
           ↓
      ApiService (modifié)
           ↓
    [Si en ligne] → Envoi direct → API
           ↓
    [Si hors ligne] → Stockage → Hive Box "pending_requests"
           
ConnectivityService (écoute en permanence)
           ↓
    [Connexion rétablie] → Notifie ApiService
           ↓
    ApiService.processPendingRequests() → API

Services impliqués

  1. ApiService (à modifier)

    • Vérifie la connectivité avant chaque requête
    • Si online : envoi direct à l'API
    • Si offline : stockage dans Hive Box
    • Méthode processPendingRequests() pour vider la queue
    • Retourne une réponse "pending" avec tempId si offline
  2. ConnectivityService (à enrichir)

    • Détecte les changements de connexion
    • Appelle ApiService.processPendingRequests() au retour
    • Vérifie périodiquement s'il y a des requêtes en attente
  3. Hive Boxes (nouvelles)

    • pending_requests : File d'attente des requêtes (JAMAIS supprimée si non vide)
    • temp_entities : Entités temporaires (messages temp_, etc.)

📊 Analyse des requêtes actuelles

1. Chat Service

  • POST /chat/rooms - Créer une conversation
  • POST /chat/rooms/{roomId}/messages - Envoyer un message
  • GET /chat/rooms - Obtenir les conversations
  • GET /chat/rooms/{roomId}/messages - Obtenir les messages
  • DELETE /chat/rooms/{roomId} - Supprimer une conversation

2. User Repository

  • GET /users - Liste des utilisateurs
  • GET /users/{id} - Détail utilisateur
  • POST /users - Créer utilisateur
  • PUT /users/{id} - Modifier utilisateur
  • DELETE /users/{id} - Supprimer utilisateur

3. Amicale Repository

  • GET /entites - Liste des amicales
  • PUT /entites/{id} - Modifier amicale
  • POST /entites/{id}/logo - Upload logo

4. Operations/Passages

  • GET /operations - Liste des opérations
  • POST /passages - Créer un passage
  • PUT /passages/{id} - Modifier un passage
  • GET /operations/{id}/export/excel - Export Excel

5. Authentification

  • POST /login - Connexion
  • POST /logout - Déconnexion

💾 Modèle de données pour la file d'attente

PendingRequest Model

@HiveType(typeId: 100)
class PendingRequest extends HiveObject {
  @HiveField(0)
  final String id; // UUID unique
  
  @HiveField(1)
  final String method; // POST, GET, PUT, DELETE
  
  @HiveField(2)
  final String path; // /chat/rooms/xxx/messages
  
  @HiveField(3)
  final Map<String, dynamic>? data; // Body de la requête
  
  @HiveField(4)
  final Map<String, dynamic>? queryParams; // Query parameters
  
  @HiveField(5)
  final DateTime createdAt; // Timestamp de création (ORDRE DE TRAITEMENT)
  
  @HiveField(6)
  final String? tempId; // ID temporaire associé (ex: temp_xxx)
  
  @HiveField(7)
  final String context; // chat, user, operation, etc.
  
  @HiveField(8)
  final int retryCount; // Nombre de tentatives
  
  @HiveField(9)
  final String? errorMessage; // Dernière erreur
  
  @HiveField(10)
  final Map<String, dynamic>? metadata; // Infos additionnelles
}

⚠️ IMPORTANT : Les requêtes DOIVENT être traitées dans l'ordre chronologique strict (FIFO - First In First Out) basé sur createdAt pour garantir la cohérence des données.

🎯 Cas d'usage spécifiques

1. Chat - Envoi de message hors ligne

Workflow actuel:

  1. Utilisateur tape un message
  2. Tentative d'envoi direct à l'API
  3. Si pas de connexion → Erreur

Nouveau workflow:

  1. Utilisateur tape un message
  2. Création d'un message temporaire avec ID temp_xxx
  3. Sauvegarde immédiate dans Hive (affichage instantané)
  4. Si en ligne → Envoi API → Remplacement par message réel
  5. Si hors ligne → Ajout à la queue → Message affiché en italique/grisé
  6. Quand connexion revenue → Traitement de la queue → Mise à jour avec ID réel

Indicateurs visuels:

  • Message en attente : Texte italique + icône horloge
  • Message en cours d'envoi : Spinner
  • Message envoyé : Texte normal
  • Message en erreur : Texte rouge + icône erreur + option retry

2. CRUD Utilisateurs/Amicales

Création hors ligne:

  • Génération d'un ID temporaire temp_user_xxx
  • Sauvegarde locale dans Hive
  • Affichage avec badge "En attente de synchronisation"
  • Synchronisation automatique au retour de connexion

Modification hors ligne:

  • Sauvegarde des modifications locales
  • Marquage de l'entité comme "modified_offline"
  • Affichage d'un indicateur de synchronisation en attente
  • Gestion des conflits si modifié par ailleurs

Suppression hors ligne:

  • Marquage local comme "deleted"
  • Masquage de l'UI
  • Envoi de la suppression au retour de connexion

3. Passages terrain

Enregistrement de passage hors ligne:

  • Sauvegarde locale complète (coordonnées GPS, timestamp, etc.)
  • Queue avec priorité HAUTE
  • Synchronisation prioritaire au retour de connexion
  • Conservation des données même après envoi (backup)

🔄 Gestion de la synchronisation

Ordre de traitement

FIFO strict (First In First Out) : Les requêtes sont TOUJOURS traitées dans l'ordre chronologique de leur création (createdAt), sans exception. Ceci garantit :

  • Les messages de chat arrivent dans le bon ordre
  • Une création précède toujours sa modification
  • Une modification précède toujours sa suppression
  • La cohérence des données est maintenue

Stratégie de retry

  • 1ère tentative : Immédiate
  • 2ème tentative : Après 30 secondes
  • 3ème tentative : Après 2 minutes
  • 4ème+ tentative : Toutes les 5 minutes
  • Abandon après 24h MODIFIÉ: Pas d'expiration automatique - requêtes persistent indéfiniment

Gestion des conflits

  • Messages chat : Pas de conflit (append-only)
  • CRUD : Dernière modification gagne + log des conflits
  • Passages : Pas de conflit (chaque passage est unique)

🛠️ Plan d'implémentation

Phase 1 : Infrastructure (Jour 1) COMPLÉTÉ

  • Créer le modèle PendingRequest
  • Créer la Hive Box pending_requests avec protection
  • Enrichir ConnectivityService avec gestion de queue
  • Ajouter les indicateurs UI d'alerte

Phase 2 : Modification ApiService (Jour 1-2) COMPLÉTÉ

  • Ajouter vérification connectivité dans chaque méthode
  • Implémenter le stockage dans Hive si offline
  • Créer méthode processPendingRequests()
  • Gérer les réponses "pending" avec tempId

Phase 3 : Traitement de la queue (Jour 2) COMPLÉTÉ

  • Implémenter le processeur de queue FIFO
  • Garantir l'ordre chronologique strict
  • Implémenter les retry avec backoff
  • Gérer les erreurs et abandons

Phase 4 : Chat (Jour 3)

  • Adapter ChatService pour messages temporaires
  • Implémenter l'UI pour messages en attente
  • Gérer le remplacement temp_ → ID réel
  • Tester envoi/réception hors ligne

Phase 5 : CRUD (Jour 4) COMPLÉTÉ

  • Adapter PassageRepository (COMPLÉTÉ)
  • Adapter les autres repositories (Client, Membre, Sector, User, Amicale, Operation) - TOUS COMPLÉTÉS
  • Gérer les IDs temporaires MODIFIÉ: Pas d'IDs temporaires (contrainte DB unsigned int)
  • Implémenter les indicateurs UI
  • Gérer les conflits de synchronisation

Phase 6 : Tests et optimisation (Jour 5)

  • Tests unitaires du QueueService
  • Tests d'intégration
  • Tests de scénarios hors ligne/en ligne
  • Optimisation des performances

🎨 UI/UX Guidelines

⚠️ ALERTES UTILISATEUR OBLIGATOIRES

Mode hors ligne détecté

  • Banner permanent en haut : Fond orange avec texte "⚠️ Mode hors ligne - X modifications en attente"
  • Badge rouge sur l'app bar : Nombre de requêtes non synchronisées
  • Vibration/Son : Alerte immédiate lors de la perte de connexion

Tentative de déconnexion avec données en attente

  • Dialog bloquant :
    ⚠️ ATTENTION
    Vous avez X modifications non synchronisées.
    
    Si vous vous déconnectez maintenant, ces données
    seront PERDUES :
    - X messages non envoyés
    - X modifications d'utilisateurs
    - X passages terrain
    
    [Annuler] [Se reconnecter d'abord] [Forcer déconnexion*]
    
    * Les données non synchronisées seront perdues
    

Fermeture de l'app avec données en attente

  • Notification système : "GeoSector a des données non synchronisées"
  • Dialog de confirmation si tentative de fermeture

Indicateurs visuels globaux

  • Badge sur l'app bar : Nombre de requêtes en attente (rouge pulsant)
  • Snackbar : Notification quand connexion perdue/retrouvée
  • Pull to refresh : Force la synchronisation manuelle
  • Bouton "Synchroniser" : Visible uniquement si données en attente

États des éléments

  • En attente : Opacity 0.7 + icône horloge + texte italique
  • En cours de sync : Shimmer effect ou spinner
  • Erreur de sync : Bordure rouge + icône erreur
  • Synchronisé : État normal

Messages utilisateur

  • "⚠️ Pas de connexion - X actions en attente de synchronisation"
  • "⚠️ Reconnectez-vous pour envoyer vos modifications"
  • " Connexion rétablie - Synchronisation en cours..."
  • " Toutes les modifications ont été synchronisées"
  • " Erreur de synchronisation - Toucher pour réessayer"

🔐 Considérations de sécurité

⚠️ PROTECTION CRITIQUE DE LA HIVE BOX

SPLASH PAGE - ATTENTION :

  • La splash_page.dart qui réinitialise les boxes au démarrage DOIT :
    1. Vérifier si pending_requests box existe et n'est pas vide
    2. Si elle contient des données → NE PAS LA SUPPRIMER
    3. Afficher un avertissement à l'utilisateur
    4. Déclencher immédiatement le traitement de la queue si connexion disponible
// Dans splash_page.dart
if (await Hive.boxExists(AppKeys.pendingRequestsBoxName)) {
  final box = await Hive.openBox<PendingRequest>(AppKeys.pendingRequestsBoxName);
  if (box.isNotEmpty) {
    print('⚠️ ${box.length} requêtes en attente trouvées');
    // NE PAS SUPPRIMER LA BOX
    // Notifier l'utilisateur
    // Déclencher le traitement si online
  }
}

Dans app_keys.dart :

static const String pendingRequestsBoxName = 'pending_requests';
static const String tempEntitiesBoxName = 'temp_entities';

Autres considérations

  1. Persistance sécurisée : Chiffrer les données sensibles dans Hive
  2. Expiration des tokens : Gérer le renouvellement des sessions
  3. Limite de queue : Maximum 1000 requêtes en attente
  4. Nettoyage : Purger les requêtes expirées (>24h) SEULEMENT si confirmé par l'utilisateur
  5. Validation : Revalider les données avant envoi
  6. Sauvegarde : Export possible des requêtes en attente avant déconnexion forcée

📈 Monitoring et debugging

Logs à implémenter

// Format des logs
[ApiService] No connection - Request queued: POST /chat/rooms/xxx/messages
[ApiService] Queue size: 5 pending requests
[ConnectivityService] Connection restored - Processing queue...
[ApiService] Processing pending request 1/5: POST /chat/rooms/xxx/messages
[ApiService] Success: temp_xxx  real_id_xxx
[ApiService] Retry 2/5 for request xxx
[ApiService] WARNING: Request xxx pending for >12h
[ApiService] ALERT: User attempting logout with 5 pending requests

Métriques à suivre

  • Taille de la queue
  • Temps moyen de traitement
  • Taux de succès/échec
  • Nombre de retry par requête
  • Durée moyenne hors ligne

🚀 Bénéfices attendus

  1. Expérience utilisateur : Application utilisable sans connexion
  2. Fiabilité : Aucune perte de données
  3. Performance : Réponse immédiate (optimistic UI)
  4. Productivité : Travail continu même en zone blanche
  5. Résilience : Gestion automatique des problèmes réseau

⚠️ Points d'attention

  1. Taille de la queue : Limiter pour éviter saturation mémoire
  2. Ordre des requêtes : Respecter les dépendances
  3. Idempotence : S'assurer que rejouer une requête est safe
  4. Conflits : Stratégie claire de résolution
  5. Battery : Optimiser pour ne pas drainer la batterie

📝 Planning détaillé d'implémentation

Phase 1 : Infrastructure de base (Jour 1) COMPLÉTÉ

  • Créer les constantes dans app_keys.dart (pendingRequestsBoxName, tempEntitiesBoxName)
  • Créer le modèle PendingRequest avec annotations Hive
  • Enregistrer l'adapter PendingRequest dans HiveAdapters
  • Protéger la box pending_requests dans HiveService
  • Ajouter pending_requests dans HiveService._boxConfigs

Phase 2 : Modification ApiService (Jour 1-2) COMPLÉTÉ

  • Créer QueueManager dans ApiService pour gérer la file
  • Modifier les méthodes ApiService (get, post, put, delete) pour vérifier connectivité
  • Implémenter processPendingRequests() dans ApiService
  • Créer le système de retry avec backoff exponentiel

Phase 3 : Intégration ConnectivityService (Jour 2) COMPLÉTÉ

  • Enrichir ApiService avec listener de connectivité pour déclencher la synchronisation au retour de connexion

Phase 4 : Chat (Proof of Concept) (Jour 3)

  • Adapter ChatService pour messages temporaires (temp_xxx)
  • Créer UI pour messages en attente (italique, icône horloge)
  • Implémenter le remplacement temp_id vers real_id

Phase 5 : Alertes utilisateur (Jour 3) COMPLÉTÉ

  • Badge de connexion avec indicateur et animation clignotante
  • Dialog d'information pour création en attente (PassageRepository)
  • Intégration du compteur de requêtes pendantes dans ConnectivityIndicator
  • Widget PendingRequestsCounter pour affichage détaillé

Phase 6 : Adaptation des Repositories CRUD (Jour 4-5) COMPLÉTÉ

Priorité HAUTE (critique terrain)

  • Adapter PassageRepository pour CRUD hors ligne avec priorité haute
  • Adapter ClientRepository pour création d'adhérents sur le terrain

Priorité MOYENNE

  • Adapter UserRepository pour CRUD hors ligne (profil utilisateur connecté uniquement)
  • Adapter MembreRepository pour gestion de l'équipe
  • Adapter SectorRepository pour assignation de secteurs

Priorité BASSE

  • Adapter AmicaleRepository pour modifications moins fréquentes
  • Adapter OperationRepository pour gestion des campagnes

Phase 7 : Gestion avancée (Jour 5)

  • Créer système de gestion des conflits
    • Détection automatique des erreurs 409 (Conflict)
    • Marquage des requêtes en conflit avec hasConflict: true
    • Méthodes de résolution : resolveConflictByDeletion() et resolveConflictByRetry()
    • Gestion des erreurs permanentes (4xx sauf 409) avec suppression automatique
    • Limite de 5 tentatives pour les erreurs temporaires
  • Implémenter limite de queue (max 1000 requêtes)
    • Vérification avant ajout avec exception si limite atteinte
    • Message d'erreur clair pour l'utilisateur
  • Créer système d'export des données en attente
    • Export JSON complet avec exportPendingRequestsToJson()
    • Import avec fusion et détection de doublons importPendingRequestsFromJson()
    • Statistiques détaillées avec getPendingRequestsStats()

Phase 8 : Monitoring et debug (Jour 6)

  • Ajouter logs détaillés pour monitoring
  • Implémenter métriques de performance

Phase 9 : Tests (Jour 6-7)

  • Créer tests unitaires pour QueueService
  • Créer tests d'intégration mode offline/online
  • Tester scénarios réels (mode avion, perte connexion)

Phase 10 : Documentation (Jour 7)

  • Documenter l'utilisation pour les développeurs
  • Créer guide utilisateur pour le mode hors ligne

📊 Estimation et priorités

Durée totale estimée : 5-7 jours

MVP minimal (3 jours)

  • Phases 1-3 : Infrastructure + ApiService + ConnectivityService
  • Phase 4 : Chat comme proof of concept
  • Phase 5 : Alertes utilisateur de base

Version complète (7 jours)

  • Toutes les phases incluant tous les repositories
  • Tests complets
  • Documentation

Ordre de priorité des repositories

  1. 🔴 Critique : PassageRepository, ClientRepository
  2. 🟠 Important : UserRepository, MembreRepository, SectorRepository
  3. 🟡 Normal : AmicaleRepository, OperationRepository

🔧 Utilisation des fonctionnalités avancées

Gestion des conflits

// Récupérer les requêtes en conflit
final conflicts = ApiService.instance.getConflictedRequests();
final conflictCount = ApiService.instance.getConflictedRequestsCount();

// Résoudre un conflit
await ApiService.instance.resolveConflictByDeletion(requestId); // Supprimer
await ApiService.instance.resolveConflictByRetry(requestId);    // Réessayer

Export/Import des données

// Exporter les requêtes en attente
final jsonData = ApiService.instance.exportPendingRequestsToJson();
// Sauvegarder dans un fichier ou partager...

// Importer des requêtes
final imported = await ApiService.instance.importPendingRequestsFromJson(jsonString);
print('$imported requêtes importées');

// Obtenir des statistiques
final stats = ApiService.instance.getPendingRequestsStats();
print('Total: ${stats['total']}, Conflits: ${stats['conflicted']}');

Limite de queue

La limite de 1000 requêtes est automatiquement vérifiée. Si atteinte :

  • Une ApiException est levée avec un message explicite
  • L'utilisateur doit attendre la synchronisation avant de nouvelles opérations

Guide de test en mode avion

  1. Activer le mode avion sur l'appareil
  2. Effectuer des opérations (créations, modifications, suppressions)
  3. Vérifier le compteur de requêtes en attente dans l'interface
  4. Désactiver le mode avion
  5. Observer la synchronisation automatique (badge devient vert)
  6. Vérifier les données synchronisées sur le serveur

Document créé le 25/08/2025 - Dernière mise à jour : 29/08/2025 - Système complet avec gestion des conflits et export