# Documentation RoadWave

## Table des matières

- RoadWave
- RoadWave - Architecture Technique
- ADR-001 : Langage Backend
- ADR-002 : Protocole de Streaming
- ADR-003 : Codec Audio
- ADR-004 : CDN
- ADR-005 : Base de Données
- ADR-006 : Chiffrement
- ADR-007 : Tests et Spécifications Exécutables
- ADR-008 : Authentification et Gestion d'Identité
- ADR-009 : Solution de Paiement et Gestion des Abonnements
- ADR-010 : Commandes au volant et likes
- ADR-011 : Conformité App Stores et Plateformes Auto
- ADR-012 : Architecture Backend
- ADR-013 : ORM et Accès Données
- ADR-014 : Frontend Mobile
- ADR-015 : Stratégie Tests
- Règles métier RoadWave
- Annexe : Fonctionnalités reportées Post-MVP
- Audio-guides multi-séquences pour piétons
- Impact des abonnements sur l'algorithme
- Limites d'abonnements et désabonnement
- Notifications contextuelles selon le mode de déplacement
- Création d'audio-guide multi-séquences
- Intégration audio-guides avec autres fonctionnalités
- Audio-guide mode piéton (navigation manuelle)
- Audio-guide mode voiture (GPS automatique)
- Audio-guides modes vélo et transport
- Audio-guides Premium et monétisation
- Sauvegarde et reprise de progression audio-guide
- Classification des contenus par âge
- Connexion utilisateur
- Inscription utilisateur
- Récupération de compte
- Gestion des sessions et tokens
- Authentification à deux facteurs (2FA)
- Vérification d'email
- Métadonnées et publication de contenu
- Modification et suppression de contenu
- Upload et encodage de contenu audio
- Validation des 3 premiers contenus
- Élargissement automatique de zone quand aucun contenu n'est disponible
- Gestion d'un contenu supprimé pendant l'écoute
- Mode dégradé sans géolocalisation
- Gestion de la perte de réseau et buffering adaptatif
- Tests BDD - Documentation des fonctionnalités
- Pas de dégradation temporelle des jauges
- Évolution des jauges d'intérêt
- Jauge initiale et cold start
- Synchronisation actions offline
- Téléchargement de contenus offline
- Validité et renouvellement contenus offline
- Modération préventive
- Sanctions et notifications de modération
- Signalement de contenu inapproprié
- Traitement des signalements par l'IA et les modérateurs
- Conditions d'activation de la monétisation
- Contenus Premium exclusifs
- Désactivation et suspension monétisation
- KYC et inscription à la monétisation
- Obligations fiscales
- Paiement des créateurs
- Sources de revenus créateurs
- Actions complémentaires à l'arrêt
- Commande "Précédent"
- Commandes vocales CarPlay et Android Auto
- Commandes au volant et interactions simplifiées
- File d'attente et commande "Suivant"
- Lecture en boucle et enchaînement automatique
- Partage de contenu
- Avantages Premium
- Gestion abonnement Premium
- Multi-devices et détection simultanée
- Offre et tarification Premium
- Profil créateur
- Création de campagnes publicitaires
- Caractéristiques et facturation des publicités
- Gestion du budget et alertes publicitaires
- Insertion et fréquence des publicités
- Métriques d'engagement et dashboard publicitaire
- Validation et modération des publicités
- Architecture technique radio live
- Arrêt du live
- Comportement auditeur pendant un live
- Démarrage d'un live
- Recherche de contenu
- Classification de géo-pertinence des contenus
- Gestion du contenu politique (MVP simplifié)
- Contenus géolocalisés en mode voiture
- Gestion de l'historique et reproposition
- Médias traditionnels sur RoadWave
- Mode Kids pour utilisateurs 13-15 ans
- Paramétrabilité admin et A/B testing
- Paramétrabilité utilisateur et profils
- Formule de scoring et recommandation
- Anonymisation des données GPS après 24h
- Conformité administrative RGPD (Registre, Breach, DPO)
- Gestion du consentement RGPD
- Durée de conservation des données et purge automatique
- Cookies et analytics avec Matomo self-hosted
- Mode dégradé avec GeoIP (sans GPS précis)
- Portabilité des données (Article 20 RGPD)
- Suppression du compte utilisateur (Article 17 RGPD - Droit à l'effacement)

# RoadWave

Réseau social audio géolocalisé pour les usagers de la route.

## Concept

RoadWave permet aux conducteurs d'écouter du contenu audio contextuel pendant leurs trajets. La navigation se fait par commandes au volant (suivant/précédent), inspirée des réseaux à scroll infini.

Le contenu est diffusé en fonction de la position géographique de l'utilisateur et de ses centres d'intérêt.

## Cas d'usage

Utilisateur | Scénario
--- | ---
Conducteur | Écoute contenu audio en conduisant, navigation par commandes au volant (suivant/précédent), reçoit notifications géolocalisées en passant près de points d'intérêt
Routier | Écoute podcasts et radios live pendant ses trajets longue distance
Touriste à pied | Visite guidée audio d'un musée, monument ou ville : choisit parmi plusieurs guides, navigue entre séquences à son rythme (tactile/vocal), reçoit notification push quand un audio-guide est disponible à proximité
Commerçant | Diffuse une publicité audio ciblée GPS devant son commerce
Passionné auto | Découvre du contenu automobile près de circuits ou concessionnaires
Habitant local | Partage anecdotes ou bons plans géolocalisés dans son quartier
Média traditionnel | Le Monde, Le Parisien diffusent actualités géolocalisées ou nationales

## Utilisateurs

Tout utilisateur peut écouter et créer du contenu (rôle flexible).

Rôle | Description
--- | ---
Auditeur | Écoute, like, s'abonne à des créateurs, signale des contenus
Créateur | Publie du contenu audio géolocalisé (individus, médias traditionnels)
Publicitaire | Diffuse des publicités ciblées géographiquement
Modérateur | Valide et modère les contenus signalés

## Types de contenu

Type | Description
--- | ---
Contenu court | Audio de quelques secondes à quelques minutes
Podcast | Épisodes plus longs, séries thématiques
Radio live | Diffusion en direct avec synchronisation approximative entre auditeurs
Audio-guide | Visite guidée multiséquence (musée, monument, ville) : plusieurs séquences numérotées, navigation manuelle entre pistes, liste complète visible, guidage vocal entre points d'intérêt

## Géolocalisation

Le créateur définit la zone de diffusion de son contenu :

Niveau | Portée
--- | ---
Point GPS | Rayon précis autour d'une coordonnée
Ville | Diffusion dans une ville
Département | Diffusion départementale
Région | Diffusion régionale
Pays | Diffusion nationale

Priorité de diffusion : plus la zone est précise, plus le contenu a de chances d'être diffusé (GPS > ville > département > région > pays).

## Algorithme de recommandation

Le contenu proposé est calculé via un score combiné :

- Proximité géographique : distance entre l'utilisateur et la zone du contenu
- Pertinence des intérêts : correspondance avec les centres d'intérêt de l'utilisateur

Lorsque plusieurs contenus sont disponibles dans une zone, seul le plus pertinent est diffusé.

## Centres d'intérêt

Chaque utilisateur possède des jauges d'intérêt qui évoluent dynamiquement :

### Catégories

- Automobile
- Voyage
- Famille
- Amour
- Musique
- Économie
- Cryptomonnaie
- Politique
- ... (extensible)

### Évolution des jauges

Action | Effet
--- | ---
Temps d'écoute long | Augmente la jauge
Like | Augmente la jauge
Abonnement | Augmente fortement la jauge
Skip rapide | Diminue la jauge

Les créateurs taguent leur contenu avec des centres d'intérêt. L'algorithme privilégie les correspondances mais n'exclut pas les utilisateurs sans correspondance.

## Interactions

### Commandes au volant (conduite)

Interactions simplifiées pour sécurité routière maximale :

Commande | Action
--- | ---
Suivant | Passer au contenu suivant
Précédent | Revenir au contenu précédent
Play/Pause | Mettre en pause / reprendre la lecture

Like automatique : Le système détecte automatiquement vos préférences selon votre temps d'écoute : - Écoute ≥80% du contenu → Like renforcé (+2 points jauge) - Écoute 30-79% du contenu → Like standard (+1 point jauge) - Skip après <10s → Signal négatif (-0.5 point)

> Voir ADR-010 pour les détails techniques

### Actions complémentaires (application à l'arrêt)

Action | Description
--- | ---
Like explicite | Bouton cœur pour liker manuellement
S'abonner | Suivre un créateur
Signaler | Signaler un contenu inapproprié
Unlike | Retirer un like

## Publicités

- Insertion entre deux contenus uniquement (jamais d'interruption)
- Ciblage géographique : point GPS, ville, département, région ou national
- Interface dédiée pour les publicitaires

## Radio live

- Diffusion en direct par des créateurs
- Buffering pour garantir une écoute fluide
- Synchronisation approximative entre les auditeurs (quelques secondes de décalage possible)

## Modération

Approche hybride combinant participation communautaire, IA et modérateurs dédiés.

### Contenus prohibés

Catégorie | Description
--- | ---
Haine et violence | Incitation à la haine, violence, discrimination
Contenu sexuel | Pornographie ou contenu sexuellement explicite
Illégalité | Apologie du terrorisme, actes criminels
Désinformation dangereuse | Fausses informations sur la santé, sécurité routière
Harcèlement | Menaces, intimidation, doxxing
Droits d'auteur | Violation de propriété intellectuelle
Fraude | Arnaques, escroqueries

### Rôles de modération

Rôle | Capacités
--- | ---
Auditeur lambda | Signaler un contenu (1 clic)
Auditeur de confiance | Signalements priorisés après historique positif
Modérateur junior | Traiter signalements simples (spam, contenu évident)
Modérateur senior | Cas complexes, appels, décisions de ban
Admin modération | Définir les règles, superviser l'équipe

### Flux de modération

    1. Auditeur signale → File d'attente
    2. IA pré-filtre → Cas évidents traités automatiquement
    3. Modérateur junior → Traite 80% des cas restants
    4. Modérateur senior → Cas complexes + recours

### Outils de modération automatique

Outil | Fonction
--- | ---
Transcription audio | Conversion automatique en texte pour analyse
Analyse vocale IA | Détection de ton agressif, cris, insultes
Empreinte audio | Détection de contenus déjà modérés (réupload)
Détection droits d'auteur | Identification automatique de musique protégée
Filtrage mots-clés | Liste noire de termes inappropriés

### Modération préventive

- Nouveaux créateurs : validation manuelle des 3 premiers contenus
- Score de confiance : évolution selon l'historique du créateur
- Publicités : validation manuelle obligatoire avant diffusion

### Système de strikes

Strike | Sanction
--- | ---
Strike 1 | Avertissement + formation modération
Strike 2 | Suspension 7 jours + contenu supprimé
Strike 3 | Suspension 30 jours
Strike 4 | Ban définitif

- Réhabilitation : -1 strike tous les 6 mois sans incident

### Priorisation des signalements

Priorité | Type de contenu
--- | ---
CRITIQUE | Violence, suicide, mise en danger immédiate
HAUTE | Harcèlement, haine, désinformation
MOYENNE | Spam, contenu inapproprié
BASSE | Qualité audio, tags incorrects

### Transparence et recours

- Notification explicite lors de suppression (raison détaillée)
- Processus d'appel : le créateur peut contester une décision
- Délai de traitement : 48-72h pour les recours
- Historique : tableau de bord des sanctions pour le créateur

### Modération communautaire

- Utilisateurs de confiance : signalements priorisés après historique positif
- Récompenses : badges, réduction premium pour signalements pertinents
- Lutte contre les signalements abusifs (sanctions possibles)

## Modèle économique

### Offres

Formule | Description
--- | ---
Gratuit | Accès complet avec publicités entre les contenus
Premium | Sans publicité + accès aux contenus exclusifs

### Monétisation créateurs

- Partage des revenus pub : rémunération basée sur le nombre d'écoutes
- Pourboires : les auditeurs peuvent faire des dons aux créateurs

## Conformité RGPD

### Données collectées

Donnée | Finalité | Base légale
--- | --- | ---
Position GPS | Diffusion de contenu géolocalisé | Consentement
Historique d'écoute | Personnalisation des recommandations | Intérêt légitime
Centres d'intérêt | Algorithme de recommandation | Consentement
Identité créateur | Publication de contenu | Exécution du contrat

### Droits des utilisateurs

- Accès : consulter toutes ses données personnelles
- Rectification : modifier ses informations
- Suppression : supprimer son compte et toutes ses données
- Portabilité : exporter ses données dans un format standard
- Opposition : désactiver le profilage publicitaire

### Mesures techniques

- Consentement explicite requis pour la géolocalisation
- Anonymisation des données de localisation après 24h (sauf historique personnel)
- Possibilité d'utiliser l'app en mode dégradé (sans géolocalisation précise)
- Données hébergées dans l'UE

# RoadWave - Architecture Technique

> Les décisions techniques sont documentées dans docs/adr/

## Stack Technologique

Composant | Technologie | ADR
--- | --- | ---
Backend | Go + Fiber | ADR-001
Architecture Backend | Monolithe Modulaire | ADR-012
Authentification | Zitadel | ADR-008
Streaming | HLS | ADR-002
Codec | Opus | ADR-003
CDN | Bunny CDN | ADR-004
Base de données | PostgreSQL + PostGIS | ADR-005
ORM/Accès données | sqlc | ADR-013
Cache | Redis Cluster | ADR-005
Chiffrement | TLS 1.3 | ADR-006
Live | WebRTC | ADR-002
Frontend Mobile | Flutter | ADR-014
Tests | Testify + Godog (Gherkin) | ADR-015, ADR-007
Paiements | Mangopay | ADR-009
Commandes volant | Like automatique | ADR-010
Conformité stores | CarPlay, Android Auto, App/Play Store | ADR-011

## Streaming Audio

### Protocole : HLS (HTTP Live Streaming)

- Fonctionne à travers firewalls et réseaux mobiles instables
- Cache CDN natif (réduction des coûts)
- Bitrate adaptatif automatique (tunnels, zones rurales)
- Support natif iOS/Android

### Codec : Opus

Optimisé pour la voix en environnement bruyant (voiture).

Qualité | Bitrate | Usage
--- | --- | ---
Basse | 24 kbps | 2G/Edge
Standard | 48 kbps | 3G
Haute | 64 kbps | 4G/5G

Fallback AAC-LC pour appareils legacy.

### Buffering Adaptatif

Réseau | Buffer min | Buffer cible | Buffer max
--- | --- | --- | ---
WiFi | 5s | 30s | 120s
4G/5G | 10s | 45s | 120s
3G | 30s | 90s | 300s

## Sécurité

### Chiffrement

- TLS 1.3 sur tous les endpoints (overhead ~1-2%)
- DTLS-SRTP pour WebRTC (radio live)
- Pas de DRM initialement (ajout si licences l'exigent)

### Authentification

- Zitadel (self-hosted) pour IAM
- JWT validation locale (zitadel-go SDK)
- OAuth2 PKCE pour mobile (iOS/Android)
- MFA et passkeys disponibles
- Rate limiting par IP et par utilisateur (Nginx + Zitadel)

## Base de Données

### PostgreSQL + PostGIS

    -- Requête géolocalisée typique
    SELECT id, ST_Distance(location::geography, ST_MakePoint($lon, $lat)::geography) as distance
    FROM contents
    WHERE ST_DWithin(location::geography, ST_MakePoint($lon, $lat)::geography, 50000)
    ORDER BY distance
    LIMIT 20;

### Redis Geospatial (Cache)

    GEOADD contents:geo longitude latitude content_id
    GEORADIUS contents:geo user_lon user_lat 50 km WITHDIST COUNT 20 ASC

TTL cache : 5 minutes (le contenu ne bouge pas).

## Architecture Services

    ┌─────────────────┐
    │   Bunny CDN     │  Cache HLS, distribution globale
    └────────┬────────┘
             │
    ┌────────┴────────┐
    │   Nginx         │  SSL, rate limiting, reverse proxy
    └────────┬────────┘
             │
    ┌────────┴────────┐
    │   API Gateway   │  Go + Fiber
    └────────┬────────┘
             │
        ┌────┴────┬─────────────┐
        │         │             │
    ┌───▼───┐ ┌───▼───┐ ┌───────▼───────┐
    │ Auth  │ │ User  │ │ Content/Geo   │
    │Service│ │Service│ │ Service       │
    └───────┘ └───────┘ └───────────────┘
        │         │             │
        └─────────┴─────────────┘
                  │
        ┌─────────┴─────────┐
        │                   │
    ┌───▼───┐         ┌─────▼─────┐
    │ Redis │         │ PostgreSQL│
    │Cluster│         │ + PostGIS │
    └───────┘         └───────────┘

## Scaling 10M Utilisateurs

### Stratégie par phase

Phase | Utilisateurs | Infra | Coût estimé
--- | --- | --- | ---
MVP | 0-100K | Monolithe Go, PostgreSQL managé + Zitadel, Bunny CDN/Storage | 50-150€/mois
Growth | 100K-1M | Kubernetes managé, replicas multi-région | 2-5K€/mois
Scale | 1M-10M | Multi-région, Nginx origin shield, Bunny CDN | 20-50K€/mois

### Métriques cibles

Métrique | Objectif
--- | ---
Latence API p99 | < 100ms
Temps de démarrage audio | < 3s
Disponibilité | 99.9%
Connexions/serveur | 100K+

## Points de vigilance

1. Buffering mobile : Pré-chargement agressif avant tunnels (détection GPS)
2. Handoff réseau : Buffer suffisant pour survivre aux changements de cellule
3. Mode offline : Téléchargement complet sur WiFi
4. Bande passante : 48 kbps Opus = ~20 MB/heure (faible consommation data)

## Pourquoi pas UDP brut ?

UDP | HLS/TCP
--- | ---
Latence minimale | Latence acceptable (5-30s)
Problèmes NAT/firewall | Passe partout
Perte de paquets = artefacts | Retransmission automatique
Pas de cache CDN | Cache CDN = économies
Complexité++ | Standard de l'industrie

Pour du contenu non-interactif (podcasts, audio-guides), la latence HLS est acceptable. WebRTC réservé à la radio live uniquement.

# ADR-001 : Langage Backend

Statut : Accepté Date : 2025-01-17

## Contexte

RoadWave doit gérer 10M d'utilisateurs avec des connexions concurrentes massives pour le streaming audio géolocalisé.

## Décision

Go avec le framework Fiber.

## Alternatives considérées

Option | Performance | Simplicité | Écosystème
--- | --- | --- | ---
Go + Fiber | 1M+ conn/serveur | Élevée | Excellent cloud-native
Rust + Tokio | 2M+ conn/serveur | Faible | Bon
Node.js | 100-500K conn | Élevée | Excellent
Elixir/Phoenix | 2M+ conn | Moyenne | Bon temps réel

## Justification

- Performance : Go gère 1M+ connexions par serveur avec ~10KB/connexion
- Simplicité : Syntaxe claire, compilation rapide, facile à recruter
- Écosystème : First-class Kubernetes, tooling natif (profiling, race detection)
- Équilibre : Meilleur compromis performance/simplicité pour une startup

## Conséquences

- Formation équipe sur Go si nécessaire
- Utilisation des bibliothèques : Fiber (HTTP), pgx (PostgreSQL), go-redis

# ADR-002 : Protocole de Streaming

Statut : Accepté Date : 2025-01-17

## Contexte

Streaming audio vers des utilisateurs mobiles en voiture, avec réseaux instables (tunnels, zones rurales, handoff cellulaire).

## Décision

HLS (HTTP Live Streaming) pour le contenu à la demande. WebRTC réservé à la radio live.

## Alternatives considérées

Option | Latence | Fiabilité mobile | Cache CDN | Complexité
--- | --- | --- | --- | ---
HLS | 5-30s | Excellente | Oui | Faible
DASH | 5-30s | Bonne | Oui | Moyenne
WebRTC | <500ms | Moyenne | Non | Élevée
UDP brut | Minimale | Faible | Non | Très élevée

## Justification

- Réseaux mobiles : HLS gère les coupures et changements de cellule nativement
- Cache CDN : Segments .ts cachables = réduction des coûts
- Compatibilité : Support natif iOS/Android
- Bitrate adaptatif : Ajustement automatique selon la qualité réseau

## Pourquoi pas UDP ?

- Problèmes NAT/firewall sur réseaux mobiles
- Perte de paquets = artefacts audio
- Impossible à cacher sur CDN
- Complexité sans bénéfice pour du contenu non-interactif

## Conséquences

- Latence de 5-30s acceptable pour podcasts/audio-guides
- WebRTC à implémenter séparément pour la radio live

# ADR-003 : Codec Audio

Statut : Accepté Date : 2025-01-17

## Contexte

Audio diffusé en voiture : environnement bruyant, réseau mobile variable, qualité studio non nécessaire.

## Décision

Opus comme codec principal, AAC-LC en fallback.

## Profils d'encodage

Qualité | Bitrate | Usage
--- | --- | ---
Basse | 24 kbps | 2G/Edge
Standard | 48 kbps | 3G
Haute | 64 kbps | 4G/5G

## Alternatives considérées

Codec | Bitrate | Qualité voix | Support mobile
--- | --- | --- | ---
Opus | 24-64 kbps | Excellente | Android natif, iOS via libs
AAC-LC | 64-128 kbps | Bonne | Universel
AAC-HE v2 | 32-64 kbps | Très bonne | Bon
MP3 | 128-320 kbps | Correcte | Universel (legacy)

## Justification

- Environnement bruyant : Opus intègre des algorithmes de résilience au bruit
- Bande passante : 48 kbps Opus ≈ qualité 96 kbps AAC pour la voix
- Consommation data : ~20 MB/heure à 48 kbps
- Latence : 2.5-60ms, idéal pour streaming adaptatif

## Conséquences

- Fallback AAC-LC pour appareils legacy
- Pipeline d'encodage à prévoir côté ingestion

# ADR-004 : CDN

Statut : Accepté Date : 2025-01-17

## Contexte

Distribution audio HLS à 10M d'utilisateurs, besoin de performance, coût maîtrisé, et indépendance vis-à-vis des géants du cloud.

## Décision

Bunny CDN comme CDN principal.

## Alternatives considérées

Solution | Coût/mois (100TB) | Setup | Performance | Dépendance
--- | --- | --- | --- | ---
Bunny CDN | ~1 000€ | 15 min | Très bon | Faible
Cloudflare | 0-5 000€ | 5 min | Excellent | Moyenne
CloudFront | ~9 750€ | 1h | Excellent | Forte (AWS)
Fastly | ~12-20 000€ | 2h | Exceptionnel | Moyenne
Nginx self-hosted | ~2-5 000€ | 1 jour | Excellent | Aucune

## Justification

- Coût : 10x moins cher que CloudFront
- HLS natif : Support optimisé pour le streaming
- Simplicité : Setup en 15 minutes, zéro maintenance
- Européen : Conforme RGPD, 114 PoPs
- Pas de lock-in : Migration facile si besoin

## Évolution prévue

1. Phase 1 (0-1M users) : Bunny CDN seul
2. Phase 2 (1-5M users) : Ajout Nginx origin shield si nécessaire
3. Phase 3 (5M+) : Évaluation multi-CDN

## Conséquences

- Configuration des règles de cache pour `.m3u8` (TTL court) et `.ts` (TTL long)
- Token authentication pour protéger les segments

# ADR-005 : Base de Données

Statut : Accepté Date : 2025-01-17

## Contexte

Requêtes géolocalisées intensives (contenus à proximité), données utilisateurs, historiques d'écoute.

## Décision

- PostgreSQL + PostGIS : Données persistantes et requêtes géospatiales
- Redis Cluster : Cache géolocalisation et sessions

## Architecture

    Requête → Redis Cache → [HIT] → Réponse
                  ↓
               [MISS]
                  ↓
              PostGIS → Cache → Réponse

## Alternatives considérées

Usage | Option choisie | Alternatives
--- | --- | ---
Données utilisateurs | PostgreSQL | MySQL, MongoDB
Géolocalisation | PostGIS | MongoDB Geo, Elasticsearch
Cache | Redis | Memcached, KeyDB
Analytics (futur) | ClickHouse | TimescaleDB

## Justification

### PostgreSQL + PostGIS

- Requêtes géospatiales complexes et précises
- Index GIST pour performance
- ACID, fiabilité éprouvée
- Écosystème mature

### Redis

- Cache géo natif (`GEORADIUS`) : 100K+ requêtes/sec
- Sessions utilisateurs
- Pub/sub pour temps réel

## Exemple de requête

    SELECT id, name,
           ST_Distance(location::geography, ST_MakePoint($lon, $lat)::geography) as distance
    FROM contents
    WHERE ST_DWithin(location::geography, ST_MakePoint($lon, $lat)::geography, 50000)
    ORDER BY distance
    LIMIT 20;

## Conséquences

- TTL cache Redis : 5 minutes (le contenu géolocalisé ne bouge pas)
- Index GIST sur colonnes géométriques
- Réplication read replicas pour scaling lecture

# ADR-006 : Chiffrement

Statut : Accepté Date : 2025-01-17

## Contexte

Streaming audio sur réseaux mobiles, conformité RGPD, protection du contenu.

## Décision

- TLS 1.3 sur tous les endpoints
- DTLS-SRTP pour WebRTC (radio live)
- Pas de DRM au lancement

## Alternatives considérées

Méthode | Overhead | Usage
--- | --- | ---
TLS 1.3 | ~1-2% CPU | HTTPS streaming
DTLS-SRTP | ~3-5% CPU | WebRTC temps réel
AES-128-CBC | Minimal | Chiffrement segments HLS
Widevine/FairPlay | Modéré | DRM (si licences l'exigent)

## Justification

### Pourquoi chiffrer ?

- RGPD : Protection des données utilisateurs obligatoire
- Confiance : Standard attendu en 2025
- Intégrité : Empêche injection de contenu par opérateurs
- Overhead minimal : TLS 1.3 optimisé, impact négligeable

### Pourquoi pas de DRM ?

- Contenu généré par utilisateurs (pas de licences)
- Complexité et coût d'intégration Widevine/FairPlay
- À reconsidérer si partenariats avec labels/éditeurs

## Conséquences

- Certificats SSL gérés par Bunny CDN ou Let's Encrypt
- Configuration TLS 1.3 sur Nginx/API
- DTLS-SRTP à implémenter pour le module radio live

# ADR-007 : Tests et Spécifications Exécutables

Statut : Accepté Date : 2025-01-17

## Contexte

RoadWave nécessite une documentation des use cases qui soit à la fois lisible par tous les stakeholders et vérifiable automatiquement. Les scénarios utilisateurs (touriste, routier, commerçant) doivent être validés en continu.

## Décision

Gherkin pour les spécifications avec Godog comme runner de tests.

## Alternatives considérées

Option | Lisibilité | Intégration Go | Maintenance
--- | --- | --- | ---
Gherkin + Godog | Excellente | Native | Faible
Gauge (Markdown) | Bonne | Plugin | Moyenne
Tests Go natifs | Faible (devs only) | Native | Faible
Concordion | Bonne | Java-centric | Élevée

## Justification

- Living Documentation : Les fichiers `.feature` servent de documentation ET de tests
- Accessibilité : Syntaxe Given/When/Then lisible par PO, devs, testeurs
- Cohérence stack : Godog est le standard BDD pour Go
- CI/CD : Intégration simple dans les pipelines

## Structure

    features/
    ├── recommendation/
    │   ├── geolocalisation.feature
    │   └── interets.feature
    ├── streaming/
    │   ├── lecture.feature
    │   └── buffering.feature
    ├── moderation/
    │   └── signalement.feature
    └── steps/
        └── steps.go

## Exemple

    Feature: Recommandation géolocalisée

      Scenario: Touriste près d'un monument
        Given un utilisateur avec l'intérêt "tourisme" à 80%
        And une position GPS à 100m de la Tour Eiffel
        When le système calcule les recommandations
        Then l'audio guide "Histoire de la Tour Eiffel" est en première position

## Conséquences

- Dépendance : `github.com/cucumber/godog`
- Les use cases du README doivent être traduits en `.feature`
- CI exécute `godog run` avant chaque merge

# ADR-008 : Authentification et Gestion d'Identité

Statut : Accepté Date : 2025-01-18

## Contexte

RoadWave nécessite un système d'authentification sécurisé pour mobile (iOS/Android), scalable jusqu'à 10M utilisateurs, avec contraintes de coût réduit et conformité RGPD.

## Décision

Zitadel (self-hosted) pour l'IAM avec validation JWT locale côté API Go.

## Alternatives considérées

Solution | Coût (10M users) | Performance | Simplicité | Intégration Go
--- | --- | --- | --- | ---
Zitadel | 200-500€/mois | Excellente | Élevée | SDK natif
Supabase Auth | 32K€/mois | Excellente | Élevée | REST API
Keycloak | 200-800€/mois | Bonne | Faible | Lib tierce
Auth0 | 50K€+/mois | Excellente | Élevée | SDK natif
JWT Custom | 0€ (dev) | Excellente | Moyenne | Natif

## Justification

- Coût maîtrisé : 100x moins cher que Supabase/Auth0 à 10M users
- Performance : JWT validation locale = 0 latence auth sur chaque requête API
- Stack alignée : Go + PostgreSQL + Redis (déjà dans RoadWave)
- Scalabilité prouvée : Clients avec 2.3M tenants, architecture event-sourced
- RGPD natif : Entreprise suisse, data residency EU, DPA fourni
- Standards ouverts : OpenID Connect certifié (pas de vendor lock-in)

## Architecture

    ┌─────────────────┐
    │  Mobile Apps    │  OAuth2 PKCE + Refresh tokens
    └────────┬────────┘
             │
    ┌────────▼────────┐
    │  Zitadel IdP    │  PostgreSQL + Redis
    │  (self-hosted)  │  MFA, passkeys, SSO
    └────────┬────────┘
             │ JWT token
    ┌────────▼────────┐
    │  Go + Fiber API │  Validation JWT locale
    │  (RoadWave)     │  github.com/zitadel/zitadel-go
    └─────────────────┘

## Exemple d'intégration

    import "github.com/zitadel/zitadel-go/v3/pkg/authorization/oauth"

    // Validation JWT locale haute performance
    verifier := oauth.WithJWT(config)
    app.Use(verifier.Middleware())

    // Accès aux claims
    userID := ctx.Locals("sub").(string)

## Conséquences

- Déploiement Docker Compose pour MVP
- Migration vers Kubernetes HA en production
- Gestion refresh tokens (rotation automatique)
- MFA et passkeys disponibles out-of-the-box
- Rate limiting intégré à Zitadel

# ADR-009 : Solution de Paiement et Gestion des Abonnements

Statut : Accepté Date : 2025-01-19

## Contexte

RoadWave nécessite une solution de paiement pour gérer les abonnements Premium (4.99€/mois) et reverser 70% des revenus aux créateurs de contenu. Besoin de marketplace natif (split payments), KYC automatique, conformité RGPD, et coûts maîtrisés.

## Décision

Mangopay (France/Luxembourg) comme solution unique pour paiements, marketplace et abonnements.

## Alternatives considérées

Solution | Coût transaction | Marketplace | KYC | Souveraineté
--- | --- | --- | --- | ---
Mangopay | 1.8% + 0.18€ | ✅ Natif | ✅ Gratuit | 🇪🇺 France/LU
Stripe Connect | 2.9% + 0.30€ | ✅ Natif | ❌ 1.20€ | 🇺🇸 USA
Mollie | 2.9% + 0.29€ | ❌ Non | ❌ Non | 🇪🇺 Pays-Bas
Paddle | 5% + 0.50€ | ✅ Natif | ✅ Inclus | 🇬🇧 UK

## Justification

- 38% moins cher que Stripe (1.8% vs 2.9%)
- Marketplace natif : E-wallets automatiques, split payments 70/30, payouts SEPA gratuits
- KYC gratuit : vérification d'identité incluse (vs 1.20€/créateur chez Stripe)
- Souveraineté EU : France/Luxembourg, régulé ACPR, RGPD natif
- Conformité DAC7 : reporting fiscal automatique
- Spécialisé marketplace : utilisé par Vinted, Ulule, ManoMano

## Architecture

    ┌────────────────────────┐
    │   Utilisateurs Premium │  4.99€/mois
    └───────────┬────────────┘
                │
        ┌───────▼───────┐
        │   Mangopay    │  - Abonnements récurrents
        │               │  - KYC créateurs (gratuit)
        │               │  - E-wallets automatiques
        └───────┬───────┘  - Payouts SEPA (gratuits)
                │
      ┌─────────┼─────────┐
      │         │         │
    ┌─▼───┐  ┌─▼───┐  ┌─▼────┐
    │Créa │  │Créa │  │Plate-│
    │teur │  │teur │  │forme │
    │  A  │  │  B  │  │(30%) │
    │(70%)│  │(70%)│  │      │
    └─────┘  └─────┘  └──────┘

## Exemple intégration

    // Abonnement récurrent
    POST /v2.01/{ClientId}/recurringpayinregistrations
    {
      "AuthorId": "{UserId}",
      "FirstTransactionDebitedFunds": {"Currency": "EUR", "Amount": 499}
    }

    // Transfer vers créateur (70%)
    POST /v2.01/{ClientId}/transfers
    {
      "DebitedWalletId": "{PlatformWalletId}",
      "CreditedWalletId": "{CreatorWalletId}",
      "DebitedFunds": {"Currency": "EUR", "Amount": 349}
    }

    // Payout SEPA gratuit
    POST /v2.01/{ClientId}/payouts/bankwire

## Conséquences

- Solution tout-en-un : 1 seul prestataire vs 2-3
- Économie de 2160€/an sur 1000 abonnés (vs Stripe)
- Délai activation compte : 2-5 jours
- Intégration Go via REST API (pas de SDK Go officiel)
- Apple/Google IAP gérés séparément (comme toute solution de paiement)

# ADR-010 : Commandes au volant et likes

Statut : Accepté Date : 2026-01-20

## Contexte

RoadWave est utilisée en conduisant. Les utilisateurs doivent pouvoir liker du contenu pour améliorer les recommandations, mais les commandes au volant ont des limitations : - 40% des véhicules n'ont que Suivant/Précédent/Mute - iOS/Android ne supportent pas nativement les appuis longs ou doubles-appuis - La sécurité impose des interactions minimales

## Décision

Like automatique basé sur le temps d'écoute.

Règles : - ≥80% d'écoute → Like renforcé (+2 points) - 30-79% d'écoute → Like standard (+1 point) - <30% d'écoute → Pas de like - Skip <10s → Signal négatif (-0.5 point)

## Alternatives considérées

Option | Compatibilité | Sécurité | Complexité
--- | --- | --- | ---
Like automatique | 100% | Maximale | Faible
Double-tap Pause | ~80% | Moyenne | Moyenne
Appui long Suivant | ~95% | Faible | Élevée
Configuration paramétrable | 100% | Variable | Très élevée

## Justification

- Sécurité maximale : Aucune action complexe en conduite
- Compatibilité universelle : Fonctionne sur 100% des véhicules
- UX intuitive : Comportement standard (Spotify, YouTube Music)
- Engagement : Tous les contenus génèrent des signaux
- Simplicité : Une seule logique à implémenter et maintenir

## Conséquences

- Tracking du temps d'écoute via le player audio
- Calcul du score côté backend basé sur `completion_rate`
- Communication onboarding : "Vos likes sont automatiques selon votre temps d'écoute"
- Possibilité de like manuel depuis l'app (à l'arrêt)
- Métriques à suivre : taux de complétion, distribution des scores, feedbacks utilisateurs

# ADR-011 : Conformité App Stores et Plateformes Auto

Statut : Accepté avec actions requises Date : 2026-01-20

## Contexte

RoadWave est une app audio géolocalisée utilisée en conduite (CarPlay/Android Auto) avec : - Contenu généré par utilisateurs (UGC) - Monétisation : publicités géolocalisées + Premium (4.99€ web / 5.99€ IAP) - GPS en arrière-plan - Partage de revenus avec créateurs (70/30)

## Décision

Stratégie de conformité multi-plateforme avec : - Modération UGC robuste (IA + humain) - Prix différenciés selon région (US/EU/Monde) - GPS avec disclosure complète - Paiements créateurs externes (Mangopay)

## Plateformes analysées

Plateforme | Conformité | Points critiques
--- | --- | ---
Android Auto | ✅ Conforme | API Level 35+ (Android 15+)
CarPlay | ✅ Conforme | Entitlement audio à demander
Google Play | ⚠️ Actions requises | Déclaration GPS + UGC modération
App Store | ⚠️ Actions requises | Prix différenciés US/EU

## Conformité détaillée

### Android Auto / CarPlay ✅

- 100% audio (pas de vidéo)
- Commandes standard au volant
- Aucun achat in-car
- Like automatique = sécurité maximale
- Notifications géolocalisées : sonore uniquement en mode CarPlay/Android Auto (pas d'overlay visuel)
- Action : Demander CarPlay Audio Entitlement (Apple)

### Google Play ⚠️

UGC (critique) : - Modération hybride IA + humain ✅ - 3 premiers contenus validés manuellement ✅ - Système de strikes (4 = ban) ✅ - Signalement + blocage utilisateurs ✅

GPS Background (critique) : - Permission "Always Location" = OPTIONNELLE - Demandée uniquement pour mode piéton (notifications arrière-plan audio-guides) - Justification Play Console :

> "RoadWave permet aux utilisateurs de recevoir des alertes audio-guides lorsqu'ils passent à pied près de monuments/musées, même quand l'app est en arrière-plan. Cette fonctionnalité est optionnelle et peut être désactivée dans les paramètres." - In-app disclosure obligatoire (écran dédié avant demande permission) - Si refusée : app fonctionne en mode voiture uniquement - Action : Remplir formulaire background location Play Console avec justification

Réponses formulaire Play Console :

Question | Réponse
--- | ---
Why does your app need background location? | "RoadWave offers optional pedestrian mode: users receive push notifications when passing near audio-guide points (museums, monuments) even when app is in background. This feature is opt-in and can be disabled in settings."
Is this feature core to your app? | "No. This is an optional feature. Users can use RoadWave without background location permission (in-car mode works with foreground location only)."
What user value does this provide? | "Pedestrian users (tourists, museum visitors) can keep phone in pocket and receive audio-guide alerts automatically without opening the app."
Does a less invasive alternative exist? | "Yes. Users can use manual navigation (open app, select audio-guide). Background location is a convenience feature for hands-free experience."

### App Store ⚠️

Prix différenciés (légaux depuis 2025-2026) : - 🇺🇸 US : Lien externe autorisé (0% commission) - 🇪🇺 EU : Paiement externe DMA (7-20% commission réduite) - 🌍 Monde : IAP obligatoire (30% commission)

UGC : - Mode Kids obligatoire (filtrage selon âge) ✅ - Système de modération + signalement ✅

GPS Background (critique) : - Permission "Always Location" = OPTIONNELLE - Deux strings Info.plist requises : - `NSLocationWhenInUseUsageDescription` : explication mode voiture - `NSLocationAlwaysAndWhenInUseUsageDescription` : explication mode piéton (optionnel) - In-app disclosure obligatoire avant demande "Always" - Flux two-step : When In Use → Always (si user active mode piéton) - Si refusée : app fonctionne en mode voiture uniquement - Action : Voir strings détaillés dans 05-interactions-navigation.md

### Revenus créateurs

Position : Paiements créateurs = "services" (comme YouTube/Uber), pas IAP - Paiement via Mangopay Connect (externe) - Commission stores uniquement sur Premium (IAP) - Comparables : YouTube AdSense, TikTok Creator Fund, Uber

## Actions bloquantes avant soumission

Action | Plateforme | Deadline | Complexité
--- | --- | --- | ---
Demander CarPlay Audio Entitlement | Apple | Avant soumission iOS | Faible
Remplir formulaire background location avec justification | Google Play | Avant soumission Android | Faible
Implémenter disclosure GPS (écran dédié mode piéton) | iOS + Android | MVP | Moyenne
Rendre permission "Always Location" optionnelle | iOS + Android | MVP | Moyenne
Désactiver overlay visuel notification en CarPlay/Android Auto | iOS + Android | MVP | Moyenne
Mettre à jour strings Info.plist avec justifications détaillées | iOS | MVP | Faible
Finaliser système modération UGC | Google + Apple | MVP | Élevée

Estimation totale : +5 jours développement avant soumission stores

## Stratégie de lancement

Phase 1 - MVP : - IAP uniquement (5.99€/mois mondial) - Modération UGC active - GPS avec disclosure - CarPlay/Android Auto basique

Phase 2 - Post-validation : - Prix différenciés US (lien externe 4.99€) - Paiement externe EU (DMA) - Monétisation créateurs (Mangopay)

## Conséquences

- Formation équipe sur politiques stores
- Suivi des métriques modération (% rejet, SLA)
- Migration iOS 26 SDK (Avril 2026)
- API Level 35 Android (2026)
- Communication transparente GPS/publicités

## Sources

- Android Auto Media Apps
- CarPlay Developer Guide
- Google Play UGC Policy
- App Store Guidelines
- Apple DMA Update EU
- Google Background Location 2026

# ADR-012 : Architecture Backend

Statut : Accepté Date : 2025-01-20

## Contexte

RoadWave nécessite une architecture backend évolutive tout en gardant la simplicité opérationnelle pour un MVP. Le système doit supporter une croissance progressive de 0 à 10M utilisateurs.

## Décision

Monolithe modulaire avec séparation claire en modules internes.

## Alternatives considérées

Architecture | Complexité | Coûts infra | Time to market | Évolutivité
--- | --- | --- | --- | ---
Monolithe modulaire | Faible | Faible | Rapide | 0-1M users
Microservices | Élevée | Élevée | Lent | 1M+ users
Hybrid (Mono + Workers) | Moyenne | Moyenne | Moyen | 100K-5M users

## Justification

- Simplicité : 1 seul binaire Go, déploiement trivial
- Transactions : Communications inter-modules en mémoire (pas de latence réseau)
- Debugging : Stack traces complètes, profiling unifié
- Coûts : 1 serveur suffit pour 100K users (vs N services)
- Refactoring : Modules internes bien séparés facilitent migration vers microservices si nécessaire

## Structure modulaire

    internal/
    ├── auth/         # Validation JWT, intégration Zitadel
    ├── user/         # Profils, centres d'intérêt
    ├── content/      # CRUD contenus, métadonnées
    ├── geo/          # Recherche géospatiale, algorithme
    ├── streaming/    # Génération HLS, transcoding
    ├── moderation/   # Signalements, workflow
    ├── payment/      # Intégration Mangopay
    └── analytics/    # Métriques écoute, jauges

Chaque module suit : `handler.go` → `service.go` → `repository.go`.

## Conséquences

- Scaling horizontal : réplication complète du binaire (acceptable jusqu'à 1M users)
- Transition vers microservices possible en phase 2 (extraction progressive des modules)
- Importance de maintenir découplage fort entre modules (interfaces claires)

# ADR-013 : ORM et Accès Données

Statut : Accepté Date : 2025-01-20

## Contexte

RoadWave nécessite des requêtes SQL complexes (PostGIS géospatiales) avec performance optimale et type safety. Le choix entre ORM, query builder ou SQL brut impacte maintenabilité et performance.

## Décision

sqlc pour génération de code Go type-safe depuis SQL.

## Alternatives considérées

Solution | Performance | Type Safety | Contrôle SQL | Courbe apprentissage
--- | --- | --- | --- | ---
sqlc | Excellente | Très haute | Total | Faible
GORM | Moyenne | Moyenne | Limité | Faible
pgx + SQL brut | Excellente | Faible | Total | Moyenne
sqlx | Bonne | Faible | Total | Faible

## Justification

- Performance : Génération compile-time, zero overhead runtime
- Type safety : Structs Go générées automatiquement, erreurs détectées à la compilation
- Contrôle SQL : Requêtes PostGIS complexes écrites en pur SQL (pas de limitations ORM)
- Maintenabilité : Modifications SQL → `sqlc generate` → code mis à jour
- Simplicité : Pas de magic, code généré lisible et debuggable

## Workflow

    -- queries/content.sql
    -- name: GetContentNearby :many
    SELECT id, title, ST_Distance(location, $1::geography) as distance
    FROM contents
    WHERE ST_DWithin(location, $1::geography, $2)
    ORDER BY distance
    LIMIT $3;

    sqlc generate

    // Code Go type-safe généré automatiquement
    contents, err := q.GetContentNearby(ctx, location, radius, limit)

## Conséquences

- Dépendance : `github.com/sqlc-dev/sqlc`
- Fichier `sqlc.yaml` à la racine pour configuration
- Migrations gérées séparément avec `golang-migrate`
- CI doit exécuter `sqlc generate` pour valider cohérence SQL/Go

# ADR-014 : Frontend Mobile

Statut : Accepté Date : 2025-01-20

## Contexte

RoadWave nécessite applications iOS et Android avec support CarPlay/Android Auto, lecture audio HLS avancée, géolocalisation temps réel. Le choix du framework impacte vélocité développement et performances.

## Décision

Flutter pour iOS et Android avec codebase unique.

## Alternatives considérées

Framework | Codebase | Performance | Audio/CarPlay | Communauté
--- | --- | --- | --- | ---
Flutter | Unique | Native | Excellente | Large
React Native | Unique | Bonne | Modules natifs requis | Très large
Native (Swift+Kotlin) | Double | Excellente | Native | Large
Ionic/Capacitor | Unique | Moyenne | Limitée | Moyenne

## Justification

- Codebase unique : iOS + Android maintenus ensemble, vélocité développement x2
- Performance : Dart compilé en code natif (pas de bridge JS)
- Audio HLS : Package `just_audio` mature avec support HLS, buffering adaptatif
- CarPlay/Android Auto : Support via packages communautaires (`flutter_carplay`, `android_auto_flutter`)
- Géolocalisation : `geolocator` robuste avec gestion permissions
- Écosystème : Widgets riches (Material/Cupertino), state management mature (Bloc, Riverpod)

## Packages clés

    dependencies:
      flutter_bloc: ^8.1.3        # State management
      just_audio: ^0.9.36         # Lecture audio HLS
      geolocator: ^11.0.0         # GPS temps réel (mode voiture)
      geofence_service: ^5.2.0    # Geofencing arrière-plan (mode piéton)
      flutter_local_notifications: ^17.0.0  # Notifications géolocalisées
      dio: ^5.4.0                 # HTTP client
      flutter_secure_storage: ^9.0.0  # Tokens JWT
      cached_network_image: ^3.3.1    # Cache images

Nouveaux packages (contenus géolocalisés) :

- `geofence_service` : Détection entrée/sortie rayon 200m en arrière-plan (mode piéton)
- Geofencing natif iOS/Android
- Minimise consommation batterie

- Supporte notifications push même app fermée

- `flutter_local_notifications` : Notifications locales avec compteur dynamique

- Notification avec compteur décroissant (7→1) en mode voiture
- Icônes personnalisées selon type contenu
- Désactivation overlay en mode CarPlay/Android Auto (conformité)

## Structure application

    lib/
    ├── core/           # Config, DI, routes
    ├── data/           # Repositories, API clients
    ├── domain/         # Models, business logic
    ├── presentation/   # UI (screens, widgets, blocs)
    └── main.dart

## Conséquences

- Équipe doit apprendre Dart (syntaxe proche Java/TypeScript)
- Taille binaire : 8-15 MB (acceptable)
- Tests : `flutter_test` pour widgets, `integration_test` pour E2E
- CI/CD : Fastlane pour déploiement stores

# ADR-015 : Stratégie Tests

Statut : Accepté Date : 2025-01-20

## Contexte

RoadWave nécessite une couverture tests robuste avec documentation vivante des use cases. La stratégie doit équilibrer vélocité développement et qualité.

## Décision

Approche multi-niveaux : unitaires, intégration, BDD (Gherkin), E2E, load testing.

## Stratégie par type

Type | Framework | Cible | Fréquence
--- | --- | --- | ---
Unitaires | Testify | 80%+ couverture | Chaque commit
Intégration DB | Testify + Testcontainers | Repositories critiques | Avant merge PR
BDD (Gherkin) | Godog | User stories | Avant release
E2E Mobile | Flutter integration_test | Parcours critiques | Nightly
Load | k6 | N/A | Avant mise en prod

## Tests unitaires (Testify)

    // internal/user/service_test.go
    func TestGetUserByID(t *testing.T) {
        mockRepo := new(MockRepository)
        service := NewService(mockRepo)

        mockRepo.On("FindByID", "123").Return(&User{ID: "123"}, nil)

        user, err := service.GetByID("123")

        assert.NoError(t, err)
        assert.Equal(t, "123", user.ID)
        mockRepo.AssertExpectations(t)
    }

Couverture minimale : 80% sur packages `internal/*/service.go`

## Tests BDD (Gherkin + Godog)

Voir ADR-007 pour contexte complet.

    # features/recommendation.feature
    Feature: Recommandation géolocalisée

      Scenario: Contenu proche prioritaire
        Given je suis à Paris (48.8566, 2.3522)
        And un contenu existe à 500m avec tag "tourisme"
        And mon intérêt "tourisme" est à 85%
        When je demande des recommandations
        Then le contenu est en première position
        And le score de pertinence est supérieur à 0.8

Couverture : Tous les cas d'usage du README.md traduits en `.feature`.

## Tests intégration (Testcontainers)

    // internal/geo/repository_integration_test.go
    func TestFindContentNearby(t *testing.T) {
        container := testcontainers.RunPostGISContainer(t)
        defer container.Terminate()

        repo := NewRepository(container.DB())

        // Insert test data
        repo.CreateContent(testContent)

        // Query
        results := repo.FindNearby(48.8566, 2.3522, 5000)

        assert.Len(t, results, 1)
    }

## Tests E2E Mobile (Flutter)

    // integration_test/player_test.dart
    testWidgets('Play audio and skip', (tester) async {
      await tester.pumpWidget(MyApp());

      await tester.tap(find.byIcon(Icons.play_arrow));
      await tester.pumpAndSettle();

      expect(find.text('Now Playing'), findsOneWidget);

      await tester.tap(find.byIcon(Icons.skip_next));
      expect(find.text('Next Content'), findsOneWidget);
    });

## Load testing (k6)

    // tests/load/streaming.js
    import http from 'k6/http';
    import { check } from 'k6';

    export let options = {
      stages: [
        { duration: '2m', target: 1000 },
        { duration: '5m', target: 10000 },
      ],
    };

    export default function () {
      let res = http.get('https://api.roadwave.com/v1/content/nearby');
      check(res, { 'status is 200': (r) => r.status === 200 });
    }

Objectif : API p99 < 100ms à 10K RPS.

## CI/CD Pipeline

    # .github/workflows/ci.yml
    - name: Unit tests
      run: go test -race -coverprofile=coverage.out ./...

    - name: BDD tests
      run: godog run features/

    - name: Integration tests
      run: go test -tags=integration ./...

    - name: Coverage gate
      run: |
        coverage=$(go tool cover -func=coverage.out | grep total | awk '{print $3}' | sed 's/%//')
        if (( $(echo "$coverage < 80" | bc -l) )); then
          echo "Coverage $coverage% < 80%"
          exit 1
        fi

## Conséquences

- Dépendances :
- `github.com/stretchr/testify`
- `github.com/cucumber/godog`
- `github.com/testcontainers/testcontainers-go`
- `grafana/k6`
- Temps CI : ~3-5 min (tests unitaires + BDD)
- Tests intégration/E2E : nightly builds (15-30 min)
- Load tests : avant chaque release majeure

# Règles métier RoadWave

> Documentation complète des règles métier validées pour l'application RoadWave. Chaque section détaille les comportements, flux et décisions techniques.

## 📋 Table des matières

### 01. Authentification & Inscription

Contenu : Inscription, connexion, récupération de compte

- Inscription : email/password uniquement (pas d'OAuth tiers)
- Vérification email : optionnelle auditeurs (limite 5 contenus), obligatoire créateurs (lien expire 7j)
- Connexion : 5 tentatives max, blocage 15 min, refresh token 30j
- Récupération mot de passe : email, lien expire 1h

### 02. Algorithme de recommandation

Contenu : Scoring, géolocalisation, orientation politique, mode Kids

- Classification géo : Ancré (70%) / Contextuel (50%) / Neutre (20%)
- Engagement : 20%, Aléatoire : 10%
- Orientation politique : 5 niveaux, équilibre imposé (40/40/20)
- Mode Kids : 4 tranches (3-6 / 6-9 / 9-12 / 13-15 ans), activation auto <13 ans
- Historique : >80% jamais reproposer, <10s ne pas reproposer

### 03. Centres d'intérêt et jauges

Contenu : Évolution jauges, valeurs initiales

- Like automatique : écoute ≥80% → +2%, écoute 30-79% → +1%
- Like explicite (manuel) : +2% (cumulable avec auto)
- Abonnement : +5%
- Skip rapide (<10s) : -0.5%
- Valeur initiale : 50% (neutre)
- Limites : 0-100% stricte, pas de dégradation temporelle

### 04. Création et publication de contenu

Contenu : Upload, métadonnées, validation, modification

- Formats : MP3, AAC (.mp3, .aac, .m4a), max 200 MB, 4h
- Métadonnées obligatoires : titre, type géo, zone, tags (1-3), classification âge
- Validation 3 premiers contenus : 24-48h (modération RoadWave)
- Modification : métadonnées uniquement, pas audio/zone/classification

### 05. Interactions et navigation

Contenu : Commandes Suivant/Précédent, interactions volant, lecture en boucle

- Suivant : pré-calcul 5 contenus, recalcul >10km ou 10 min
- Précédent : <10s → contenu avant, ≥10s → replay début
- Commandes volant : Suivant, Précédent, Play/Pause uniquement
- Like automatique : ≥80% écoute → +2 points, 30-79% → +1 point
- Actions manuelles : bouton cœur (arrêt véhicule) ou vocal (CarPlay/Android Auto)
- Passage auto après 2s (1s mode Kids)

### 06. Publicités

Contenu : Campagnes, fréquence, insertion, facturation

- Interface self-service, budget min 50€, étalement paramétrable
- Fréquence : 1/5 contenus (gratuits uniquement)
- Durée : 10-60s (recommandé 15-30s), skippable après 5s
- Validation manuelle 24-48h, prépaiement Mangopay
- Facturation : écoute complète 0.05€, skip après 5s : 0.02€, skip immédiat : 0€

### 07. Radio live

Contenu : Démarrage, arrêt, comportement auditeur

- Buffer 15s avant diffusion publique, durée max 8h
- Notification push abonnés dans zone géo uniquement
- Arrêt : compte à rebours 5s (manuel) ou auto si déco ≥60s
- Enregistrement auto MP3 256 kbps → replay sous 5-10 min
- Auditeur : buffer 15s, continuation si sortie zone, AUCUN chat

### 08. Abonnements et notifications

Contenu : Impact algorithme, notifications, audio-guides, limites

- Boost +30% au score final (pas priorité absolue)
- Détection contexte : <5 km/h piéton, >10 km/h voiture
- Voiture : in-app uniquement, Piéton : push actives
- Limite 10 notifications push/jour (5-20), mode silencieux 22h-8h
- Audio-guide piéton : détection <100m lieu, page sélection, navigation manuelle
- Max 200 abonnements, +5% jauges tous tags créateur

### 09. Monétisation créateurs

Contenu : Activation, KYC, sources revenus, paiement

- Conditions : compte ≥3 mois, ≥500 abonnés, ≥10K écoutes, 0 strike, ≥5 contenus/90j
- KYC via Mangopay Connect : SIRET, TVA, RIB pro, pièce ID, Kbis <3 mois
- Revenus pub : 3€ / 1000 écoutes complètes (6% CA pub)
- Revenus Premium : 70% créateur, 30% plateforme (proportionnel temps écoute)
- Paiement : seuil 50€, mensuel (15 du mois suivant), SEPA Mangopay

### 10. Premium

Contenu : Offre, multi-devices, avantages, gestion abonnement

- Prix : 4.99€/mois OU 49.99€/an (4.16€/mois effectif)
- Pas d'essai gratuit, pas de partage familial (MVP)
- Multi-devices : 1 seul stream actif, détection connexion simultanée
- Avantages : 0 pub, contenus exclusifs 👑, qualité 64 kbps Opus, offline illimité
- Paiement : Mangopay (web) ou IAP iOS/Android 5.99€/mois (+30% commission)

### 11. Mode offline

Contenu : Téléchargement, validité, synchronisation

- Zone géographique : choix manuel (autour de moi / ville / département / région)
- Nombre contenus : gratuit 50 max, Premium illimité
- WiFi par défaut, mobile avec confirmation + estimation volume
- Validité : 30 jours, renouvellement auto si WiFi (contenus >25 jours)
- Sync : likes/abonnements batch auto à reconnexion, queue actions 7j max

### 12. Gestion des erreurs

Contenu : Aucun contenu, contenu supprimé, perte réseau, GPS désactivé

- Aucun contenu : élargissement auto 50km → 100km → département → région → national
- Contenu supprimé : laisser terminer, passage auto suivant après 2s
- Perte réseau : buffer adaptatif (WiFi 5-120s, 4G 10-120s, 3G 30-300s), retry 5s max 6×
- GPS désactivé : mode dégradé (contenu national + neutre + téléchargé)

### 13. Conformité RGPD

Contenu : Consentements, anonymisation, export, suppression

- Consentement : Tarteaucitron.js + PostgreSQL versioning
- GPS précis : 24h puis geohash 5 (~5km²)
- Export : JSON + HTML + audio → ZIP, génération asynchrone sous 48h, expire 7j
- Suppression : grace period 30j, contenus créés anonymisés (créateur = "Utilisateur supprimé")
- Analytics : Matomo self-hosted, IP anonymisées, 0 cookie tiers
- DPO : fondateur formé CNIL (non obligatoire <250 employés)

### 14. Modération - Flows opérationnels

Contenu : Signalement, traitement, sanctions

- Signalement : 7 catégories (haine, sexuel, illégalité, droits auteur, spam, fake news, autre)
- IA pré-filtre : Whisper large-v3 (transcription) + NLP open source (1-10 min)
- SLA : Critique <2h (24/7), Haute/Moyenne <24h, Basse <72h
- Notification sanction : email + push + in-app (détail complet : catégorie, timestamp, transcription)
- Appel : formulaire in-app, délai 7j max, réponse 72h garanti (standard)

### 15. Autres comportements

Contenu : Partage, profil créateur, recherche

- Partage : bouton partout, lien `roadwave.fr/share/c/[id]`, web player + deep link
- Profil créateur : @pseudo, bio (300 car), stats publiques arrondies, badge vérifié ✓
- Badge vérifié : KYC validé OU célébrité OU >10K abonnés
- Recherche : full-text PostgreSQL (français, stemming), recherche géo (Nominatim OSM)
- Filtres : type, durée, âge, géo, tags, date, premium (combinables)
- Affichage : liste enrichie (20/page, infinite scroll) + vue carte Leaflet

### 16. Audio-guides multi-séquences

Contenu : Modes déplacement, navigation, déclenchement GPS, publicités

- 4 modes : 🚶 Piéton (manuel) / 🚗 Voiture (GPS auto + manuel) / 🚴 Vélo / 🚌 Transport
- Mode Piéton : pause auto après chaque séquence, user clique Suivant, navigation libre
- Mode Voiture : déclenchement GPS auto (rayon 30m), boutons manuels actifs, warning sécurité >10 km/h
- Affichage voiture : distance temps réel + ETA + direction (flèche) + vitesse
- Rayons : Voiture 30m, Vélo 50m, Transport 100m (configurable créateur 10-200m)
- Publicités : 1/5 séquences tous modes, auto-play, skippable 5s
- Reprise : sauvegarde auto (séquence + position exacte), popup si <30j, multi-device (sync cloud)

## 🗂️ Organisation

Chaque fichier de règles métier suit la structure :

1. Décisions : choix validés avec justifications
2. Comportements détaillés : flux utilisateur, cas limites
3. Paramètres : valeurs exactes, seuils, durées
4. Points d'attention Gherkin : éléments à tester

## 🚀 Utilisation

Ces documents servent de référence unique pour :

- ✅ Développement backend/frontend
- ✅ Écriture des tests Gherkin (BDD)
- ✅ Validation QA
- ✅ Documentation produit

Prochaine étape : Création des fichiers `.feature` Gherkin dans `features/` basés sur ces règles.

## 📊 Statistiques

- 16 sections validées
- ~12 000 lignes de spécifications détaillées
- Coût infrastructure MVP : ~50-250€/mois (hors salaires)
- Technologies : 100% open source (sauf Mangopay paiements)

Dernière mise à jour : Janvier 2026 Statut : ✅ Toutes sections validées

## 1. Authentification & Inscription

### 1.1 Méthodes d'inscription

Décision : Email/Password uniquement (pas d'OAuth tiers)

- ❌ Pas de Google, Apple, Facebook OAuth (dépendance services US/Chine)
- ✅ Email + mot de passe
- ✅ 2FA (Two-Factor Authentication) disponible
- ✅ Option "Appareil de confiance" (skip 2FA pour 30 jours)

Justification : - Souveraineté : pas de dépendance externe - RGPD : données 100% contrôlées - Coût : 0€ (Zitadel intégré)

### 1.2 Vérification email

Décision : Différenciée selon le rôle utilisateur

#### Pour les auditeurs (écoute uniquement)

État | Capacités
--- | ---
Email non vérifié | Lecture illimitée + création max 5 contenus
Email vérifié | Toutes fonctionnalités débloquées

Paramètres : - Lien de vérification expire après 7 jours - Possibilité de renvoyer le lien (max 3 fois/jour) - Rappel in-app après création du 3ème contenu

Justification : - Friction minimale à l'inscription - Anti-spam sans bloquer l'essai du produit - Incitation naturelle à vérifier (déblocage)

#### Pour les créateurs (monétisation)

Vérification obligatoire sous 7 jours pour : - Accès au programme de monétisation - KYC et reversement des revenus (conformité Mangopay) - Publication illimitée de contenus

Justification : - Conformité légale : KYC obligatoire pour transferts financiers - Anti-fraude : Vérification identité réelle pour paiements - Responsabilité : RoadWave doit pouvoir prouver identité créateurs monétisés

### 1.3 Données requises à l'inscription

Obligatoires : - ✅ Email (format validé) - ✅ Mot de passe (voir règles ci-dessous) - ✅ Pseudo (3-30 caractères, alphanumérique + underscore) - ✅ Date de naissance (vérification âge minimum)

Optionnelles : - ❌ Nom complet (privacy by design) - ❌ Photo de profil (avatar par défaut généré) - ❌ Bio (ajout ultérieur)

Âge minimum : - 13 ans minimum (conformité réglementation réseaux sociaux EU) - Vérification à l'inscription via date de naissance - Blocage inscription si <13 ans avec message explicite

Justification : - RGPD minimal data - Friction réduite (4 champs max) - Protection mineurs (obligation légale)

### 1.4 Tranches d'âge des contenus

Décision : Classification obligatoire des contenus

Catégories : - 🟢 Tout public (défaut) - 🟡 13+ : contenu mature léger (débats, actualité sensible) - 🟠 16+ : contenu mature (violence verbale, sujets sensibles) - 🔴 18+ : contenu adulte (langage explicite, sujets réservés)

Règles de diffusion : - Utilisateur 13-15 ans → contenus 🟢 uniquement - Utilisateur 16-17 ans → contenus 🟢 🟡 - Utilisateur 18+ → tous contenus

Modération : - Vérification obligatoire de la classification lors de la validation - Reclassification possible par modérateurs - Strike si classification volontairement incorrecte

Justification : - Protection mineurs (obligation légale) - Responsabilité plateforme - Coût : champ supplémentaire + règle algo

### 1.5 Validation mot de passe

Règles : - ✅ Minimum 8 caractères - ✅ Au moins 1 majuscule - ✅ Au moins 1 chiffre - ❌ Pas de symbole obligatoire (simplicité)

Validation : - Côté client (feedback temps réel) - Côté backend (sécurité) - Message d'erreur explicite par règle non respectée

Justification : - Standard industrie - Bloque 95% des mots de passe faibles - UX acceptable (pas trop restrictif)

### 1.6 Two-Factor Authentication (2FA)

Décision : Optionnel mais recommandé

Méthodes disponibles : - ✅ TOTP (Time-based One-Time Password) via app (Google Authenticator, Authy) - ✅ Email (code 6 chiffres, expire 10 min) - ❌ SMS (coût élevé ~0.05€/SMS)

Appareil de confiance : - Option "Ne plus demander sur cet appareil" → bypass 2FA pendant 30 jours - Révocable depuis paramètres compte - Liste des appareils de confiance visible

Justification : - Sécurité renforcée sans coût SMS - UX : appareil de confiance évite friction quotidienne - Zitadel natif (0€)

### 1.7 Tentatives de connexion

Règles : - Maximum 5 tentatives par période de 15 minutes - Blocage temporaire après 5 échecs - Compteur reset automatique après 15 min - Notification email si blocage (tentative suspecte)

Déblocage : - Automatique après 15 min - Ou via lien "Mot de passe oublié"

Justification : - Anti brute-force - Standard industrie (équilibre sécurité/UX) - Zitadel natif (0€)

### 1.8 Sessions et refresh tokens

Durée de vie : - Access token : 15 minutes - Refresh token : 30 jours

Rotation : - Refresh token rotatif (nouveau token à chaque refresh) - Ancien token invalidé immédiatement - Détection token replay attack

Extension automatique : - Si app utilisée, session prolongée automatiquement - Inactivité 30 jours → déconnexion

Justification : - Sécurité (token court-vie) - UX (pas de reconnexion fréquente) - Standard OAuth2/OIDC

### 1.9 Multi-device

Décision : Sessions simultanées illimitées

Gestion : - Liste des devices connectés visible (OS, navigateur, dernière connexion, IP/ville) - Révocation individuelle possible - Révocation globale "Déconnecter tous les appareils"

Alertes : - Notification push + email si connexion depuis nouveau device - Détection localisation suspecte (IP pays différent)

Justification : - UX maximale (écoute voiture + tablette maison + web) - Sécurité via transparence (utilisateur voit tout) - Coût : table sessions PostgreSQL

### 1.10 Récupération de compte

Méthode : Email uniquement

Processus : 1. Utilisateur clique "Mot de passe oublié" 2. Email avec lien de reset envoyé 3. Lien expire après 1 heure 4. Page de reset : nouveau mot de passe (validation règles) 5. Confirmation + déconnexion tous devices (sauf celui en cours)

Notifications : - Email immédiat si changement mot de passe - Push si changement depuis appareil non reconnu

Limite : - Maximum 3 demandes/heure (anti-spam)

Justification : - Standard sécurité - Pas de coût SMS - Protection contre attaque sociale

## Récapitulatif Section 1

## 2. Algorithme de recommandation

### 2.1 Classification de géo-pertinence

Décision : 3 types de contenus selon leur pertinence géographique

Type | Description | Exemple | Pondération géo
--- | --- | --- | ---
Géo-ancré | Contenu lié à un lieu précis | Audio-guide monument, pub restaurant local | 70%
Géo-contextuel | Pertinent dans une zone | Actualité régionale, événement local | 50%
Géo-neutre | Universel, pas de lien géo | Podcast philosophie, musique | 20%

Qui décide : - ✅ Créateur choisit le type à la publication - ✅ Modération peut reclassifier après validation - ✅ Modification possible après publication (tout le monde a le droit de se tromper)

Justification : - Différencie audio-guide (hyper-local) des podcasts génériques - Algorithme adapte automatiquement la pondération - Coût : champ supplémentaire en DB + règle algo

### 2.2 Formule de scoring

Décision : Score combiné dynamique selon type de contenu

    score_final = (score_geo * poids_geo_type)
                + (score_interets * poids_interets_type)
                + (score_engagement * 0.2)
                + (bonus_aleatoire)

    où :
    - score_geo = 1 - (distance_km / distance_max_km)
    - score_interets = moyenne des jauges utilisateur pour les tags du contenu
    - score_engagement = (taux_completion * 0.5) + (ratio_likes * 0.3) + (ratio_abonnements * 0.2)
    - bonus_aleatoire = 10% des recommandations tirées aléatoirement

Pondérations par type :

Type | Poids géo | Poids intérêts
--- | --- | ---
Géo-ancré | 0.7 | 0.1
Géo-contextuel | 0.5 | 0.3
Géo-neutre | 0.2 | 0.6

Paramètres : - Distance max recommandée : 200 km - Dégradation : linéaire (1 - distance/200km) - Rayon point GPS : 500m (adapté au volume de contenu local)

Tous ces paramètres sont configurables à chaud via interface admin.

Justification : - Flexibilité totale selon type de contenu - Linéaire = rattrapage naturel du contenu viral ancien - Auditable via métriques engagement (moyenne/médiane)

### 2.3 Score d'engagement et popularité

Décision : Intégration popularité avec poids 0.2

Métriques : - Taux de complétion : écoutes >80% / total écoutes (poids 0.5) - Ratio likes : likes / écoutes (poids 0.3) - Ratio abonnements : nouveaux abonnés après écoute / écoutes (poids 0.2)

Seuil minimum : - Minimum 50 écoutes avant de considérer l'engagement - Contenu <50 écoutes : score engagement = 0.5 (neutre)

Contenu viral : - Un contenu viral à Paris peut être proposé à Marseille - Score géo faible compensé par score engagement élevé - Paramétrable admin

Dépréciation temporelle : - Pas de dépréciation automatique - Ratio linéaire = contenu ancien mais toujours apprécié reste pertinent

Justification : - Équilibre découverte / qualité - Pas de pénalisation arbitraire des contenus anciens - Coût : calculs sur métriques existantes

### 2.4 Part d'aléatoire (exploration)

Décision : 10% par défaut, paramétrable utilisateur

Fonctionnement : - 1 contenu sur 10 = tirage aléatoire (hors historique déjà écouté) - Utilisateur peut ajuster : curseur 0% (aucun aléatoire) à 50% (exploration max)

Curseur utilisateur : - 🎯 0% : Personnalisé max (recommandations strictes) - ⚖️ 10% : Équilibré (défaut) - 🎲 30% : Découverte élevée - 🌍 50% : Découverte max (équivaut à national = découverte)

Justification : - Évite la bulle de filtre - Laisse l'utilisateur maître de son expérience - Coût : variable aléatoire en algo

### 2.5 Contenu politique (version MVP simplifiée)

> ⚠️ Note : La classification politique avancée (échelle gauche/droite, équilibrage imposé) a été reportée post-MVP. Voir ANNEXE-POST-MVP.md pour la version complète.

Décision MVP : Tag simple "Politique" sans classification idéologique

Tagging : - Créateur peut taguer son contenu comme "Politique" (optionnel) - Tag "Politique" au même niveau que "Économie", "Sport", "Culture", etc. - Pas de classification gauche/droite - Pas d'équilibrage imposé

Filtrage utilisateur : - Option paramètres : "Masquer contenu politique" - Si activé → 0% de contenus tagués "Politique" dans le feed - Par défaut : désactivé (tous contenus visibles)

Justification MVP : - Simplicité : Pas de modération politique coûteuse (~2000€/mois économisés) - Neutralité technique : Aucun jugement éditorial sur orientation - Risque minimal : Évite controverses et contentieux DSA au lancement - Fonctionnel : Utilisateurs peuvent filtrer si souhaité

Post-MVP : - Classification avancée possible si forte demande utilisateurs - Nécessite ressources modération dédiées et audit DSA

### 2.6 Mode Kids (13-15 ans)

Décision : Mode optionnel pour adolescents 13-15 ans uniquement

> ⚠️ Note : Âge minimum d'inscription = 13 ans (obligation légale EU). Pas d'utilisateurs <13 ans sur la plateforme.

Tranche concernée :

Tranche | Description | Contenus autorisés | Restrictions
--- | --- | --- | ---
13-15 ans | Collège | Contenus "Tous publics" uniquement | Filtrage 16+ et 18+

Activation : - ❌ Pas d'activation automatique (tous les utilisateurs ont ≥13 ans) - ✅ Activation manuelle via toggle paramètres - ✅ Parents peuvent activer pour leurs enfants 13-15 ans - ✅ Utilisateur peut désactiver à tout moment

Filtrage quand Mode Kids activé : - ✅ Contenus "Tous publics" uniquement - ❌ Exclusion contenus 16+ et 18+ - ❌ Pas de contenu politique (automatiquement filtré) - ❌ Pas de publicité (ou uniquement pub validée manuellement)

Interface : - Interface standard (pas d'interface dédiée enfants pour MVP) - Filtrage algorithmique des contenus inappropriés

Justification : - Conformité légale : Âge minimum 13 ans (RGPD, DSA) - Simplicité MVP : Un seul mode optionnel vs 4 tranches d'âge - Protection mineurs : Filtrage contenus adultes pour 13-15 ans - Flexibilité : Parents décident d'activer ou non

### 2.7 Déclenchement géographique

Décision : Notification au passage, pas d'anticipation

Fonctionnement : 1. Utilisateur passe à <500m d'un point GPS (contenu géo-ancré) 2. Notification sonore (bip court) + visuelle (logo selon type) 3. Types de logos : 📍 Info, 🏛️ Culturel, 🍴 Commercial, 🎭 Événement 4. Délai réaction utilisateur : 5 secondes pour accepter (bouton volant ou commande vocale) 5. Si accepté → lecture immédiate 6. Si ignoré → contenu proposé normalement en file d'attente

Publicités : - ⚠️ Jamais d'interruption de contenu en cours - Pub s'intercale entre deux séquences uniquement - Notification pub : son différent (facultatif selon paramètres)

Gestion demi-tour : - Si utilisateur repart du point après notification → pas de nouvelle notification (déjà proposé) - Réinitialisation après 24h

Justification : - Respect écoute en cours (pas de coupure brutale) - UX fluide (utilisateur garde contrôle) - Simplicité technique (pas de prédiction trajectoire)

### 2.8 Historique et repropositon

Décision : Pas de reproposition sauf contenu partiel

Règles :

État écoute | Completion | Action
--- | --- | ---
Écouté complètement | >80% | ❌ Ne jamais reproposer (sauf flag `replayable = true` pour audio-guides)
Skippé rapidement | <10s | ❌ Ne pas reproposer
Partiellement écouté | 10-80% | ✅ Reproposer avec reprise position (`last_position_seconds`)

Stockage historique : - Table `user_content_history` (user_id, content_id, completion_rate, last_position, listened_at) - Historique illimité (PostgreSQL) - Algorithme considère les 100 derniers pour optimisation requêtes - Export complet disponible (RGPD)

Justification : - Découverte maximale (pas de redites) - Respect erreurs de clic (contenu partiel = 2nde chance) - Coût stockage négligeable (PostgreSQL scalable)

### 2.9 Paramétrabilité admin (interface dashboard)

Décision : Tous paramètres scoring exposés + A/B testing

Paramètres configurables à chaud :

Paramètre | Plage | Défaut | Unité
--- | --- | --- | ---
`poids_geo_ancre` | 0.5 - 1.0 | 0.7 | %
`poids_geo_contextuel` | 0.3 - 0.7 | 0.5 | %
`poids_geo_neutre` | 0.0 - 0.4 | 0.2 | %
`poids_engagement` | 0.0 - 0.5 | 0.2 | %
`part_aleatoire_global` | 0.0 - 0.3 | 0.1 | %
`distance_max_km` | 50 - 500 | 200 | km
`rayon_gps_point_m` | 100 - 2000 | 500 | m
`seuil_min_ecoutes_engagement` | 10 - 200 | 50 | nb

Application changements : - Immédiat : nouveaux calculs utilisent nouvelle config - Aucun recalcul batch (coût CPU) - Version config trackée (git-like) - Rollback 1 clic

A/B Testing : - Création variantes (Config A vs Config B) - Split utilisateurs 50/50 aléatoire - Métriques comparatives : taux complétion, engagement, session duration - Dashboard graphique temps réel

Audit engagement : - Métriques clés : moyenne/médiane temps d'écoute par session - Graphiques : évolution engagement selon config - Export CSV pour analyse externe

Justification : - Optimisation continue sans redéploiement - Data-driven decisions (métriques objectives) - Coût : dashboard admin à développer (one-time)

### 2.10 Paramétrabilité utilisateur

Décision : Curseurs avancés avec profils sauvegardables

Niveaux de personnalisation :

Curseurs disponibles : - 📍 Géolocalisation : Local ← slider → National (découverte = national) - 🎲 Découverte : 0% ← slider → 50% (part aléatoire) - ⚖️ Politique : Masquer / Équilibré / Mes préférences

Profils sauvegardables : - 🚗 Trajet quotidien (boulot) : géo local, découverte 5%, politique masqué - 🛣️ Road trip : géo régional, découverte 30%, politique équilibré - 👶 Enfants : Mode Kids activé

Synchronisation : - ✅ Sync profils entre devices (cloud PostgreSQL) - ❌ Pas de partage profils entre utilisateurs (famille) - Auto-switch selon context (détection trajet récurrent via GPS)

Sécurité conduite : - ⚠️ Blocage modification si vitesse GPS >10 km/h - Warning au lancement app : "Configurez avant de prendre la route" - Modifications uniquement app arrêtée/passager

Justification : - Utilisateur maître de son expérience - Contextes d'usage différents (quotidien vs voyage) - Sécurité routière (pas de distraction)

### 2.11 Médias traditionnels

Décision : Ouverture aux médias établis

Médias autorisés : - Presse nationale : Le Monde, Le Parisien, Libération, Le Figaro, etc. - Radios : France Inter, RTL, Europe 1, etc. - Médias régionaux : Ouest-France, Sud-Ouest, etc.

Format contenus : - Flashs info géolocalisés (actualité régionale) - Chroniques thématiques (culture, économie, sport) - Éditos et débats (classification politique appliquée)

Validation : - Compte média vérifié (badge ✓) - Pas de validation 3 premiers contenus (confiance établie) - Modération a posteriori uniquement

Monétisation : - Partage revenus pub standard (même conditions créateurs) - Possibilité sponsoring direct (pas via plateforme)

Justification : - Crédibilité plateforme (contenus professionnels) - Diversité éditoriale - Attractivité grand public (noms reconnus)

## Récapitulatif Section 2

## 3. Centres d'intérêt et jauges

### 3.1 Évolution des jauges

Décision : Système simple avec valeurs fixes

Action | Impact jauge | Justification
--- | --- | ---
Like automatique renforcé (≥80% écoute) | +2% | Signal fort d'intérêt (écoute quasi-complète)
Like automatique standard (30-79% écoute) | +1% | Signal modéré d'intérêt
Like explicite (manuel) | +2% | Signal fort, cumulable avec auto
Abonnement créateur | +5% sur tous ses tags | Signal très fort d'affinité
Skip rapide (<10s) | -0.5% | Désintérêt marqué
Skip tardif (≥30%) | 0% | Neutre (contenu essayé suffisamment)

Paramètres techniques : - Les jauges sont bornées strictement entre 0% et 100% - Calcul immédiat à chaque action (pas de batch différé) - Les tags du contenu sont définis par le créateur à la publication - Si un contenu a plusieurs tags, chaque jauge correspondante est impactée

Exemple de calcul :

    Contenu de 5 minutes tagué "Automobile" + "Voyage"

    Scénario 1 : Écoute 4min30 (90%)
    → Like automatique renforcé (+2%)
    → Jauge Automobile : 45% → 47%
    → Jauge Voyage : 60% → 62%

    Scénario 2 : Écoute 2min30 (50%)
    → Like automatique standard (+1%)
    → Jauge Automobile : 45% → 46%
    → Jauge Voyage : 60% → 61%

    Scénario 3 : Écoute 2min30 (50%) + Like manuel
    → Like auto +1% puis like manuel +2% = +3% total
    → Jauge Automobile : 45% → 48%
    → Jauge Voyage : 60% → 63%

    Scénario 4 : Skip après 5s
    → Signal négatif (-0.5%)
    → Jauge Automobile : 45% → 44.5%
    → Jauge Voyage : 60% → 59.5%

Justification : - Like automatique : Reflète l'engagement réel (voir ADR-010) - Sécurité routière : Pas d'action complexe en conduite - Prévisibilité : Règles claires et déterministes - Coût minimal : Calculs simples en backend - Fiabilité : Pas d'edge cases complexes - Ajustable : Valeurs modifiables via dashboard admin si besoin

### 3.2 Jauge initiale

Décision : Démarrage neutre à 50%, pas de questionnaire

À l'inscription : - Toutes les jauges d'intérêt sont initialisées à 50% - Pas de questionnaire onboarding (friction zéro) - L'algorithme apprend naturellement via les premières écoutes

Catégories disponibles : - Automobile - Voyage - Famille - Amour - Musique - Économie - Cryptomonnaie - Politique - Culture générale - Sport - Technologie - Santé - ... (extensible)

Cold start (premiers jours) : 1. Nouvel utilisateur s'inscrit → toutes jauges à 50% 2. Écoute premier podcast "Automobile" → jauge Auto monte à 51% 3. Skip un contenu "Économie" → jauge Éco descend à 48% 4. Après 10-15 écoutes, profil commence à se dessiner clairement

Alternative optionnelle (post-MVP) : - Questionnaire optionnel proposé après 3 écoutes (in-app) - Message : "Améliorez vos recommandations en sélectionnant vos centres d'intérêt" - Si rempli : jauges sélectionnées passent à 70%, non sélectionnées à 30% - Si skip : conserve 50% partout

Justification : - Inscription ultra-rapide : pas de questionnaire = moins de churn - Découverte naturelle : l'algorithme apprend en quelques écoutes - Équitable : pas de biais initial vers certains créateurs - Comportement déterministe : facile à tester et débugger - Cold start acceptable : à 50%, tous les contenus ont une chance égale initialement

### 3.3 Dégradation temporelle

Décision : Pas de dégradation automatique

Les jauges ne diminuent jamais avec le temps de manière automatique.

Règle : - Une jauge ne change que par les actions utilisateur (like, écoute, skip) - Pas de cron job de dégradation périodique - Pas de "rafraîchissement" artificiel

Scénario illustratif :

    Utilisateur aimait "Économie" (jauge 80%) il y a 1 an
    → Depuis, skip tous les contenus Éco
    → Jauge descend naturellement à 40% via les skips
    → Pas besoin de dégradation temporelle

Si utilisateur inactif longtemps : - Utilisateur part en vacances 6 mois → jauges conservées - Au retour : ses jauges reflètent toujours ses goûts d'avant - Comportement cohérent et prévisible

Alternative utilisateur (contrôle explicite) : - Bouton "Réinitialiser mes centres d'intérêt" dans paramètres - Action manuelle : remet toutes les jauges à 50% - Permet nouveau départ si souhaité (changement de vie, etc.)

Justification : - Principe KISS (Keep It Simple, Stupid) - Coût 0 : pas de batch nocturne, pas de calculs temporels - Fiabilité maximale : pas de bugs de fuseaux horaires, dates, etc. - UX prévisible : jauge = reflet des actions, pas d'automatisme caché - Respect historique : si utilisateur aimait X depuis 2 ans, pourquoi "oublier" ? - Évolution naturelle : les actions récentes suffisent à faire évoluer les jauges

## Récapitulatif Section 3

## 4. Création et publication de contenu

### 4.1 Upload et encodage

Décision : Formats universels avec encodage asynchrone

Formats acceptés : - ✅ MP3 (`.mp3`) - ✅ AAC (`.aac`, `.m4a`) - ❌ WAV, FLAC (trop lourds, inutiles en voiture)

Limites :

Paramètre | Valeur | Justification
--- | --- | ---
Taille maximale | 200 MB | ~4h de podcast à 128 kbps
Durée maximale | 4 heures | Suffisant pour podcasts longs
Validation format | Client + backend | Double sécurité

Pipeline d'encodage :

    1. Upload fichier (MP3/AAC) → Bunny Storage temporaire
    2. Job asynchrone (worker Go + FFmpeg) :
       - Validation format et intégrité
       - Réencodage Opus 3 profils (24/48/64 kbps)
       - Génération segments HLS (.m3u8 + .ts)
       - Génération image couverture par défaut
    3. Suppression fichier original (économie stockage)
    4. Notification créateur : "Contenu prêt à publier"

Temps d'encodage estimé : - Contenu 5 min → ~30 secondes - Podcast 1h → ~5 minutes - Podcast 4h → ~20 minutes

Profils Opus générés :

Qualité | Bitrate | Usage
--- | --- | ---
Basse | 24 kbps | 2G/Edge
Standard | 48 kbps | 3G (défaut)
Haute | 64 kbps | 4G/5G

Écoute accélérée :

Vitesse | Usage
--- | ---
0.75x | Compréhension difficile (accent, technique)
1.0x | Normal (défaut)
1.25x | Gain léger
1.5x | Podcasts longs
2.0x | Survol rapide (modérateurs)

Disponible pour : - ✅ Modérateurs (validation rapide : 30s → 15s à 2x) - ✅ Auditeurs (tous les contenus) - ✅ Standard industrie (YouTube, Spotify, Apple Podcasts)

Justification : - Simplicité : 2 formats couvrent 95% des cas d'usage - Coût optimisé : pas de conversion WAV/FLAC lourds - Stockage réduit : suppression original après encodage - Scalabilité : workers horizontalement (Kubernetes jobs) - Productivité : écoute accélérée = double productivité modération

### 4.2 Métadonnées obligatoires

Décision : Minimaliste pour réduire friction

Champs obligatoires :

Champ | Format | Validation
--- | --- | ---
Titre | 5-100 caractères | Alphanumérique + ponctuation basique
Type géo | Enum | Ancré / Contextuel / Neutre
Zone diffusion | Composite | Voir détails ci-dessous
Tags | Enum | 1 à 3 parmi liste prédéfinie
Classification âge | Enum | Tout public / 13+ / 16+ / 18+

Zone de diffusion (obligatoire) :

Options mutuellement exclusives : - Point GPS : latitude + longitude + rayon (100m à 10km) - Ville : sélection dans référentiel INSEE - Département : sélection liste - Région : sélection liste - National : France entière

Tags disponibles (1 à 3 obligatoires) : - Automobile - Voyage - Famille - Amour - Musique - Économie - Cryptomonnaie - Politique - Culture générale - Sport - Technologie - Santé

Champs optionnels : - ❌ Description (ajout ultérieur) - ❌ Image couverture (génération auto)

Image de couverture par défaut :

Génération automatique selon règles : - Icône selon type géo : 📍 Ancré / 🌍 Contextuel / 🎧 Neutre - Couleur selon tag principal : bleu (Auto), vert (Voyage), rouge (Musique), etc. - Format 800×800px, PNG - Personnalisable ultérieurement (post-MVP)

Exemple de publication :

    Titre : "Histoire de la Tour Eiffel"
    Type géo : Ancré
    Zone : Point GPS (48.8584, 2.2945, rayon 500m)
    Tags : Voyage, Culture générale
    Classification : Tout public
    → Image auto : 📍 fond bleu-vert (Voyage)

Justification : - Friction minimale : 5 champs max = 2 min de publication - Publication rapide : pas de blocage sur description/image - Coût 0 : pas de génération IA au MVP - Évolutif : champs optionnels ajoutables ultérieurement

### 4.3 Validation des 3 premiers contenus

Décision : Validation manuelle par équipe modération RoadWave

Processus nouveau créateur :

1. Créateur upload ses 3 premiers contenus
2. Contenus passent en file d'attente modération
3. Modérateur junior RoadWave :
4. Écoute 30 secondes (ou 15s à 2x)
5. Vérifie métadonnées
6. Valide ou rejette avec raison
7. Si accepté : contenu publié + notification créateur
8. Si refusé : notification avec raison détaillée + lien vers règles
9. Après 3 contenus validés : créateur passe en statut vérifié

Critères de validation :

Critère | Détails
--- | ---
Qualité audio | Compréhensible (pas de grésillement excessif)
Respect règles | Pas de contenu prohibé évident (haine, spam, illégal)
Classification âge | Cohérente avec contenu écouté
Tags pertinents | Correspondance minimale avec contenu
Zone diffusion | Cohérente (pas "Tour Eiffel" avec zone "National")

Délai de validation : - Objectif : 24-48h (jours ouvrés) - Priorité : FIFO (First In First Out) - Weekend : délai peut atteindre 72h - Message au créateur : "Validation en cours, délai estimé 24-48h"

Notification créateur :

Si accepté : - Email + push : "✅ Votre contenu '[Titre]' est en ligne !" - Lien direct vers le contenu - Compteur : "2/3 contenus validés pour devenir créateur vérifié"

Si refusé : - Email + push : "❌ Contenu '[Titre]' refusé" - Raison détaillée : "Qualité audio insuffisante" / "Tags non pertinents" / "Classification incorrecte" / etc. - Lien vers règles de publication - Possibilité de correction + resoumission

Après 3 validations :

Créateur obtient statut "Vérifié" : - Badge ✓ visible sur profil - Contenus futurs publiés immédiatement (modération a posteriori uniquement) - Modération seulement si signalé par utilisateurs

Outils modérateur : - Écoute accélérée (1.5x ou 2x) = double productivité - Interface dédiée : queue de contenus à valider - Raccourcis clavier : A (Accepter), R (Rejeter), Espace (Pause) - Historique créateur visible (si déjà 1-2 contenus validés)

Modération communautaire (post-MVP) :

⚠️ Non implémenté au MVP (complexité juridique)

Vision future (envisageable) : - Créateurs établis peuvent opt-in "Modérateur communautaire" - Formation obligatoire (30 min) + quiz (80%) - Pré-validation uniquement (validation finale toujours par équipe RoadWave) - Compensation : badges, premium offert - Attribution aléatoire (pas de collusion)

Justification décision MVP : - Responsabilité juridique : plateforme reste responsable (DSA EU) - Qualité garantie : modérateurs formés et mandatés - Anti-spam efficace : bloque 95% des abus dès le début - Coût raisonnable : 30s × 3 contenus = 1.5 min/créateur - UX acceptable : délai 24-48h expliqué clairement - Pas de validation par pairs au MVP = évite risques juridiques (collusion, compétence, conflits)

### 4.4 Modification et suppression

Décision : Modification métadonnées uniquement, suppression immédiate

Modification autorisée :

Élément | Modifiable | Justification
--- | --- | ---
Titre | ✅ | Correction coquilles
Description | ✅ | Si ajoutée ultérieurement
Tags | ✅ | Ajustement pertinence
Image couverture | ✅ | Personnalisation
Audio | ❌ | Intégrité contenu
Zone diffusion | ❌ | Évite manipulation algo
Type géo | ❌ | Évite manipulation algo
Classification âge | ❌ | Sécurité mineurs

Raisons restrictions :

Audio non modifiable : - Évite fraude : uploader contenu validé → remplacer par spam - Intégrité : auditeurs doivent écouter ce qui a été validé

Zone/Type non modifiables : - Évite manipulation : créer "Local Paris" → changer en "National" pour boost visibilité - Évite abus : créer "Neutre" (faible pondération géo) → changer en "Ancré" (forte pondération)

Classification non modifiable : - Évite contournement : uploader "Tout public" → passer en "18+" sans revalidation - Sécurité : garantit que classification a été vérifiée

Si besoin de changer audio/zone/classification : - Action : Supprimer contenu + republier - Si créateur <3 contenus validés : retourne en file validation - Si créateur ≥3 contenus validés : publication immédiate

Suppression de contenu :

Aspect | Comportement
--- | ---
Délai | Immédiat
Réversibilité | Non
Historique auditeurs | Marqué "Contenu supprimé par créateur"
Analytics plateforme | Anonymisé et conservé
Fichiers CDN | Supprimés sous 24h

Exemple scénario suppression :

    Créateur supprime podcast écouté par 1000 personnes
    → CDN : fichiers purgés sous 24h
    → BDD : entrée marquée "deleted", auteur anonymisé
    → Historique auditeurs : "Contenu supprimé" (conserve durée écoute pour stats)
    → Analytics : métriques globales conservées (anonymes, RGPD OK)

Notifications suppression : - Pas de notification aux auditeurs (pour éviter effet Streisand) - Historique reste consultable : "Vous avez écouté ce contenu le [date]" - Si auditeur tente de réécouter : "Ce contenu n'est plus disponible"

Justification : - Simplicité : règles claires et non-ambiguës - Sécurité : évite manipulations algorithme et contournements modération - Contrôle créateur : liberté totale de supprimer (RGPD) - Traçabilité : historique conservé pour analytics (anonymisé) - Coût 0 : pas de revalidation métadonnées

## Récapitulatif Section 4

## 5. Interactions et navigation

### 5.1 File d'attente et commande "Suivant"

Décision : Pré-calcul 5 contenus avec insertion prioritaire pour points géographiques

File d'attente : - 5 contenus pré-calculés en cache (Redis) - Recalcul automatique si : - Déplacement >10km - Toutes les 10 minutes (rafraîchissement contenu) - File d'attente <3 contenus restants

Insertion prioritaire géo-ancrée (mode voiture uniquement) :

Détection : - Calcul ETA (Estimated Time of Arrival) via API GPS native iOS/Android - Notification déclenchée 7 secondes avant d'arriver au point GPS - Si vitesse < 5 km/h ET distance < 50m → notification immédiate - ⚠️ App doit être ouverte (pas de détection en arrière-plan en mode voiture)

Notification : - Sonore uniquement : bip court ou son personnalisé RoadWave - Visuelle minimale : icône selon type de contenu (🏛️ culture, 👨‍👩‍👧 famille, 🎵 musique, etc.) - Compteur visible : 7...6...5...4...3...2...1 (décompte des secondes) - Pas de texte affiché (éviter distraction conducteur) - Pas de bouton "Annuler" : seul le bouton "Suivant" permet validation

Actions utilisateur : 1. User entend notification sonore + voit icône et compteur 2. User appuie "Suivant" dans les 7 secondes → décompte 5s démarre 3. Pendant décompte : contenu actuel continue, compteur visible (5...4...3...2...1) 4. Si contenu actuel se termine pendant décompte → contenu suivant du buffer démarre 5. À la fin du décompte → contenu géolocalisé démarre (fade out/in 0.3s)

Si user n'appuie pas sur "Suivant" : - Notification disparaît après 7 secondes - Contenu géolocalisé est perdu (pas d'insertion dans file) - Pas de nouveau contenu géolocalisé pendant 10 minutes (éviter spam)

Limitation anti-spam : - Maximum 6 contenus géolocalisés par heure - Timer reset toutes les heures (rolling window) - Exception : séquences d'un même audio-guide multi-séquences (comptent comme 1) - Si quota atteint : notifications suivantes ignorées jusqu'à libération du quota

Invalidation immédiate : - Utilisateur change ses préférences (curseurs géo/découverte/politique) - ⚠️ Modification bloquée si vitesse GPS >10 km/h (sécurité routière) - Live démarre d'un créateur suivi dans la zone

Implémentation :

    Redis cache :
      - Clé : user:{user_id}:queue
      - Structure : [content_1, content_2, ..., content_5]
      - Métadonnées : {last_lat, last_lon, computed_at, mode: "voiture"|"pieton"}
      - TTL : 15 minutes

    Tracking GPS temps réel (mobile) :
      - Vérification toutes les 1 seconde
      - Calcul ETA vers points géolocalisés proches (rayon 500m)
      - Si ETA ≤ 7s → trigger notification
      - Historique GPS : 30 derniers points pour calcul vitesse moyenne

    Quota anti-spam (Redis) :
      - Clé : user:{user_id}:geo_quota
      - Structure : sorted set avec timestamps des 6 derniers contenus
      - TTL : 1 heure
      - Vérification avant notification : ZCOUNT pour compter contenus dernière heure

    Cooldown après ignorance (Redis) :
      - Clé : user:{user_id}:geo_cooldown
      - TTL : 10 minutes
      - Set après notification ignorée

Justification : - Expérience fluide : pas de latence au clic "Suivant" - Réactivité géo : contenu local inséré immédiatement - Coût optimisé : recalcul uniquement si nécessaire - Sécurité : pas de modification en conduite

### 5.1.2 Mode piéton (audio-guides)

Décision : Notifications push en arrière-plan avec rayon large

Contexte : - Mode piéton détecté automatiquement si vitesse moyenne < 5 km/h - Cas d'usage : visites à pied, musées, monuments, quartiers historiques - User n'a pas besoin d'avoir l'app ouverte - ⚠️ Fonctionnalité optionnelle : requiert permission "localisation en arrière-plan" (activée par user)

Détection : - App peut être en arrière-plan (si permission accordée) - Rayon de détection : 200 mètres autour du point GPS - Geofencing iOS/Android pour minimiser consommation batterie - Permission demandée uniquement si user active "Notifications audio-guides piéton" dans settings

Notification push système :

Format :

    Titre : "Audio-guide à proximité"
    Body : "[Nom du contenu] - [Nom créateur]"
    Action : Tap → ouvre app sur le contenu

Exemple :

    Audio-guide à proximité
    Musée du Louvre : La Joconde - @paris_museum

Permissions requises :

⚠️ Important : Permission "Always Location" est optionnelle et demandée uniquement si user active le mode piéton dans settings.

iOS (`Info.plist`) :

    <key>NSLocationWhenInUseUsageDescription</key>
    <string>RoadWave utilise votre position pour vous proposer des contenus audio géolocalisés adaptés à votre trajet en temps réel.</string>

    <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
    <string>Si vous activez les notifications audio-guides piéton, RoadWave peut vous alerter lorsque vous passez près d'un monument ou musée, même quand l'app est en arrière-plan. Cette fonctionnalité est optionnelle et peut être désactivée à tout moment dans les réglages.</string>

Android (`AndroidManifest.xml`) :

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

Disclosure avant demande permission (Android requis, iOS recommandé) :

Écran affiché avant demande permission "Always Location" :

    ┌────────────────────────────────────────┐
    │ 📍 Notifications audio-guides piéton   │
    ├────────────────────────────────────────┤
    │ Pour vous alerter d'audio-guides à     │
    │ proximité même quand vous marchez avec │
    │ l'app fermée, RoadWave a besoin de     │
    │ votre position en arrière-plan.        │
    │                                        │
    │ Votre position sera utilisée pour :   │
    │ ✅ Détecter monuments à 200m           │
    │ ✅ Vous envoyer une notification       │
    │                                        │
    │ Votre position ne sera jamais :        │
    │ ❌ Vendue à des tiers                  │
    │ ❌ Utilisée pour de la publicité       │
    │                                        │
    │ Cette fonctionnalité est optionnelle.  │
    │ Vous pouvez utiliser RoadWave sans     │
    │ cette permission.                      │
    │                                        │
    │ [Continuer] [Non merci]                │
    │                                        │
    │ Plus d'infos : Politique confidentialité│
    └────────────────────────────────────────┘

Si user refuse : - Mode piéton désactivé (uniquement mode voiture disponible) - App fonctionne normalement avec permission "When In Use" - Audio-guides accessibles en mode manuel (user ouvre app, sélectionne contenu)

Comportement après tap sur notification : 1. User tap notification push 2. App s'ouvre sur la page du contenu 3. User peut démarrer la lecture manuellement 4. Navigation libre (voir section 16.2 pour audio-guides piéton)

Basculement automatique voiture ↔ piéton :

Détection par vitesse GPS moyenne sur 30 secondes : - Vitesse < 5 km/h (stable 10s) → mode piéton - Vitesse ≥ 5 km/h (stable 10s) → mode voiture

Changements de mode :

Mode actuel | Vitesse détectée | Nouveau mode | Effet
--- | --- | --- | ---
Piéton | ≥ 5 km/h | Voiture | Notifications push → sonores + icône (app ouverte requise)
Voiture | < 5 km/h | Piéton | Notifications sonores → push arrière-plan

Pas de popup confirmation : - Basculement transparent et automatique - User n'a rien à faire - Hysteresis (10s) pour éviter basculements intempestifs

Quota anti-spam mode piéton : - Même limitation que mode voiture : 6 contenus/heure - Cooldown 10 min si notification ignorée (app pas ouverte après tap)

Justification : - ✅ Expérience adaptée aux visites à pied (rayon large, pas de timing précis) - ✅ Économie batterie (geofencing natif iOS/Android) - ✅ User peut garder téléphone en poche - ✅ Basculement automatique = pas de friction

### 5.2 Commande "Précédent"

Décision : Comportement smart selon progression écoute

Règles :

Situation | Temps écouté | Action "Précédent"
--- | --- | ---
Début de contenu | <10 secondes | Retour au contenu précédent (position exacte)
Milieu/fin | ≥10 secondes | Replay contenu actuel depuis le début
Premier de session | N/A | Replay depuis début (rien avant)

Historique de navigation : - 10 contenus maximum en mémoire (Redis List) - Structure : `[{content_id, position_seconds, listened_at}, ...]` - FIFO : au-delà de 10, suppression du plus ancien

Exemple scénario :

    Utilisateur écoute :
    1. Contenu A → écoute 5s → "Suivant"
    2. Contenu B → écoute 2min30 → "Suivant"
    3. Contenu C → écoute 5s → "Précédent"
       → Retour Contenu B à 2min30 (car >10s)
    4. Sur Contenu B → "Précédent"
       → Retour Contenu A à 5s (position exacte)

Interface (responsabilité front) : - ❌ Pas de message UI - ✅ Progress bar revient au début ou à position exacte - ✅ Animation fluide (transition 0.3s)

Justification : - UX intuitive : comportement standard Spotify/YouTube - Pas de frustration : si début, vraiment revenir en arrière - Simplicité : règle unique (seuil 10s)

### 5.3 Interactions au volant : Like automatique et engagement

> ⚠️ Architecture Decision Record : Voir ADR-010 pour les détails techniques complets

Décision : Like automatique basé sur le temps d'écoute

Problème technique identifié : - iOS et Android ne supportent pas nativement les appuis longs ou doubles-appuis sur les commandes média - Les commandes physiques au volant varient selon les véhicules (pas de bouton "Pause" dédié sur beaucoup de modèles) - Système de double-appui/appui long = non-intuitif et risques sécurité (regarder écran pour feedback)

#### Commandes au volant simplifiées

Actions disponibles (100% compatibles tous véhicules) :

Commande physique | Action RoadWave
--- | ---
Suivant | Passer au contenu suivant
Précédent | Revenir au contenu précédent (règle 10s, voir section 5.2)
Play/Pause | Pause/reprise lecture (fade out 0.3s)

Aucune action complexe au volant → Sécurité routière maximale.

#### Like automatique implicite

Principe : Le système détecte automatiquement l'intérêt utilisateur selon le temps d'écoute.

Règles d'attribution :

Durée écoutée | Action automatique | Points jauge | Justification
--- | --- | --- | ---
≥ 80% du contenu | Like renforcé | +2.0 | Écoute quasi-complète = fort intérêt
30-79% du contenu | Like standard | +1.0 | Écoute significative = intérêt
< 30% du contenu | Pas de like | 0 | Écoute trop courte
Skip après <10s | Signal négatif | -0.5 | Désintérêt marqué

Exemples concrets :

    Contenu de 3 minutes (180s) :
    - Écoute 2min30 (83%) → Like renforcé (+2 points)
    - Écoute 1min15 (42%) → Like standard (+1 point)
    - Écoute 30s (17%) puis skip → Pas de like
    - Skip après 5s → Signal négatif (-0.5 point)

    Contenu de 15 minutes (900s) :
    - Écoute 13min (87%) → Like renforcé (+2 points)
    - Écoute 6min (40%) → Like standard (+1 point)

#### Actions complémentaires (app à l'arrêt)

Interface mobile (véhicule arrêté uniquement) :

Action | Moyen | Effet
--- | --- | ---
Like explicite | Bouton cœur | +2 points jauge (même si déjà liké auto)
Unlike | Re-clic cœur (toggle) | -2 points jauge
Abonnement | Bouton "S'abonner" profil créateur | +5 points toutes jauges tags créateur
Désabonnement | Bouton "Se désabonner" | -5 points
Signalement | Menu contextuel "⋮" | Ouverture flux modération

Feedback visuel : - Like automatique : Badge discret "♥ Ajouté à vos favoris" (2s, bas de l'écran) - Like explicite : Animation cœur rouge + vibration courte - Abonnement : Animation étoile dorée + badge "Abonné ✓"

#### Commandes vocales (optionnel, si CarPlay/Android Auto)

Disponible uniquement avec : - Apple CarPlay (Siri) - Android Auto (Google Assistant) - ~30-40% du parc automobile EU (2026)

Exemples de commandes :

    "Hey Siri, like ce podcast"
    "OK Google, abonne-moi à ce créateur"
    "Hey Siri, passe au contenu suivant"
    "OK Google, signale ce contenu"

Implémentation : Intents iOS/Android personnalisés (Sprint 5, post-MVP)

#### Gestion impacts jauges (algorithme)

Like automatique : - Like renforcé (≥80%) → +2% jauges de tous les tags du contenu - Like standard (30-79%) → +1% jauges des tags du contenu - Signal négatif (skip <10s) → -0.5% jauges des tags du contenu

Actions explicites : - Like manuel → +2% jauges (cumulable avec like auto) - Unlike → -2% jauges - Abonnement → +5% toutes jauges tags créateur - Désabonnement → -5% toutes jauges

Persistance : - Événements stockés en base (table `listen_events`) - Mise à jour jauges : immédiate (Redis) + async batch (PostgreSQL)

#### Implémentation technique

Backend (Go) :

    type ListenEvent struct {
        UserID       string
        ContentID    string
        StartedAt    time.Time
        StoppedAt    time.Time
        Duration     int     // secondes écoutées
        ContentTotal int     // durée totale contenu
        Percentage   float64 // duration / contentTotal * 100
        Action       string  // "completed", "skipped", "paused"
    }

    func ProcessListenEvent(event ListenEvent) {
        percentage := event.Percentage

        // Signal négatif fort
        if event.Action == "skipped" && event.Duration < 10 {
            UpdateJauges(event.UserID, event.ContentID, -0.5)
            return
        }

        // Like automatique
        if percentage >= 80 {
            AutoLike(event.UserID, event.ContentID, 2.0) // Renforcé
        } else if percentage >= 30 {
            AutoLike(event.UserID, event.ContentID, 1.0) // Standard
        }
        // < 30% : pas de like
    }

Mobile (iOS/Android) :

    // iOS - Tracking écoute
    class AudioPlayerManager {
        var startTime: Date?
        let contentDuration: TimeInterval

        func onPlay() {
            startTime = Date()
        }

        func onStop(action: String) { // "completed" | "skipped" | "paused"
            guard let start = startTime else { return }
            let duration = Date().timeIntervalSince(start)
            let percentage = (duration / contentDuration) * 100

            // API call
            API.track(ListenEvent(
                contentId: currentContentId,
                duration: Int(duration),
                percentage: percentage,
                action: action
            ))
        }
    }

#### Justification

Avantages : - ✅ Sécurité routière maximale : aucune action complexe au volant - ✅ UX intuitive : comportement standard industrie (Spotify, YouTube Music, Deezer) - ✅ Compatibilité 100% : fonctionne sur tous véhicules, tous OS - ✅ Engagement amélioré : tous les contenus écoutés génèrent des signaux - ✅ Algorithme plus précis : données granulaires (30%, 50%, 80%, 100%) - ✅ Simplicité développement : pas de workarounds complexes iOS/Android

Inconvénients mitigés : - ⚠️ Pas de like explicite en conduite → Mitigation : like automatique + vocal (CarPlay/Android Auto) - ⚠️ Pas d'abonnement en conduite → Mitigation : liste "Créateurs à découvrir" dans app - ⚠️ Like automatique peut surprendre → Mitigation : onboarding clair + unlike possible

#### Communication utilisateurs (onboarding)

Écran onboarding 1 :

    🚗 Conduite sécurisée

    RoadWave détecte automatiquement vos goûts
    selon vos écoutes.

    Plus vous écoutez longtemps, plus
    l'algorithme s'améliore !

    [Suivant]

Écran onboarding 2 :

    ❤️ Likes automatiques

    Pas besoin de liker manuellement :
    si vous écoutez >50% d'un contenu,
    on comprend que vous aimez !

    [Suivant]

Écran onboarding 3 :

    ⏸️ Commandes simples

    Utilisez les boutons au volant :
    • Suivant → Prochain contenu
    • Précédent → Contenu d'avant
    • Pause → Mettre en pause

    [Commencer]

### 5.4 Lecture en boucle et enchaînement

Décision : Passage automatique après 2s + insertion pub paramétrable

Fin de contenu : 1. Audio termine → Timer 2 secondes démarre 2. UI overlay : "Contenu suivant dans 2s..." + barre décompte 3. Possibilité annuler : bouton "Rester sur ce contenu" (optionnel) 4. Timer atteint 0 → passage automatique au contenu suivant

Délai selon contexte :

Mode | Délai | Justification
--- | --- | ---
Standard | 2 secondes | Temps réaction confortable
Mode Kids | 1 seconde | Attention courte enfants
Live | 0 seconde | Enchaînement immédiat

Insertion publicité : - Pub s'insère pendant le délai de 2s (transition naturelle) - Fréquence : paramétrable admin (défaut : 1 pub / 5 contenus) - Message : "Publicité (15s)" puis lecture pub - ⚠️ Jamais d'interruption d'un contenu en cours

Publicité skippable : - Durée minimale visionnage : paramétrable (défaut : 5 secondes) - Bouton "Passer" apparaît après délai - Métriques engagement : taux skip, durée écoute moyenne - Like et abonnement autorisés sur pub (engagement créateur pub)

Si aucun contenu disponible : 1. Message : "Aucun contenu disponible dans cette zone" 2. Proposition : "Élargir la zone de recherche ?" (bouton) 3. Si accepté → relance algo avec rayon +50km 4. Sinon → lecture en pause, attente action utilisateur

Gestion erreurs : - Échec chargement contenu suivant → retry 3× avec backoff exponentiel - Si 3 échecs → message "Connexion instable, basculement mode offline" - Mode offline → lecture contenus téléchargés uniquement

Justification : - Fluidité : enchaînement naturel sans action utilisateur - Contrôle : possibilité annuler pendant délai - Paramétrabilité pub : évite frustration excès publicité - Engagement pub : like/abonnement autorisé = monétisation créateurs pub

## Récapitulatif Section 5

## 6. Publicités

### 6.1 Système de campagnes publicitaires

Décision : Interface self-service avec maîtrise budget et métriques détaillées

Fonctionnalités publicitaire :

#### Création de campagne

Paramètres configurables :

Paramètre | Options | Justification
--- | --- | ---
Budget total | Montant libre (min 50€) | Maîtrise coût total
Durée campagne | Date début/fin + étalement | Ex: 300€ sur 2 semaines
Ciblage géographique | Point GPS / Ville / Département / Région / National | Précision selon besoin
Ciblage horaire | Plages horaires (ex: 7h-9h, 17h-19h) | Optimisation trajet domicile-travail
Centres d'intérêt | Tags (ex: Automobile, Voyage) | Ciblage thématique
Tranche d'âge | Tout public / 13+ / 16+ / 18+ | Respect classifications

Étalement budget :

    Exemple campagne :
    - Budget : 300€
    - Durée : 14 jours
    - Zone : Département du Var
    - Horaires : 7h-9h + 17h-19h (rush)

    Calcul automatique :
    → Budget/jour = 300€ / 14 = 21.43€/jour
    → Diffusions/jour estimées : ~430 (0.05€/écoute)
    → Alerte si budget épuisé avant fin (réajustement possible)

Mode de paiement : - ✅ Prépaiement obligatoire (évite impayés) - ✅ Carte bancaire uniquement (Mangopay) - ✅ Recharge automatique optionnelle (si budget <10%)

#### Validation et modération

Processus : 1. Publicitaire upload audio pub (formats : MP3, AAC) 2. Validation manuelle obligatoire (modérateur RoadWave) - Délai : 24-48h ouvrées - Critères : respect réglementation, qualité audio, classification correcte 3. Si accepté → campagne démarre à la date choisie 4. Si refusé → email avec raison + remboursement automatique

Contenus interdits en pub : - ❌ Alcool, tabac (réglementation française) - ❌ Jeux d'argent - ❌ Contenu politique (pendant campagnes électorales) - ❌ Contenu sexuel ou violence - ✅ Tous commerces/services légaux

#### Dashboard métriques engagement

Indicateurs temps réel :

Métrique | Description | Utilité
--- | --- | ---
Impressions | Nombre de diffusions | Volume exposition
Écoutes complètes | Pub écoutée >80% | Engagement réel
Taux de skip | % skip après délai min | Qualité contenu
Durée moyenne écoute | Secondes écoutées | Rétention attention
Likes | Nombre de likes | Appréciation contenu
Abonnements | Abonnements au créateur pub | Conversion forte
Coût par écoute | Budget / écoutes complètes | ROI campagne
Répartition géographique | Heatmap diffusions | Validation ciblage
Répartition horaire | Graphique par heure | Optimisation horaires

Métriques engagement avancées : - Taux complétion par tranche d'âge : identifier audience réceptive - Carte de chaleur GPS : visualiser zones forte écoute - Comparatif campagnes : A/B testing créatifs publicitaires

Export données : - ✅ CSV/Excel pour analyse externe - ✅ Graphiques interactifs (Chart.js) - ✅ Rapport PDF automatique fin de campagne

#### Gestion budget et alertes

Suivi temps réel : - Dashboard : Budget restant, % consommé, jours restants - Projection : "À ce rythme, budget épuisé dans X jours" - Alerte email/push si : - Budget consommé à 80% - Budget consommé à 90% - Budget épuisé - Campagne terminée (rapport final)

Ajustements en cours : - ✅ Pause campagne (budget conservé) - ✅ Prolonger campagne (recharge budget) - ✅ Modifier ciblage horaire/géo (si <50% budget consommé) - ❌ Modifier audio (nécessite nouvelle validation)

#### Système d'enchères (post-MVP)

Optionnel future : - Enchère au CPM (coût pour 1000 impressions) - Priorité selon prix : pub prix élevé → diffusion privilégiée - Floor price : 2€ CPM minimum - Évite surcharge pub : max 1 pub / 5 contenus stricte

Justification décision MVP : - Tarif fixe simple : 0.05€/écoute complète - Pas de complexité enchères immédiatement - Scalable : passage enchères ultérieur si demande forte

### 6.2 Insertion et fréquence

Décision : Paramétrable admin + respect expérience utilisateur

Fréquence d'insertion : - Défaut : 1 pub / 5 contenus (utilisateurs gratuits) - Paramétrable admin : curseur 1/3 à 1/10 - Utilisateurs Premium : 0 pub (modèle sans publicité)

Règles strictes : - ⚠️ Jamais d'interruption contenu en cours - Pub s'insère uniquement entre deux contenus (pendant délai 2s) - Rotation : même pub max 3 fois/jour par utilisateur (évite saturation) - Limite : max 6 pubs/heure par utilisateur (évite spam)

Ciblage intelligent : - Géolocalisation prioritaire (point GPS > ville > département > région > national) - Centres d'intérêt secondaires (tags utilisateur) - Horaire (campagne 7h-9h → diffusion uniquement pendant plage)

Volume audio normalisé : - Pub normalisée à -14 LUFS (standard broadcast) - Évite effet "pub trop forte" (frustration utilisateur) - Validation automatique via FFmpeg lors encodage

### 6.3 Caractéristiques publicités

Durée : - Minimum : 10 secondes - Maximum : 60 secondes - Recommandé : 15-30 secondes (sweet spot engagement)

Skippable : - Délai minimum obligatoire : 5 secondes (paramétrable admin : 3-10s) - Bouton "Passer la publicité" apparaît après délai - Durée minimale comptabilisée pour facturation

Facturation : - Écoute complète (>80%) : 0.05€ facturé publicitaire - Skip après délai min : 0.02€ (exposition partielle) - Skip immédiat (<5s) : 0€ (pas d'engagement)

Justification modèle tarif : - Incitatif qualité : pub engageante = coût réduit - Équitable : publicitaire paie pour attention réelle - Transparent : dashboard montre écoutes complètes vs skips

## Récapitulatif Section 6

## 7. Radio live

### 7.1 Démarrage d'un live

Décision : Buffer 15s + notification abonnés + limite 8h

Processus de démarrage :

1. Créateur appuie "Démarrer live" dans l'app
2. Vérification pré-live :
3. Connexion ≥1 Mbps upload (warning si insuffisant)
4. Micro autorisé
5. Zone diffusion déjà définie (ville, département, région, national)
6. Buffer initial 15 secondes avant diffusion publique
7. Créateur parle pendant 15s → accumulation buffer serveur
8. Message créateur : "Live démarre dans 15s... Testez votre micro"
9. Permet vérifier qualité audio avant diffusion
10. Après 15s → Live public, auditeurs peuvent rejoindre

Notification abonnés : - ✅ Push notification immédiate à tous les abonnés dans la zone géographique - Message : "🔴 [Nom créateur] est en direct : [Titre live]" - Tap notification → ouverture app + lecture live immédiate - Filtrage géographique : si abonné hors zone, pas de notif (évite frustration)

Limite de durée : - Maximum 8 heures par session live - Warning créateur à 7h30 : "Votre live se terminera dans 30 min" - Si besoin continuer → arrêt + redémarrage nouveau live (évite abus ressources serveur)

Métadonnées obligatoires :

Champ | Format | Validation
--- | --- | ---
Titre | 5-100 caractères | Ex: "Discussion politique en direct"
Tags | 1-3 centres d'intérêt | Sélection liste prédéfinie
Classification âge | Enum | Tout public / 13+ / 16+ / 18+
Zone diffusion | Geo | Ville / Département / Région / National

Contenus interdits en live :

Type | Description | Sanction
--- | --- | ---
Concert/spectacle | Diffusion concert en direct depuis la salle | Strike 2 immédiat + ban temporaire
Événement sportif payant | Match, compétition avec droits TV | Strike 2 immédiat
Œuvre protégée | Film, série, musique en fond sans droits | Strike 1 + suppression live
Contenu violent | Agression, violence physique | Ban immédiat
Contenu illégal | Apologie terrorisme, pédopornographie | Ban définitif + signalement autorités

Exemple usecase interdit :

    ❌ Utilisateur dans salle de concert diffuse live performance
    → Violation droits d'auteur + droits de diffusion
    → Détection : modération réactive (signalements) + IA audio fingerprint
    → Sanction : Strike 2 (suspension 7 jours) + suppression live + suppression replay

Détection violations : - Signalement utilisateurs : bouton "Signaler" accessible pendant live - IA audio fingerprint : détection musique protégée en arrière-plan (post-MVP) - Modération réactive : modérateurs peuvent écouter lives signalés en temps réel - Coupure immédiate : modérateur peut arrêter live si contenu illégal évident

Justification : - Buffer 15s : équilibre entre test qualité et friction minimale - Notification abonnés : engagement maximal, valeur ajoutée live - 8h max : couvre 99% cas usage (podcasts longs, émissions radio) sans abus - Interdictions strictes : protection juridique plateforme (DSA EU, droits d'auteur) - Coût : WebRTC ingestion + HLS distribution (réutilise infra existante)

### 7.2 Arrêt du live

Décision : Compte à rebours 5s + tolérance déconnexion 60s + enregistrement auto

Fin manuelle créateur :

1. Créateur appuie "Arrêter live"
2. Compte à rebours 5 secondes affiché
3. Message audio : "Ce live se termine dans 5... 4... 3... 2... 1"
4. Permet au créateur de faire un outro propre
5. Annulable pendant décompte (bouton "Annuler")
6. Timer atteint 0 → arrêt diffusion
7. Traitement post-live automatique démarre (voir ci-dessous)

Fin automatique si déconnexion :

Durée coupure | Comportement
--- | ---
<60 secondes | Message auditeurs : "Connexion créateur perdue, reconnexion en cours..."
≥60 secondes | Arrêt automatique live + message : "Le live est terminé suite à une coupure de connexion"

Enregistrement automatique :

✅ Obligatoire et automatique (valeur ajoutée énorme)

Processus : 1. Pendant live : enregistrement continu serveur (format Opus raw) 2. Fin live → job asynchrone (worker Go + FFmpeg) : - Conversion MP3 256 kbps (qualité optimale) - Génération segments HLS (comme contenu classique) - Normalisation volume -14 LUFS - Détection silences prolongés (nettoyage) 3. Publication automatique du replay : - Titre : "[REPLAY] [Titre live original]" - Même zone diffusion, tags, classification - Disponible sous 5-10 minutes après fin live - Type géo : automatiquement "Géo-neutre" (replay = contenu pérenne)

Options créateur :

Option | Défaut | Description
--- | --- | ---
Publier replay automatiquement | ✅ OUI | Désactivable avant démarrage live
Supprimer replay après coup | ✅ Possible | Suppression standard contenu
Modifier replay | ❌ Non | Intégrité enregistrement

Conservation fichier source : - Opus raw conservé 7 jours après fin live (backup) - Suppression automatique après 7j (économie stockage) - Si replay supprimé par créateur → fichier raw supprimé immédiatement

Justification : - Compte à rebours 5s : outro propre, pas de coupure brutale - Tolérance 60s : évite arrêts intempestifs (tunnel, changement cellule) - Enregistrement auto : valorisation contenu éphémère, génération contenu pérenne - MP3 256 kbps : qualité optimale pour replay (vs 48 kbps live) - Coût : stockage minimal (Opus → MP3 1× par live, puis suppression raw après 7j)

### 7.3 Comportement auditeur

Décision : Buffer 15s + continuation hors zone + reconnexion au live actuel + écoute passive uniquement

Buffer de synchronisation :

- 15 secondes entre créateur et auditeurs
- Raisons :
- Stabilité réseau mobile (3G/4G fluctuant)
- Synchronisation approximative acceptable (pas besoin temps réel strict)
- Permet buffering anticiper coupures courtes (tunnels)

Comparaison buffers :

Buffer | Avantages | Inconvénients | Décision
--- | --- | --- | ---
5s | Quasi temps réel | Instable 3G, coupures fréquentes | ❌
10s | Bon compromis | Légèrement juste pour 3G | ❌
15s | Stabilité optimale 3G/4G | Léger décalage acceptable | ✅
20s+ | Très stable | Décalage trop perceptible | ❌

Zone géographique pendant live :

- ✅ Continuation si sortie de zone
- Scénario : auditeur écoute live régional → sort du département → live continue
- Raisons :
- Pas de coupure brutale (mauvaise UX)
- Écoute engagée = terminer naturellement
- Après fin live → algo normal (pas de contenus hors zone)

Reconnexion après coupure réseau :

Durée coupure | Comportement
--- | ---
<90 secondes | Reprend au live actuel (pas au buffer ancien) + saut temporel transparent
≥90 secondes | Message : "Live en cours perdu, passage au contenu suivant" + algo propose contenu normal

Interactions disponibles :

Décision ferme : ❌ Aucun chat en direct, ni maintenant ni dans le futur

Raisons : - Sécurité routière : pas de distraction en voiture (focus UX) - Harcèlement : évite contenu haineux, insultes, trolling - Modération : pas de coût modération temps réel (impossible à scale) - Simplicité : écoute passive = expérience uniforme

Actions autorisées pendant live :

Action | Disponible | Effet
--- | --- | ---
Like | ✅ | Bouton cœur interface mobile (véhicule arrêté)
Abonnement créateur | ✅ | Bouton profil créateur (interface mobile)
Skip | ✅ | Passe au contenu suivant, sort du live
Précédent | ❌ | Pas de sens sur live (flux temps réel)
Chat | ❌ | Jamais implémenté (décision définitive)
Réactions emoji | ❌ | Jamais implémenté (décision définitive)

Messages utilisateur : - "💬 Les discussions ne sont pas disponibles sur RoadWave pour garantir votre sécurité en voiture et éviter le harcèlement."

Justification décision définitive : - UX cohérente : RoadWave = écoute en conduisant, pas réseau social interactif - Bien-être : évite toxicité, harcèlement, haine (fléau réseaux sociaux) - Juridique : pas de risque contentieux modération chat (DSA EU) - Coût : 0€ infra chat, 0€ modération temps réel - Différenciation : positionnement "audio safe" vs plateformes toxiques

### 7.4 Architecture technique

Stack :

    Créateur (App mobile)
        ↓ WebRTC (OPUS 48 kbps)
    Serveur Ingestion (Go + Pion WebRTC)
        ↓ Conversion temps réel
    Serveur HLS (segments .ts)
        ↓ CDN (Bunny)
    Auditeurs (App mobile, HLS natif)

Flux détaillé : 1. Créateur → WebRTC OPUS 48 kbps vers serveur Go 2. Serveur Go → Conversion temps réel OPUS → segments HLS (.m3u8 + .ts) 3. Bunny CDN → Distribution HLS avec cache 4. Auditeurs → Lecture HLS native iOS/Android (buffer 15s) 5. Enregistrement parallèle → Opus raw stocké temporairement 6. Post-live → Job async : Opus → MP3 256 kbps → Publication replay

Dépendances : - ✅ Pion WebRTC (Go library, open source, MIT license) - ✅ FFmpeg (conversion audio, LGPL/GPL) - ✅ Bunny CDN (distribution HLS, pas Google/Cloudflare) - ✅ PostgreSQL + Redis (métadonnées live + cache)

Avantages : - ✅ Pas de dépendance Google/Facebook/Cloudflare (souveraineté) - ✅ WebRTC standard ouvert (Pion = lib Go pure) - ✅ Réutilise infra HLS existante (pas de doublon) - ✅ CDN cache les segments (coût réduit) - ✅ Scalable horizontalement (workers Go)

Coût estimé :

Phase | Utilisateurs | Infra live | Coût/mois
--- | --- | --- | ---
MVP | 0-100K | 1 instance Go (ingestion 100 lives simultanés) | +50€ (serveur) + bande passante CDN
Growth | 100K-1M | 3-5 instances Go (500 lives simultanés) | +200€ + bande passante
Scale | 1M-10M | Kubernetes auto-scale (2000+ lives) | +1K€ + bande passante

Bande passante : - Live : 48 kbps × nb_auditeurs (via CDN, cache segments) - Exemple : 100 auditeurs = 4.8 Mbps = ~2 Go/heure via CDN - Coût Bunny : ~0.01€/GB = 0.02€/heure pour 100 auditeurs

## Récapitulatif Section 7

## 8. Abonnements et notifications

### 8.1 Impact sur l'algorithme

Décision : Boost +30% au score + reste dans le mix

Boost de score abonnements : - +30% au score final pour contenus d'un créateur suivi - Application : multiplicateur sur le score calculé

    score_final_avec_boost = score_final × 1.3

Reste dans le mix : - ❌ Pas de priorité absolue (pas de file dédiée abonnements) - ✅ Contenu suivi entre en compétition avec autres contenus - ✅ Si créateur suivi publie contenu faible engagement → peut être battu par contenu viral non-suivi

Exemple concret :

    Utilisateur à Paris, 2 contenus disponibles :

    Contenu A (créateur NON suivi) :
    - Score géo : 0.9 (très proche)
    - Score intérêts : 0.8
    - Score engagement : 0.7
    → Score final : 0.80

    Contenu B (créateur suivi) :
    - Score géo : 0.5 (moyennement proche)
    - Score intérêts : 0.6
    - Score engagement : 0.5
    → Score final : 0.53
    → Score avec boost : 0.53 × 1.3 = 0.69

    → Contenu A proposé en premier (0.80 > 0.69)

Cas où abonnement fait la différence :

    Contenu A (non suivi) : score 0.70
    Contenu B (suivi) : score 0.60 → avec boost 0.78
    → Contenu B proposé (boost fait pencher la balance)

Justification : - Équilibre : valorise abonnements sans enfermer utilisateur - Découverte : contenus viraux/locaux peuvent toujours émerger - Prévisible : boost fixe, pas de logique opaque - Coût 0 : multiplicateur simple dans l'algo

### 8.2 Notifications contextuelles

Décision : Push adapté selon contexte (voiture vs à pied) + limite 10/jour

Détection contexte utilisateur :

Contexte | Détection | Comportement
--- | --- | ---
En voiture | Vitesse GPS >10 km/h | Notifications silencieuses (in-app uniquement) + commandes volant
À pied | Vitesse GPS <5 km/h | Notifications push actives + interface tactile/vocale

Notifications activées :

#### En voiture (mode conduite)

Événement | Notification | Comportement
--- | --- | ---
Nouveau contenu créateur suivi | In-app uniquement | Badge compteur, pas de push (sécurité)
Live créateur suivi | In-app uniquement | Badge compteur, pas de push
Point d'intérêt proche | Audio notification | Bip + annonce vocale : "Audio-guide disponible"

#### À pied (mode piéton)

Événement | Notification | Comportement
--- | --- | ---
Nouveau contenu créateur suivi | ✅ Push | Si utilisateur dans zone géo du contenu
Live créateur suivi | ✅ Push | Si utilisateur dans zone géo
Audio-guide disponible | ✅ Push | "📍 Audio-guide disponible : [Lieu]"
Séquence suivante suggérée | Audio notification | Annonce vocale : "Pièce suivante disponible"

Format notifications :

Nouveau contenu :

    🎧 [Nom créateur] a publié : "[Titre contenu]"
    Tap pour écouter

Live en direct :

    🔴 [Nom créateur] est en direct : "[Titre live]"
    Tap pour rejoindre

Audio-guide à pied :

    📍 Audio-guide disponible : [Nom du lieu]
    Choisissez parmi 3 guides pour [Musée du Louvre]
    Tap pour explorer

Filtrage géographique : - Si contenu/live hors zone utilisateur → pas de notification - Évite frustration : "notification pour contenu que je ne peux pas écouter" - Exception : contenu national → notifie tous les abonnés

Fréquence maximale : - Maximum 10 notifications push/jour par utilisateur (tous types confondus) - Si dépassement : notifications regroupées - Message groupé : "🎧 3 nouveaux contenus de créateurs suivis"

Plages horaires : - Mode silencieux : 22h-8h (pas de push, sauf live) - Paramétrable utilisateur (désactivation totale possible) - Option "Notifications importantes uniquement" (lives uniquement)

Gestion préférences :

Préférence | Défaut | Description
--- | --- | ---
Nouveaux contenus | ✅ Activé | Push à chaque nouveau contenu (à pied uniquement)
Lives | ✅ Activé | Push au démarrage live (à pied uniquement)
Audio-guides proximité | ✅ Activé | Push quand audio-guide détecté à <100m
Mode silencieux | ✅ Activé (22h-8h) | Pas de push nocturne
Limite quotidienne | 10 | Modifiable 5-20

Justification : - Sécurité routière : pas de push en conduite (distraction) - Engagement piéton : push actifs pour audio-guides (valeur ajoutée tourisme) - Pas de spam : limite 10/jour + mode silencieux - Filtrage géo : pertinence maximale (pas de notif inutiles) - Coût : Firebase Cloud Messaging (gratuit jusqu'à volume élevé)

### 8.3 Mode Audio-guide (piéton)

Décision : Navigation manuelle multiséquence + choix parmi plusieurs guides

Fonctionnement :

#### Détection et proposition

1. Utilisateur à pied (<5 km/h) passe à <100m d'un lieu avec audio-guides
2. Notification push : "📍 Audio-guide disponible : [Musée du Louvre]"
3. Tap notification → Page de sélection audio-guides

#### Page de sélection

Affichage :

    📍 Musée du Louvre

    Choisissez votre guide :

    ┌─────────────────────────────────┐
    │ 🎨 Visite complète (45 min)    │
    │ Par [Créateur A] • 12 séquences│
    │ ⭐ 4.8 • 1.2K écoutes           │
    └─────────────────────────────────┘

    ┌─────────────────────────────────┐
    │ 🏛️ Œuvres majeures (20 min)    │
    │ Par [Créateur B] • 5 séquences │
    │ ⭐ 4.9 • 3.5K écoutes           │
    └─────────────────────────────────┘

    ┌─────────────────────────────────┐
    │ 👶 Visite famille (30 min)     │
    │ Par [Créateur C] • 8 séquences │
    │ ⭐ 4.7 • 850 écoutes            │
    └─────────────────────────────────┘

#### Interface audio-guide

Après sélection :

    🎨 Visite complète • Musée du Louvre

    Piste actuelle : 2/12
    "La Joconde - Histoire et mystères"
    [████████────────────] 3:24 / 6:50

    Liste des séquences :
    ✅ 1. Introduction et architecture
    ▶️ 2. La Joconde - Histoire et mystères
    ⏸️ 3. Vénus de Milo
    ⏸️ 4. Victoire de Samothrace
    ⏸️ 5. Peintures Renaissance
    ...
    ⏸️ 12. Conclusion et boutique

Navigation :

Action | Geste | Effet
--- | --- | ---
Séquence suivante | Tap "Suivant" ou commande vocale "Suivant" | Passe à séquence N+1
Séquence précédente | Tap "Précédent" ou commande vocale "Précédent" | Revient à séquence N-1
Saut direct | Tap séquence dans liste | Lecture séquence choisie
Pause | Tap bouton pause | Met en pause, reprise position exacte
Quitter | Tap "×" | Sauvegarde progression, sortie guide

Guidage vocal automatique : - Entre 2 séquences : "Vous avez terminé la séquence 2. Dirigez-vous vers la Vénus de Milo pour la séquence 3." - Si utilisateur s'éloigne (>50m de la prochaine pièce) : "Vous vous éloignez de la prochaine étape. Consultez le plan."

Sauvegarde progression : - Position dans guide sauvegardée automatiquement - Retour ultérieur : "Reprendre à la séquence 5 ?" ou "Recommencer depuis le début" - Historique : guide marqué "Terminé" si toutes séquences écoutées

Création audio-guide multiséquence :

Processus créateur : 1. Créateur upload plusieurs fichiers audio (1 par séquence) 2. Numérote les séquences : "Séquence 1", "Séquence 2", etc. 3. Titre chaque séquence : "Introduction", "La Joconde", etc. 4. Définit point GPS unique pour tout le guide (centre du lieu) 5. Métadonnées : durée totale calculée automatiquement

Format stockage :

    {
      "guide_id": "abc123",
      "title": "Visite complète Musée du Louvre",
      "location": {"lat": 48.8606, "lon": 2.3376, "radius": 200},
      "sequences": [
        {
          "sequence_number": 1,
          "title": "Introduction et architecture",
          "audio_url": "https://cdn.../seq1.mp3",
          "duration_seconds": 180
        },
        {
          "sequence_number": 2,
          "title": "La Joconde - Histoire et mystères",
          "audio_url": "https://cdn.../seq2.mp3",
          "duration_seconds": 410
        },
        ...
      ],
      "total_duration_seconds": 2700,
      "creator_id": "creator_xyz"
    }

Justification : - UX piéton : navigation tactile adaptée (pas de commandes volant) - Autonomie : utilisateur maître de son rythme (pas d'enchaînement forcé) - Choix : plusieurs guides = diversité styles (famille, expert, rapide) - Engagement : sauvegarde progression = incitation terminer - Coût : réutilise infra contenu standard (juste métadonnées séquences)

### 8.4 Limites et désabonnement

Décision : 200 abonnements max + désabonnement -5% jauges

Nombre maximum d'abonnements : - 200 créateurs maximum par utilisateur - Raisons : - Évite spam : au-delà de 200, notifications ingérables - Usage réaliste : 200 créateurs = déjà énorme (vs 100-150 sur YouTube/Twitter) - Performance : requêtes SQL optimisées (index sur 200 max)

Si limite atteinte : - Message : "Vous suivez déjà 200 créateurs. Désabonnez-vous d'un créateur pour en suivre un nouveau." - Liste triable : par date abonnement, nb contenus écoutés, dernière activité - Suggestion : "Vous n'avez pas écouté [Créateur X] depuis 6 mois, le désabonner ?"

Abonnement initial : - Impact : +5% toutes jauges tags du créateur (défini en ADR-010) - Action : Bouton "S'abonner" dans profil créateur (interface mobile) - Immédiat à l'action

Désabonnement : - Impact : -5% toutes jauges tags du créateur (symétrique) - Action : Bouton "Se désabonner" dans profil créateur - Immédiat à l'action - Pas de confirmation (action réversible)

Exemple :

    Créateur tague ses contenus : Automobile, Voyage

    Abonnement :
    → Jauge Automobile : 60% → 65% (+5%)
    → Jauge Voyage : 55% → 60% (+5%)

    3 mois plus tard, désabonnement :
    → Jauge Automobile : 65% → 60% (-5%)
    → Jauge Voyage : 60% → 55% (-5%)

Gestion multi-tags : - Si créateur a 3 tags → +5% sur chacun des 3 tags - Logique : abonnement = signal fort d'affinité à TOUS les sujets du créateur

Abonnements réciproques : - ❌ Pas d'abonnement mutuel visible - Créateur ne voit pas qui est abonné (privacy) - Créateur voit uniquement : nombre total abonnés (métrique globale)

Justification : - Limite 200 : équilibre entre liberté et gestion spam - Symétrie +5%/-5% : cohérence mathématique, prévisibilité - Privacy : pas de liste publique abonnés (évite stalking) - Coût : table abonnements PostgreSQL standard

## Récapitulatif Section 8

## 9. Monétisation créateurs

### 9.1 Pourboires

Décision : ❌ Fonctionnalité abandonnée pour le MVP

Raisons : - Complexité juridique (collecte pour compte de tiers, TVA variable) - Frais de transaction élevés sur petits montants (Mangopay ~1.8% + 0.18€) - UX additionnelle à développer (wallet, transactions, confirmations) - Charge comptable importante pour la plateforme

Post-MVP : Possible réintégration avec crypto (Bitcoin/Lightning Network) si législation UE l'autorise clairement (régulation MiCA en cours).

### 9.2 Conditions d'activation de la monétisation

Décision : 5 critères cumulatifs obligatoires

Critère | Seuil | Justification
--- | --- | ---
Ancienneté | Compte créé depuis ≥ 3 mois | Anti-fraude : temps de détecter comportements suspects
Popularité | ≥ 500 abonnés | Garantit audience réelle et engagée
Engagement | ≥ 10 000 écoutes complètes cumulées | Créateurs produisant du contenu de qualité
Fiabilité | Aucun strike actif, 0 contenu modéré dans les 6 derniers mois | Historique propre requis
Régularité | ≥ 5 contenus publiés dans les 90 derniers jours | Activité constante

Vérification : Automatique via requêtes SQL lors de la demande d'activation

Affichage : - Bouton "Demander la monétisation" dans profil créateur - Si critères non remplis → affichage progression vers objectifs - Si critères remplis → redirection vers KYC Mangopay

Justification : - Anti-fraude : Le délai de 3 mois permet de détecter les comptes suspects - Qualité : Seuls les créateurs sérieux avec audience réelle sont monétisés - Coût administratif : Réduit le nombre de comptes à gérer (KYC, comptabilité, virements) - Légitimité : Audience organique prouvée

### 9.3 KYC (Know Your Customer) et inscription

Décision : Statut juridique professionnel obligatoire

Statuts acceptés : - Auto-entrepreneur (micro-BNC pour artistes/créateurs de contenu) - SARL/SAS/SASU (sociétés)

Documents requis :

Document | Obligatoire | Format | Validité
--- | --- | --- | ---
SIRET | ✅ | 14 chiffres | Permanent
RIB professionnel | ✅ | IBAN FR | Permanent
Pièce d'identité | ✅ | CNI/Passeport | En cours de validité
Numéro TVA intracommunautaire | ⚠️ Si applicable | FR + 11 chiffres | Permanent
Kbis <3 mois | ⚠️ Si société | PDF | <3 mois

Vérification : Via Mangopay (KYC intégré + vérification bancaire)

Délai : 24-72h si documents conformes

Rejet possible si : - Documents invalides/illisibles - Identité ne correspond pas au compte RoadWave - Liste noire anti-blanchiment (vérification automatique Mangopay) - RIB non professionnel (particulier)

Base légale : - Conformité fiscale : L'État français impose déclaration revenus >1200€/an (DAS2) - Anti-blanchiment : Directive EU 2018/843 (5ème directive LCB-FT) - RGPD : Données hébergées EU via Mangopay (conforme)

Justification : - Responsabilité légale : RoadWave doit pouvoir prouver identité réelle créateurs monétisés - Automatisation : Mangopay gère tout (KYC, vérifications, conformité, e-wallets) - KYC gratuit : inclus dans l'offre Mangopay (vs 1.20€ chez Stripe) - Souveraineté EU : Mangopay est européen (France/Luxembourg), régulé ACPR

### 9.4 Sources de revenus créateurs

#### A) Publicités (utilisateurs gratuits)

Formule : 3€ / 1000 écoutes complètes (CPM créateur)

Répartition économique :

    Publicité facturée par RoadWave : 0.05€/écoute complète = 50€ CPM
    ├─ Créateur touche : 3€ (6% du CA pub)
    └─ Plateforme garde : 47€ (94%)
        ├─ CDN + infrastructure : ~10-15€
        ├─ Modération + support : ~5-10€
        ├─ Développement + R&D : ~10-15€
        └─ Marge opérationnelle : ~10-15€

Exemple concret : - 10 000 écoutes/mois → créateur touche 30€ - 50 000 écoutes/mois → créateur touche 150€ - 100 000 écoutes/mois → créateur touche 300€

Comparaison industrie : - YouTube : 3-5€/1000 vues - Spotify : 3-4€/1000 écoutes - RoadWave : 3€/1000 écoutes (aligné)

Règles comptabilisation : - ✅ Écoute complète = ≥80% du contenu écouté - ✅ Utilisateur gratuit uniquement - ❌ Écoutes Premium ne comptent pas ici (autre système) - ❌ Bots détectés exclus (rate limiting + analyse patterns)

#### B) Abonnés Premium

Formule : 70% au créateur, 30% à la plateforme

Répartition proportionnelle au temps d'écoute effectif :

    Utilisateur Premium = 4.99€/mois
    ├─ 3.49€ reversés aux créateurs (70%)
    └─ 1.50€ gardés par plateforme (30%)

    Si l'utilisateur écoute 3 créateurs ce mois :
    - Créateur A : 10h d'écoute (50%) → 1.75€
    - Créateur B : 6h d'écoute (30%) → 1.05€
    - Créateur C : 4h d'écoute (20%) → 0.70€

Calcul technique :

    -- Pour chaque utilisateur Premium
    SELECT
        creator_id,
        SUM(listen_duration_seconds) AS total_seconds,
        (SUM(listen_duration_seconds) / total_user_seconds) AS ratio,
        (4.99 * 0.70 * ratio) AS revenue_euros
    FROM premium_listens
    WHERE user_id = :user_id
      AND month = :current_month
    GROUP BY creator_id;

Comparaison industrie : - YouTube Premium : 70/30 - Spotify : 70/30 - Apple Music : 52/48 (moins avantageux) - RoadWave : 70/30 (standard)

Justification : - Standard industrie : ratio équitable éprouvé - Incitation qualité : créateurs les plus écoutés gagnent plus - Équité : pas de "winner takes all", chaque créateur écouté reçoit sa part - Marge plateforme : 30% couvre absence revenus pub sur Premium

### 9.5 Paiement des créateurs

Seuil minimum : 50€

- En dessous → solde reporté mois suivant
- Évite frais bancaires sur micro-sommes
- Standard industrie (YouTube/Twitch/Spotify = 50-100€)

Fréquence : Mensuelle

Date | Action
--- | ---
Dernier jour du mois (ex: 31 janvier) | Calcul revenus du mois via SQL
1-14 du mois suivant | Traitement contestations/fraudes éventuelles
15 du mois suivant (ex: 15 février) | Virement SEPA via Mangopay (Payout)
16-18 du mois suivant | Réception virement (1-3 jours ouvrés SEPA)

Virement via Mangopay : - SEPA pour comptes EU (gratuit, 1-3 jours) - Virement international hors EU (frais variables selon pays, rare en pratique) - E-wallets automatiques : chaque créateur possède un wallet Mangopay où ses revenus sont transférés automatiquement

Tableau de bord créateur (temps réel) :

Métrique | Description | Mise à jour
--- | --- | ---
Revenus pub | Écoutes × CPM | Temps réel
Revenus premium | Abonnés actifs × ratio écoute | Temps réel
Solde disponible | Total revenus mois en cours | Temps réel
Solde en attente | Revenus mois précédent (paiement le 15) | Figé fin de mois
Historique virements | Liste des paiements reçus | Permanent
Export comptable CSV | Données pour expert-comptable | Téléchargement

Gestion échecs virement : 1. Tentative 1 (15 du mois) → échec 2. Retry automatique J+3 3. Retry automatique J+7 4. Si 3 échecs → suspension monétisation + email créateur (RIB invalide)

### 9.6 Contenus Premium exclusifs

Décision : Créateur décide individuellement pour chaque contenu

Fonctionnement : - Toggle "Réservé Premium" lors création/édition contenu - Aucune limite imposée : créateur peut mettre 0%, 50% ou 100% en premium - Badge 👑 visible sur interface utilisateur

Comportement utilisateurs gratuits : - Contenu premium visible dans liste/algo - Tentative lecture → overlay bloquant - Message : "Ce contenu est réservé aux abonnés Premium" - CTA : "Passez Premium pour 4.99€/mois"

Comportement algorithme : - Contenus premium inclus dans recommandations - Si user gratuit → contenu skippé automatiquement (ne consomme pas de slot) - Si user premium → diffusé normalement

Métadonnées : - Champ `is_premium` (boolean) en base - Index sur ce champ pour requêtes rapides - Cache Redis : `content:{id}:premium` (TTL 1h)

Justification : - Liberté créateur : chaque créateur choisit sa stratégie (freemium, tout gratuit, tout premium) - Incitation Premium : contenu exclusif = argument fort pour s'abonner - Équité : un petit créateur peut tout mettre en premium, un gros peut tout offrir gratuitement

### 9.7 Obligations fiscales

RoadWave génère automatiquement :

Document | Fréquence | Destinataire | Base légale
--- | --- | --- | ---
Relevé mensuel PDF | Chaque mois | Créateur | Transparence
Export CSV comptable | À la demande | Créateur + expert-comptable | Facilitation déclarations
DAS2 annuel | Si >1200€/an | Impôts (DGFIP) | Obligation légale France

Créateur responsable de : - Déclarer ses revenus à l'URSSAF (cotisations sociales auto-entrepreneur ou IS/IR) - Déclarer ses revenus aux impôts (IR ou IS selon statut) - Gérer sa TVA si applicable (franchise en base jusqu'à ~37K€/an en micro-BNC) - Conserver justificatifs 10 ans (obligation légale comptable)

Mangopay transmet automatiquement : - Données aux autorités fiscales EU via DAC7 (directive 2021/514) - Justificatif de chaque virement (preuve bancaire pour comptabilité créateur)

Exemple DAS2 :

    Si créateur a touché 2500€ en 2026 :
    → RoadWave envoie DAS2 aux impôts en janvier 2027
    → Créateur reçoit copie par email
    → Créateur doit déclarer ces 2500€ dans sa déclaration annuelle

Justification : - Conformité légale : RoadWave doit déclarer revenus versés (DAS2, DAC7) - Responsabilité fiscale : Le créateur reste responsable de sa déclaration (impossible de gérer pour lui) - Automatisation : Minimise charge administrative côtés créateur et plateforme

### 9.8 Désactivation et suspension monétisation

Créateur peut : - Désactiver temporairement (vacances, pause création) - Réactiver sans refaire KYC si données à jour (<2 ans) - Solde conservé pendant désactivation

Plateforme suspend automatiquement si :

Motif | Action | Réversible
--- | --- | ---
Strike 3+ actif | Suspension immédiate | Oui, après résolution strikes
Compte bancaire invalide | Suspension après 3 échecs virement | Oui, après mise à jour RIB
Documents KYC expirés | Suspension avec préavis 30j | Oui, après renouvellement docs
Fraude détectée | Suspension immédiate + enquête | Cas par cas

Suppression définitive si : - Demande du créateur (solde versé sous 30 jours) - Inactivité 24 mois + solde <50€ (purge RGPD) - Ban définitif compte (Strike 4)

Notification : - Email + in-app pour toute suspension - Raison explicite fournie - Procédure de réactivation indiquée

Justification : - Flexibilité : créateur peut faire pause sans perdre statut - Sécurité : plateforme doit pouvoir suspendre en cas problème légal/technique - RGPD : suppression auto données inactives après délai raisonnable

## Récapitulatif Section 9

## 10. Premium

### 10.1 Offre et tarification

Décision : Deux formules sans essai gratuit

Formule | Prix | Économie | Prix effectif
--- | --- | --- | ---
Mensuel | 4.99€/mois | - | 4.99€/mois
Annuel | 49.99€/an | 2 mois offerts | 4.16€/mois

❌ Pas d'essai gratuit

Raisons : - Anti-abus vacances : évite inscriptions opportunistes (essai 14j avant road trip vacances, puis annulation) - Protection revenus créateurs : les écoutes Premium rémunèrent créateurs dès jour 1 - Simplicité : pas de gestion période trial + conversion - Engagement : utilisateur qui paie dès début = plus engagé

❌ Pas de partage familial (MVP)

Raisons : - Complexité technique (gestion invitations, validation liens, limite devices) - Risque abus ("familles" de 6 inconnus) - Coût dev/support élevé pour ROI incertain - La plupart des users RoadWave sont individuels (conducteurs) - Post-MVP : Si forte demande, offre "Famille" à 9.99€/mois pour 5 comptes

Justification tarif : - Aligné marché bas : Spotify = 10.99€, YouTube Premium = 11.99€, Apple Music = 10.99€ - Prix accessible : cible conducteurs quotidiens (budget raisonnable) - Incitation annuel : 2 mois offerts = engagement long terme + réduction churn

### 10.2 Multi-devices et détection simultanée

Décision : 1 seul stream actif par compte à tout moment

Détection connexion simultanée :

    User A écoute sur iPhone
    → User A lance sur iPad
    → Détection : session active iPhone existe
    → Action : Arrêt lecture iPhone (WebSocket close)
    → Message iPhone : "Lecture interrompue : votre compte est utilisé sur un autre appareil"
    → Lecture démarre iPad

Implémentation technique :

    Redis : active_streams:{user_id} → {device_id, started_at}
    TTL : 5 minutes (refresh à chaque heartbeat)

    Heartbeat toutes les 30s depuis app :
    → Si autre device détecté : kill session actuelle
    → Si pas de heartbeat pendant 5 min : considérer session morte

Exceptions : - Contenus téléchargés (offline) ne comptent pas comme stream actif - Transition rapide device (<10s) tolérée (changement voiture → maison)

Justification : - Anti-partage compte : empêche 2 personnes d'utiliser même compte Premium - Protection revenus créateurs : 1 abonnement = 1 personne = 1 écoute - UX claire : message explicite, pas de coupure brutale

### 10.3 Contenus exclusifs Premium

Décision : Créateur décide (déjà couvert section 9.6)

Rappel règles : - Toggle "Réservé Premium" par contenu - Aucune limite de ratio gratuit/premium - Badge 👑 visible - Users gratuits : lecture bloquée avec CTA "Passez Premium"

Impact algorithme : - Contenus premium inclus dans recommandations - Si user gratuit → skip automatique (ne consomme pas slot) - Si user premium → diffusé normalement selon score

### 10.4 Avantages Premium

Inclus dans l'abonnement :

Avantage | Gratuit | Premium
--- | --- | ---
Publicités | 1/5 contenus | 0 (aucune)
Contenus exclusifs | ❌ Bloqués | ✅ Accès complet
Qualité audio | 48 kbps Opus | 64 kbps Opus
Mode offline | 50 contenus max | Illimité
Historique écoute | 100 derniers | Illimité

Qualité audio : - Gratuit : 48 kbps Opus (~20 MB/h) = très correct pour voix - Premium : 64 kbps Opus (~30 MB/h) = excellente qualité

Justification différences : - 0 pub = argument principal (confort écoute) - Qualité audio = avantage tangible audiophiles - Offline illimité = use case road trips longs - Pas d'over-engineering : pas de badges cosmétiques, fonctionnalités sociales, etc. (focus essentiel)

### 10.5 Gestion abonnement

Souscription :

Canal | Prestataire | Prix | Commission
--- | --- | --- | ---
Web (desktop/mobile) | Mangopay | 4.99€ | 1.8% + 0.18€ = 0.27€
iOS App | Apple In-App Purchase | 5.99€ | 30% (Apple)
Android App | Google Play Billing | 5.99€ | 30% (Google)

Majoration mobile (5.99€) : - Apple/Google prennent 30% de commission - RoadWave majore prix de 20% pour compenser - Incitation web : Email aux users "Abonnez-vous sur roadwave.com pour 4.99€/mois" (38% moins cher en frais !)

Renouvellement automatique : - Email rappel 7 jours avant renouvellement - Email confirmation après renouvellement réussi - Retry automatique si échec paiement (3 tentatives sur 7 jours) - Annulation automatique après 3 échecs

Annulation : - Self-service dans Settings app : "Abonnement > Annuler" - Accès Premium maintenu jusqu'à fin période payée - Pas de remboursement prorata (standard industrie) - Email confirmation annulation avec date fin d'accès

Réabonnement : - Possibilité immédiate - ❌ Pas de nouvelle période d'essai (pas d'essai du tout)

Architecture données :

    CREATE TABLE subscriptions (
        id UUID PRIMARY KEY,
        user_id UUID NOT NULL REFERENCES users(id) UNIQUE,
        mangopay_recurring_payin_id VARCHAR(255), -- Null si IAP
        mangopay_user_id VARCHAR(255),            -- Null si IAP
        apple_transaction_id VARCHAR(255),        -- Null si Mangopay
        google_purchase_token VARCHAR(255),       -- Null si Mangopay
        status VARCHAR(50) NOT NULL, -- 'active', 'cancelled', 'expired', 'past_due'
        plan VARCHAR(50) NOT NULL, -- 'monthly', 'yearly'
        current_period_start TIMESTAMP NOT NULL,
        current_period_end TIMESTAMP NOT NULL,
        cancelled_at TIMESTAMP,
        created_at TIMESTAMP NOT NULL DEFAULT NOW()
    );

Vérification Premium en temps réel :

    Cache Redis : premium:{user_id} → boolean (TTL 1h)
    Refresh via webhooks :
    - Mangopay : PAYIN_NORMAL_SUCCEEDED, PAYIN_NORMAL_FAILED
    - Apple : App Store Server Notifications
    - Google : Real-time Developer Notifications

## Récapitulatif Section 10

## 11. Mode offline

### 11.1 Téléchargement

Zone géographique : Choix manuel utilisateur

Options prédéfinies : - "Autour de moi" (rayon 50 km position actuelle) - "Ma ville" (limite administrative détectée) - "Mon département" (sélection liste) - "Ma région" (sélection liste) - Recherche manuelle : "Paris", "Lyon", "Marseille", etc.

Nombre de contenus téléchargeables :

Statut | Limite | Affichage
--- | --- | ---
Gratuit | 50 contenus max | "12/50 contenus téléchargés"
Premium | Illimité | "245 contenus (3.2 GB)"

Calcul temps disponible : - 50 contenus × 5 min moyenne = 250 min = 4h d'écoute (suffisant pour gratuits) - Premium illimité = limité uniquement par espace disque device

Connexion WiFi/Mobile :

Par défaut : WiFi uniquement

Sur données mobiles : 1. User clique "Télécharger" 2. Détection : pas de WiFi 3. Popup : "Vous n'êtes pas connecté en WiFi. Télécharger via données mobiles consommera environ X MB. Continuer ?" 4. Boutons : "Attendre WiFi" / "Continuer"

Calcul estimation :

    Nombre contenus × durée moyenne × bitrate qualité
    Exemple : 20 contenus × 5 min × 48 kbps = ~72 MB

Qualité audio téléchargement :

Qualité | Bitrate | Taille | Disponibilité
--- | --- | --- | ---
Basse | 24 kbps | ~10 MB/h | Gratuit + Premium
Standard | 48 kbps | ~20 MB/h | Gratuit + Premium (défaut)
Haute | 64 kbps | ~30 MB/h | Premium uniquement

Justification : - Standard = bon compromis qualité/taille (Opus 48 kbps = très correct pour voix) - Haute réservée Premium = incitation upgrade - User peut réduire à "basse" si espace limité

### 11.2 Validité et renouvellement

Durée de validité : 30 jours après téléchargement

Standard industrie : - Spotify : 30 jours - YouTube Music : 30 jours - Deezer : 30 jours

Renouvellement automatique :

    App détecte WiFi + contenus >25 jours
    → Requête API : GET /offline/contents/refresh
    → Backend vérifie pour chaque contenu :
        - Abonnement Premium toujours actif ?
        - Contenu pas modéré/supprimé ?
        - Métadonnées à jour ?
    → Renouvelle validité à 30 jours supplémentaires
    → Mise à jour métadonnées (titre, créateur, statut)
    → Pas de re-téléchargement audio (sauf si fichier corrompu)

Notification avant expiration : - J-3 : "X contenus expirent dans 3 jours. Connectez-vous en WiFi pour les renouveler" - J-0 : Suppression automatique - J+0 : Toast "15 contenus expirés ont été supprimés"

Justification : - Force reconnexion : vérifier abonnement actif, contenus légaux - Évite stockage obsolète : contenus supprimés/modérés ne restent pas - UX transparente : renouvellement silencieux si WiFi régulier

### 11.3 Synchronisation actions offline

Actions stockées localement (SQLite) : - Likes/unlikes - Abonnements/désabonnements - Signalements - Progression audio-guides

Sync automatique à la reconnexion :

    1. App détecte reconnexion Internet
    2. Récupération queue locale : SELECT * FROM pending_actions ORDER BY created_at
    3. Envoi batch API : POST /sync/actions
    4. Backend traite chaque action
    5. Confirmation réception : DELETE FROM pending_actions WHERE id IN (...)
    6. Toast : "3 likes et 1 abonnement synchronisés"

Gestion erreurs sync : - Si échec après 3 tentatives → notification : "Impossible de synchroniser. Réessayez plus tard" - Actions conservées jusqu'à sync réussie (pas de perte) - Rétention max 7 jours : après = purge (évite queue infinie)

Conflits contenus supprimés :

    Backend retourne : {deleted_content_ids: [123, 456]}
    → App supprime fichiers locaux
    → Si contenu 123 en cours d'écoute :
        - Attendre fin lecture actuelle
        - Passage auto suivant après 2s
    → Toast : "1 contenu téléchargé a été retiré (violation règles)"

Justification : - Pas de conflit possible : actions unilatérales user (likes/abonnements) - UX fluide : pas de blocage offline - Batch = économie : requêtes HTTP groupées - Conformité modération : contenu illégal disparaît même offline

## Récapitulatif Section 11

Aspect | Décision | Valeur
--- | --- | ---
Zone téléchargement | Choix | Manuel (autour/ville/département/région/recherche)
Limite gratuit | Contenus | 50 max
Limite Premium | Contenus | Illimité (espace disque)
Connexion | Par défaut | WiFi (mobile avec confirmation)
Qualité Standard | Bitrate | 48 kbps Opus
Qualité Haute | Bitrate | 64 kbps (Premium uniquement)
Validité | Durée | 30 jours
Renouvellement | Mode | Automatique si WiFi
Notification expiration | Délai | J-3
Sync actions | Mode | Batch automatique reconnexion
Rétention queue | Durée | 7 jours max

## 12. Gestion des erreurs

### 12.1 Aucun contenu disponible

Stratégie : Élargissement automatique progressif

Flow :

    1. Recherche rayon 50 km → aucun résultat
    2. Élargissement auto 100 km
    3. Si toujours rien → département
    4. Si toujours rien → région
    5. Dernier recours → contenu national (toujours disponible)

Messages adaptatifs :

Cas | Message
--- | ---
Trouvé à 100 km | "Aucun contenu dans votre zone immédiate. Voici du contenu à proximité (100 km)"
Trouvé département | "Aucun contenu local disponible. Voici du contenu dans votre département"
Contenu national | "Aucun contenu local disponible. Voici du contenu national qui pourrait vous intéresser"

Justification : - UX fluide : pas de message d'erreur bloquant "Aucun contenu" - User ne reste jamais sans contenu - Contenu national = filet de sécurité : actualités Le Monde, podcasts génériques

### 12.2 Contenu signalé/supprimé pendant l'écoute

Décision : Pas d'interruption brutale

Flow :

    1. Contenu supprimé côté backend (modération)
    2. Si contenu en écoute → laisser terminer lecture en cours
    3. Après fin lecture → désactiver bouton "Précédent" pour ce contenu
    4. Passage automatique suivant après 2s
    5. Toast notification discrète : "Contenu précédent retiré (violation règles)"

Si tentative "Précédent" manuellement : - Message : "Ce contenu n'est plus disponible" - Retour au contenu actuel

Justification : - Sécurité routière : pas d'interruption brutale pendant conduite - User informé mais pas alarmé : message discret - Empêche réécoute : contenu modéré inaccessible

### 12.3 Perte de réseau

Buffer adaptatif (cf. TECHNICAL.md) :

Réseau | Buffer min | Buffer cible | Buffer max
--- | --- | --- | ---
WiFi | 5s | 30s | 120s
4G/5G | 10s | 45s | 120s
3G | 30s | 90s | 300s

Comportement détaillé :

Phase 1 : Connexion instable (latence élevée, paquets perdus) - Aucun message immédiat - Lecture continue sur buffer - Si > 10s latence : toast discret "Connexion instable"

Phase 2 : Perte totale réseau - Lecture continue jusqu'à épuisement buffer - Toast : "Hors ligne, lecture sur buffer (30s restantes)" - Compte à rebours visible

Phase 3 : Buffer épuisé sans reconnexion - Pause automatique - Overlay : "Connexion perdue. Reconnexion en cours..." - Retry automatique toutes les 5s (max 6 tentatives = 30s)

Phase 4 : Basculement mode offline (après 30s échec) - Popup : "Voulez-vous continuer avec vos contenus téléchargés ?" - Boutons : "Réessayer" / "Mode offline" - Si "Mode offline" → lecture contenus téléchargés

Reconnexion réussie : - Reprise automatique lecture au point d'arrêt exact - Toast : "Connexion rétablie"

Justification : - Expérience fluide zones blanches (tunnels, campagne) - Buffer généreux : absorbe fluctuations réseau mobile - Mode offline secours : si coupure prolongée

### 12.4 Géolocalisation désactivée

Mode dégradé automatique

Contenu disponible :

Type contenu | Disponible
--- | ---
Contenu national (podcasts, actualités) | ✅
Contenu téléchargé (offline) | ✅
Contenus "Neutre" géographiquement | ✅
Contenu géolocalisé (Ancré/Contextuel) | ❌
Audio-guides | ❌
Notifications push géo-déclenchées | ❌

Popup au lancement : - Apparition : Premier lancement après refus géolocalisation - Message : "RoadWave fonctionne mieux avec la géolocalisation activée. Sans elle, seul le contenu national sera disponible." - Boutons : - "Activer" → Redirection paramètres OS - "Continuer sans" → Mode dégradé - Checkbox : "Ne plus me demander"

Banner permanent si refus : - Bandeau haut écran : "Mode limité : géolocalisation désactivée. [Activer]" - Pas intrusif mais rappel constant - Disparaît si géolocalisation réactivée

Justification : - App reste fonctionnelle sans GPS (pas de blocage) - Incitation forte à activer (meilleure UX) - Respecte choix user (RGPD : consentement libre)

## Récapitulatif Section 12

## 13. Conformité RGPD

### 13.1 Gestion du consentement

Décision : Tarteaucitron.js + PostgreSQL backend

Implémentation web : - ✅ Tarteaucitron.js (opensource, self-hosted) - ✅ Banner RGPD français, customisable - ✅ Granularité : fonctionnel / analytique / marketing

Implémentation backend : - Table `user_consents` avec versioning - Champs : user_id, consent_type, version, accepted, timestamp - Historique complet conservé (preuve légale)

Consentements requis : - Géolocalisation précise : obligatoire (banner + permission OS) - Analytics : optionnel (Matomo) - Notifications push : optionnel (permission OS)

Justification : - Opensource, 0€, conformité RGPD garantie - Historique backend = preuve légale en cas de contrôle - Granularité conforme recommandations CNIL

### 13.2 Anonymisation des données GPS

Décision : Geohash après 24h

Processus : 1. Données précises conservées 24h (recommandation personnalisée) 2. Après 24h : conversion en geohash précision 5 (~5km²) 3. Coordonnées originales supprimées définitivement

Implémentation PostGIS :

    -- Job quotidien
    UPDATE location_history
    SET location = ST_SetSRID(ST_GeomFromGeoHash(ST_GeoHash(location::geography, 5)), 4326)::geography,
        anonymized = true
    WHERE created_at < NOW() - INTERVAL '24 hours' AND anonymized = false;

Exceptions : - ✅ Historique personnel visible (liste trajets) : conservation intégrale tant que compte actif - ❌ Analytics globales : uniquement geohash anonyme

Justification : - Vraie anonymisation RGPD (CNIL compliant) - Permet analytics agrégées (heatmaps trafic) - PostGIS natif, 0€

### 13.3 Export des données (portabilité)

Décision : JSON + HTML + ZIP, génération asynchrone

Contenu de l'export :

    export-roadwave-[user_id]-[date].zip
    ├── export.json              # Machine-readable
    ├── index.html              # Human-readable (stylé)
    ├── audio/
    │   ├── content-123.opus
    │   ├── content-456.opus
    │   └── ...
    └── README.txt              # Instructions

Données exportées : - Profil utilisateur (email, pseudo, date inscription, bio) - Historique d'écoute (titres, dates, durées) - Contenus créés (audio + métadonnées) - Abonnements et likes - Centres d'intérêt (jauges) - Historique consentements

Processus : 1. Demande via paramètres compte 2. Génération asynchrone (worker background) 3. Email avec lien download (expire 7 jours) 4. Délai : 48h maximum (conformité RGPD)

Limite : - Maximum 1 export/mois (anti-abus)

Justification : - Conformité article 20 RGPD (portabilité) - Double format (human + machine) - Worker asynchrone évite timeout

### 13.4 Suppression du compte

Décision : Grace period 30j + anonymisation contenus

Processus : 1. Utilisateur clique "Supprimer mon compte" 2. Compte désactivé immédiatement (login impossible) 3. Contenus cachés pendant 30 jours (non diffusés) 4. Email confirmation + lien annulation (valide 30j) 5. Après 30j sans annulation : suppression effective

Suppression effective : - ✅ Compte utilisateur supprimé (données personnelles) - ✅ Historique d'écoute supprimé - ✅ GPS historique supprimé - ✅ Sessions et tokens révoqués - ⚠️ Contenus créés anonymisés (créateur = "Utilisateur supprimé") - ⚠️ Likes et abonnements supprimés (mais compteurs préservés)

Contenus conservés anonymement : - Audio files (CDN) - Métadonnées (titre, description, tags, géolocalisation) - Statistiques d'écoute

Justification : - Grace period évite suppressions impulsives - Anonymisation contenus = intérêt légitime communauté - Conforme RGPD si créateur = donnée supprimée

### 13.5 Mode dégradé (sans GPS précis)

Décision : GeoIP par défaut, GPS optionnel

Niveaux de précision :

Niveau | Technologie | Contenus accessibles | Consentement
--- | --- | --- | ---
Pays | Aucune géoloc | Contenus nationaux uniquement | ❌ Non requis
Ville | GeoIP (MaxMind) | Contenus régionaux/ville | ❌ Non requis
Précis | GPS | Tous contenus (hyperlocaux inclus) | ✅ Requis

Implémentation : - Démarrage app : GeoIP automatique (IP → ville) - Banner in-app : "Activez la géolocalisation pour découvrir du contenu près de chez vous" - Upgrade volontaire vers GPS

API GeoIP : - MaxMind GeoLite2 (gratuit, self-hosted) - Update DB mensuelle automatique - Précision ~80% au niveau ville

Justification : - RGPD : pas de consentement requis pour GeoIP (pas de donnée personnelle) - UX dégradée acceptable (contenus disponibles) - Progressive disclosure (upgrade optionnel)

### 13.6 Durée de conservation des données

Décision : 5 ans inactivité → purge automatique

Règles :

Type de compte | Seuil inactivité | Action
--- | --- | ---
Auditeur uniquement | 5 ans sans connexion | Suppression automatique
Créateur avec contenus actifs | Jamais (tant qu'écoutes) | Conservation indéfinie
Créateur inactif | 5 ans sans connexion + 2 ans sans écoute | Suppression automatique

Notifications avant suppression : - Email + push : 90 jours avant - Email + push : 30 jours avant - Email + push : 7 jours avant - Toute connexion = reset compteur inactivité

Contenu conservé : - Contenus créés par comptes supprimés (anonymisés) : conservation indéfinie

Justification : - Conformité principe minimisation RGPD - 5 ans = équilibre raisonnable (standard industrie) - Exception créateurs actifs = intérêt légitime plateforme

### 13.7 Cookies et trackers web

Décision : Matomo self-hosted, zéro cookie tiers

Cookies utilisés :

Cookie | Type | Durée | Finalité | Consentement
--- | --- | --- | --- | ---
`session` | Technique | 30j | Authentification | ❌ Non requis
`refresh_token` | Technique | 30j | Session persistante | ❌ Non requis
`_pk_id` | Analytique | 13 mois | Matomo (IP anonyme) | ✅ Requis

Analytics : Matomo self-hosted : - Hébergé sur nos serveurs (Docker) - IP anonymisées automatiquement (2 derniers octets) - Pas de cookie si consentement refusé - Alternative : Plausible (SaaS EU, 9€/mois)

Trackers interdits : - ❌ Google Analytics - ❌ Facebook Pixel - ❌ Hotjar, Mixpanel, etc.

Justification : - Souveraineté données (pas de transfert US) - Conformité RGPD max (CNIL compatible) - Matomo = opensource, 0€ infra

### 13.8 Registre des traitements

Décision : Document Markdown versionné Git (MVP)

Emplacement : - `docs/rgpd/registre-traitements.md` - Versionné Git (historique modifications)

Contenu obligatoire par traitement : - Nom et finalité du traitement - Catégories de données collectées - Base légale (consentement / contrat / intérêt légitime) - Durée de conservation - Destinataires (sous-traitants, CDN, etc.) - Transferts hors UE (aucun prévu)

Responsable : - DPO / Fondateur - Review trimestrielle obligatoire - Update immédiate si nouveau traitement

Migration future : - Si > 100K utilisateurs : interface admin PostgreSQL

Justification : - Obligation RGPD Article 30 - Markdown = simple, versionné, auditable - 0€

### 13.9 Notification violations de données (breach)

Décision : Monitoring + alertes + runbook

Détection automatique :

Événement | Outil | Alerte
--- | --- | ---
Erreurs backend critiques | Sentry | Discord/Slack immédiat
Pic requêtes anormal | Grafana | Email équipe
Accès non autorisé DB | PostgreSQL logs | SMS fondateur
Authentification suspecte | Zitadel alerts | Email équipe

Procédure breach : - Runbook : `docs/rgpd/procedure-breach.md` - Checklist 72h CNIL : 1. H+0 : Détection et confinement 2. H+24 : Évaluation gravité (données concernées, utilisateurs impactés) 3. H+48 : Notification CNIL si risque pour utilisateurs 4. H+72 : Notification utilisateurs si risque élevé

Contact CNIL : - Email pré-rédigé (template) - Formulaire en ligne (account CNIL créé)

Justification : - Obligation RGPD Article 33 (notification 72h) - Monitoring proactif évite découverte tardive - Sentry gratuit < 5K events/mois

### 13.10 DPO (Délégué à la Protection des Données)

Décision : Fondateur = DPO temporaire (MVP)

Raison légale : - Non obligatoire si : - < 250 employés - Pas de traitement à grande échelle de données sensibles - RoadWave : données localisation = sensible MAIS échelle MVP

Formation : - CNIL : formation gratuite en ligne (4h) - Certification CNIL "Atelier RGPD" (gratuit)

Contact : - Email : dpo@roadwave.fr - Publié dans CGU et mentions légales - Délai réponse : 1 mois (RGPD)

Migration future : - Si > 100K utilisateurs : DPO externe mutualisé (~200€/mois) - Ou recrutement DPO interne si > 10 employés

Justification : - Conforme RGPD (non obligatoire en phase MVP) - 0€, contrôle total - Bonne pratique : avoir un contact identifié

## Récapitulatif Section 13

Mesure | Implémentation | Coût
--- | --- | ---
Consentement | Tarteaucitron.js + PostgreSQL | 0€
Anonymisation GPS | Geohash PostGIS (24h) | 0€
Export données | JSON+HTML+ZIP asynchrone | 0€
Suppression compte | Grace period 30j + anonymisation | 0€
Mode dégradé | GeoIP MaxMind + GPS optionnel | 0€
Conservation | Purge auto 5 ans inactivité | 0€
Analytics | Matomo self-hosted | ~5€/mois
Registre traitements | Markdown Git | 0€
Breach detection | Sentry + Grafana + runbook | 0€
DPO | Fondateur formé CNIL | 0€

Coût total RGPD : ~5€/mois

## Points d'attention pour Gherkin

- Tester consentement géolocalisation (accept/refuse → contenus différents)
- Tester anonymisation GPS après 24h (job cron)
- Tester export données (génération complète + vérification contenu)
- Tester grace period suppression (annulation possible)
- Tester mode GeoIP (ville détectée correctement)
- Tester purge automatique (5 ans inactivité)
- Tester notifications avant purge (90j/30j/7j)

## 14. Modération - Flows opérationnels

### 14.1 Signalement

Décision : Formulaire simple avec 7 catégories prédéfinies

#### 14.1.1 Catégories de signalement

Liste déroulante avec 7 options :

Catégorie | Description
--- | ---
🚫 Haine & violence | Incitation à la haine, discrimination, menaces
🔞 Contenu sexuel | Pornographie, contenu explicite
⚖️ Illégalité | Terrorisme, apologie de crimes
🎵 Droits d'auteur | Musique/contenu protégé non autorisé
📧 Spam | Publicité non sollicitée, répétition
❌ Fausse information | Désinformation sur santé, sécurité routière
🔧 Autre | Champ texte obligatoire si sélectionné

Justification : - Équilibre entre simplicité (pas trop de choix) et précision (aide les modérateurs) - Coût : 0€ (liste déroulante standard)

#### 14.1.2 Commentaire du signaleur

Décision : Optionnel avec incitation

- Champ texte libre (0-500 caractères)
- Placeholder : "Décrivez le problème (optionnel mais recommandé)"
- Non bloquant : le signalement peut être envoyé sans commentaire

Justification : - Encourage la qualité des signalements sans créer de friction - Aide les modérateurs à comprendre le contexte - Pas de risque d'abandon du processus

#### 14.1.3 Confirmation après signalement

Décision : Toast in-app avec lien historique

Affichage : - Toast notification : "✓ Signalement envoyé. Nous l'examinerons sous 24-48h." - Durée affichage : 5 secondes - Bouton optionnel "Voir mes signalements" (accès historique)

Historique personnel : - Liste des signalements envoyés par l'utilisateur - Statut : En cours / Traité / Rejeté - Notification in-app si action prise (contenu retiré, signalement rejeté)

Justification : - Transparence maximale - Coût : 0€ (aucun email automatique) - Bonne UX

### 14.2 Traitement des signalements

#### 14.2.1 IA pré-filtre (transcription + analyse)

Décision : OpenAI Whisper open source + NLP

Stack technique :

Composant | Technologie | Hébergement
--- | --- | ---
Transcription | Whisper large-v3 | Self-hosted (CPU MVP, GPU scale)
Analyse sentiment | distilbert-base-uncased | Self-hosted
Détection haine | facebook/roberta-hate-speech | Self-hosted
Mots-clés | Liste noire FR/EN + regex | PostgreSQL

Processus : 1. Signalement reçu → ajout file d'attente asynchrone 2. Transcription audio (1-10 minutes selon durée) 3. Analyse automatique : - Score de confiance : 0-100% - Catégorie détectée - Timestamps des passages problématiques 4. Priorisation automatique selon score

Délais : - Audio <5 min : 1-3 minutes - Audio 5-30 min : 3-10 minutes - Audio >30 min : 10-20 minutes

Coût : - MVP : 0€ (CPU standard, processing asynchrone) - Scale : 50-200€/mois (GPU VPS si >1000 signalements/jour)

Justification : - 100% open source, pas de dépendance GAFAM - Coût maîtrisé (scaling progressif) - Gain productivité modérateurs ×3-5

#### 14.2.2 Délais de traitement (SLA)

Décision : SLA progressif selon priorité

Priorité | Délai cible | Traitement
--- | --- | ---
CRITIQUE | <2h (24/7) | Violence, suicide, mise en danger → Astreinte modérateur senior
HAUTE | <24h (jours ouvrés) | Haine, harcèlement, désinformation → Modérateur junior/senior
MOYENNE | <24h (jours ouvrés) | Spam, contenu inapproprié → Modérateur junior
BASSE | <72h (jours ouvrés) | Qualité audio, tags incorrects → Modérateur junior

Traitement automatique : - Score IA >95% + catégorie évidente (ex: spam répété) → Action automatique immédiate - Notification créateur + possibilité d'appel

Justification : - Réaliste et conforme DSA (Digital Services Act) - Scalable : priorisation automatique - Ressources humaines optimisées

#### 14.2.3 Priorisation automatique

Décision : File d'attente intelligente basée sur score IA

Calcul de priorité :

    Priorité = (Score_IA × 0.7) + (Signalements_cumulés × 0.2) + (Fiabilité_signaleur × 0.1)

Détails : - Score_IA : 0-100% (confiance analyse automatique) - Signalements_cumulés : nombre de signalements du même contenu (boost priorité) - Fiabilité_signaleur : score utilisateur (historique signalements pertinents)

Classification résultante : - Priorité ≥90 → CRITIQUE (traitement immédiat) - Priorité 70-89 → HAUTE (file prioritaire) - Priorité 40-69 → MOYENNE (file normale) - Priorité <40 → BASSE (file différée)

Justification : - Optimise le temps des modérateurs - Traite les cas graves en priorité - Coût : 0€ (algorithme simple)

### 14.3 Sanctions

#### 14.3.1 Notification au créateur

Décision : Multi-canal (email + push + in-app)

Canaux utilisés :

Canal | Timing | Contenu
--- | --- | ---
Push notification | Immédiat | Alerte courte : "Votre contenu a été modéré"
In-app | Au prochain lancement | Popup détaillée avec bouton "Voir détails"
Email | Dans l'heure | Notification complète avec lien vers formulaire d'appel

Contenu email :

    Objet : Modération de votre contenu "[Titre du contenu]"

    Bonjour [Pseudo],

    Votre contenu "[Titre]" publié le [Date] a été modéré.

    Catégorie violée : [Catégorie]
    Raison : [Explication détaillée]
    Sanction : [Strike X / Suspension X jours / Suppression contenu]

    Extrait audio concerné : [Timestamp]
    Transcription : "[Passage problématique surligné]"

    Vous pouvez contester cette décision sous 7 jours :
    [Lien formulaire d'appel]

    L'équipe RoadWave

Coût : - Email : ~0.001€/notification (Brevo, Resend) - Push : 0€ (Firebase Cloud Messaging / APNs) - In-app : 0€

Justification : - Conformité DSA (transparence obligatoire) - Multi-canal garantit réception - Coût négligeable

#### 14.3.2 Détail de la sanction

Décision : Notification complète avec preuves

Éléments inclus obligatoirement :

1. Catégorie violée : référence précise CGU (ex: "Article 3.2 - Haine & violence")
2. Raison détaillée : explication en langage clair (non juridique)
3. Extrait audio : timestamp exact du passage problématique (ex: "3:42-4:15")
4. Transcription : texte problématique surligné en rouge
5. Gravité : Strike actuel + conséquences (ex: "Strike 2/4 - Suspension 7 jours")
6. Recours : lien direct vers formulaire d'appel + délai (7 jours)

Exemple visuel in-app :

    ┌─────────────────────────────────────┐
    │ ⚠️ Contenu modéré                   │
    ├─────────────────────────────────────┤
    │ Titre : "Mon podcast #42"           │
    │ Publié le : 15/01/2026              │
    │                                     │
    │ Catégorie violée :                  │
    │ 🚫 Haine & violence (Article 3.2)   │
    │                                     │
    │ Passage problématique : 3:42-4:15   │
    │ "[Transcription surlignée]"         │
    │                                     │
    │ Sanction : Strike 2/4               │
    │ Suspension : 7 jours                │
    │                                     │
    │ [Contester cette décision]          │
    └─────────────────────────────────────┘

Justification : - Transparence maximale (obligation DSA) - Créateur comprend l'erreur → amélioration future - Réduit les appels non fondés

#### 14.3.3 Processus d'appel

Décision : Formulaire in-app structuré

Accès : - Bouton "Contester cette décision" dans notification - Section "Mes sanctions" dans profil créateur

Formulaire d'appel :

Champ | Type | Obligatoire
--- | --- | ---
Sanction contestée | Pré-rempli (non modifiable) | ✅
Raison de l'appel | Texte libre (50-1000 caractères) | ✅
Arguments | Zone texte enrichie | ✅
Preuves | Upload fichiers (max 5, 10 MB total) | ❌

Après soumission : - Génération numéro de ticket unique (ex: `#MOD-2026-00142`) - Email confirmation : "Votre appel sera traité sous 72h" - Statut visible dans l'app : "En cours d'examen"

Délai de soumission : - Maximum 7 jours après notification de sanction - Après 7 jours : appel automatiquement refusé

Justification : - Professionnel et traçable - Intégration complète avec système modération - Coût : 0€ (formulaire custom backend)

#### 14.3.4 Délai de réponse pour appel

Décision : SLA 72h garanti

Délais :

Type d'appel | Délai | Responsable
--- | --- | ---
Standard | 72h max (3 jours ouvrés) | Modérateur senior
Complexe | 5 jours ouvrés + notification intermédiaire J+3 | Modérateur senior + Admin modération
Critique | 24h (cas suspension longue/ban) | Admin modération

Notification intermédiaire (si délai >72h) : - Email J+3 : "Votre appel #MOD-XXX est en cours d'examen approfondi. Réponse sous 2 jours."

Réponse finale :

Email détaillé avec : 1. Décision : Maintien / Annulation / Réduction de sanction 2. Justification : explication de la décision d'appel 3. Actions : Strike retiré / Suspension annulée / Contenu rétabli (si applicable) 4. Définitif : mention "Cette décision est définitive" (pas de second appel)

Suivi in-app : - Mise à jour statut : "Appel accepté ✓" ou "Appel rejeté ✗" - Badge notification

Justification : - Équilibre entre rapidité et qualité de traitement - Conforme pratiques industrie (YouTube, TikTok : 5-7 jours) - Ressources humaines réalistes

### 14.4 Outils modérateurs

Stack technique complète :

Outil | Technologie | Fonction
--- | --- | ---
Dashboard | React + TanStack Table | Interface modération
File signalements | PostgreSQL + Redis | Priorisation temps réel
Player audio | Wavesurfer.js | Lecture avec waveform + annotations
Transcription | Whisper large-v3 | Conversion audio → texte
Historique créateur | Vue 360° | Contenus, strikes, appels, métriques
Actions rapides | Shortcuts clavier | Approuver (A), Rejeter (R), Escalade (E)
Logs audit | PostgreSQL + export | Traçabilité complète (DSA)
Collaboration | Système de commentaires | Modérateurs peuvent s'entraider sur cas complexes

Fonctionnalités clés :

1. Lecture accélérée : 0.75x à 2x (gain productivité)
2. Marqueurs temporels : annotation directe sur waveform
3. Historique créateur : vue rapide contenus précédents + strikes
4. Statistiques : signalements traités/jour, temps moyen, précision
5. Fil d'activité : actions récentes équipe (temps réel)

Coût infrastructure : - MVP : 0-50€/mois (serveur CPU) - Scale : 50-200€/mois (GPU + Redis Cluster)

### 14.5 Modération préventive (rappel)

Nouveaux créateurs : - Validation manuelle des 3 premiers contenus - Délai : 24-48h (jours ouvrés) - Transcription automatique pour aide modérateur

Score de confiance : - Évolution dynamique selon historique - Créateur fiable (0 strike depuis 6 mois) → validation automatique - Créateur suspect (strikes récents) → validation manuelle systématique

Publicités : - Validation manuelle obligatoire 24-48h (responsabilité juridique) - Transcription + analyse métadonnées (ciblage, durée, volume)

Justification : - Prévention > réaction (économie modération) - Qualité plateforme préservée dès le début

## Récapitulatif Section 14

Point | Décision | Coût
--- | --- | ---
Catégories signalement | 7 catégories prédéfinies + champ libre | 0€
Commentaire signaleur | Optionnel avec incitation | 0€
Confirmation | Toast in-app + historique personnel | 0€
IA pré-filtre | Whisper (CPU MVP, GPU scale) + NLP open source | 0-200€/mois
Délais traitement | SLA progressif : 2h/24h/72h selon priorité | Dépend équipe
Priorisation | File intelligente basée score IA | 0€
Notification sanction | Email + push + in-app (multi-canal) | ~0.001€/notif
Détail sanction | Complet : raison + extrait + transcription | 0€
Processus appel | Formulaire in-app structuré | 0€
Délai appel | 72h garanti (standard) | Dépend équipe
Outils modérateurs | Dashboard React + Whisper + Wavesurfer.js | 0-200€/mois

Coût total MVP : 0-200€/mois (infrastructure IA optionnelle)

Conformité : - ✅ DSA (Digital Services Act) : transparence, traçabilité, délais - ✅ RGPD : données modération anonymisées après 3 ans - ✅ Logs audit : toutes actions tracées (obligation légale plateforme)

Scalabilité : - 0-1000 signalements/mois : équipe 1-2 modérateurs junior + 1 senior - 1000-10K signalements/mois : équipe 5-10 modérateurs + IA GPU - 10K+ signalements/mois : équipe dédiée + IA optimisée + modération communautaire

Prochaine section à clarifier : Section 11 (Mode offline) ou Section 12 (Gestion des erreurs)

## 15. Autres comportements

### 15.1 Partage de contenu

Décision : Système de partage complet avec web player

#### 15.1.1 Bouton "Partager"

Disponibilité : Partout dans l'application

Emplacements : - Player en lecture (bouton dans contrôles) - Page profil créateur (sur chaque contenu) - Liste de recherche (menu contextuel) - Historique personnel

Icône : ⬆️ (universelle iOS/Android)

Menu options : - Copier le lien - WhatsApp - Email - SMS - Plus... (sheet natif OS)

Justification : - Viralité = croissance organique gratuite - Aucune friction, partage universel

#### 15.1.2 Comportement du lien partagé

Format URL : `https://roadwave.fr/share/c/[content_id]`

Comportement multi-plateforme :

    User clique lien partagé
        ↓
    Page web responsive
        ↓
    ┌─────────────────────────────────┐
    │ Si app installée                │
    │ → Deep link (ouverture directe) │
    └─────────────────────────────────┘
        ↓
    ┌─────────────────────────────────┐
    │ Si app non installée             │
    │ → Web player + CTA téléchargement│
    └─────────────────────────────────┘

Contenu de la page web :

    ┌───────────────────────────────────────┐
    │ RoadWave                              │
    ├───────────────────────────────────────┤
    │ [Image cover 16:9]                    │
    │                                       │
    │ 📻 Titre du contenu                   │
    │ Par @créateur · 12 min · 🎧 2.3K      │
    │                                       │
    │ 📍 Paris 5e · Ancré                   │
    │ 🏷️ #Voyage #Histoire                  │
    │                                       │
    │ Description : Lorem ipsum...          │
    │                                       │
    │ [▶️ Écouter maintenant]               │
    │ (Player HTML5 si contenu public)      │
    │                                       │
    │ ──────────────────────────────────    │
    │                                       │
    │ 📱 Télécharger l'app RoadWave         │
    │ [App Store] [Google Play]             │
    │                                       │
    │ [Voir le profil de @créateur]         │
    └───────────────────────────────────────┘

Métadonnées Open Graph (SEO) :

    <meta property="og:title" content="[Titre contenu] - RoadWave">
    <meta property="og:description" content="[Description ou extrait]">
    <meta property="og:image" content="[URL cover image]">
    <meta property="og:audio" content="[URL audio si public]">
    <meta property="og:type" content="music.song">
    <meta property="og:site_name" content="RoadWave">
    <meta name="twitter:card" content="player">
    <meta name="twitter:player" content="https://roadwave.fr/player/[content_id]">

Deep linking : - iOS : Universal Links (configuration `apple-app-site-association`) - Android : App Links (configuration `assetlinks.json`) - URL scheme : `roadwave://content/[content_id]`

Justification : - Meilleure viralité (partage social optimisé) - SEO (contenus indexés Google) - UX optimale (web + app) - Coût : 0€ (backend simple + CDN existant)

#### 15.1.3 Contenus Premium partagés

Décision : Preview 30 secondes + paywall

Comportement :

1. User clique lien contenu Premium partagé
2. Page web affiche badge "👑 Contenu Premium"
3. Player démarre automatiquement
4. Après 30 secondes exactement :
5. Fade out audio (2 secondes)
6. Overlay apparaît :

    ┌─────────────────────────────────┐
    │ 👑 Contenu réservé Premium      │
    │                                 │
    │ Profitez de ce contenu complet  │
    │ et de milliers d'autres         │
    │ sans publicité                  │
    │                                 │
    │ [Passer Premium - 4.99€/mois]   │
    │ [Télécharger l'app]             │
    └─────────────────────────────────┘

1. Utilisateur peut :
2. S'abonner Premium (redirection web Mangopay)
3. Télécharger l'app (redirection stores)
4. Rejouer les 30 premières secondes (illimité)

Tracking : - Métriques créateur : "Partages Premium" + "Conversions Premium" - Créateur touche sa part si conversion (70%)

Justification : - Équilibre viralité / monétisation - 30s = assez pour donner envie, pas assez pour satisfaire - Protège revenus créateurs

### 15.2 Profil créateur

Décision : Profil public complet et transparent

#### 15.2.1 Structure de la page profil

URL : `https://roadwave.fr/@[pseudo]`

Layout :

    ┌────────────────────────────────────────┐
    │ [Photo profil 120×120]                 │
    │ @pseudo ✓                              │
    │ [Badge vérifié si applicable]          │
    │                                        │
    │ Bio : Lorem ipsum dolor sit amet...    │
    │ (300 caractères max)                   │
    │                                        │
    │ 🎧 1.2K abonnés                        │
    │ 📻 42 contenus                         │
    │ ⏱️ 18h de contenu créé                 │
    │ 🔊 54K écoutes totales                 │
    │                                        │
    │ [S'abonner] [Partager profil] [•••]   │
    │                                        │
    │ ────────────────────────────────────   │
    │                                        │
    │ Contenus ▼ [Plus récents ▼]           │
    │                                        │
    │ ┌──────────────────────────────────┐  │
    │ │ [Cover] Titre contenu 1           │  │
    │ │ 12 min · 🎧 2.3K · 📍 Paris       │  │
    │ │ [▶️]                              │  │
    │ └──────────────────────────────────┘  │
    │                                        │
    │ ┌──────────────────────────────────┐  │
    │ │ [Cover] Titre contenu 2           │  │
    │ │ 8 min · 🎧 5.1K · 📍 Lyon         │  │
    │ │ [▶️]                              │  │
    │ └──────────────────────────────────┘  │
    │                                        │
    │ [Charger plus]                         │
    └────────────────────────────────────────┘

Informations affichées :

Élément | Visibilité | Détails
--- | --- | ---
Photo + pseudo | ✅ Public | Identité visuelle
Badge vérifié ✓ | ✅ Public (si applicable) | Compte authentique
Bio | ✅ Public | 0-300 caractères, markdown basique (gras, italique, liens)
Nombre abonnés | ✅ Public | Arrondi si >1000 (ex: 1.2K, 54K)
Nombre contenus | ✅ Public | Exact
Durée totale créée | ✅ Public | Arrondi en heures (ex: 18h, 142h)
Écoutes totales | ✅ Public | Arrondi (ex: 54K, 1.2M)
Liste abonnés | ❌ Privé | Protection vie privée (RGPD)
Revenus | ❌ Privé | Confidentialité financière
Localisation précise | ❌ Privé | Sécurité
Email | ❌ Privé | Anti-spam

Tri des contenus :

Option | Comportement
--- | ---
Plus récents | Date publication DESC (défaut)
Plus populaires | Écoutes complètes × (1 + (date_publication - now) / 90 jours)
Plus anciens | Date publication ASC
Par tag | Filtre multi-sélection tags

Recherche locale : - Barre recherche dans profil : "Rechercher dans les contenus de @pseudo" - Recherche full-text sur titres + descriptions

Actions menu [•••] : - Partager profil - Signaler profil (spam, usurpation) - Bloquer créateur (masque tous ses contenus)

#### 15.2.2 Statistiques publiques

Décision : Stats arrondies et motivantes

Affichage public :

Métrique | Format affichage | Exemple
--- | --- | ---
Abonnés | Exact si <1000, arrondi sinon | 342 / 1.2K / 54K / 1.2M
Écoutes totales | Arrondi dès 1000 | 842 / 5.4K / 142K / 2.1M
Contenus publiés | Exact | 42 contenus
Durée totale | Arrondi en heures | 18h / 142h de contenu

Métriques PRIVÉES (créateur uniquement) :

Métrique | Disponible dans dashboard créateur
--- | ---
Taux complétion moyen | 78% (écoutes >80% / écoutes totales)
Évolution abonnés | Graphique 30j / 90j / 1 an
Écoutes par contenu | Tableau détaillé
Revenus | Dashboard monétisation dédié
Taux conversion Premium | Partages → conversions
Démographie | Âge / zone géo (agrégée, anonymisée)

Justification : - Arrondi = évite comparaisons anxiogènes - Preuve sociale pour nouveaux auditeurs (trust) - Gamification douce (motivation créateurs) - Privacy by design

#### 15.2.3 Badge vérifié

Décision : Badge unique ✓ (vérifié officiel)

Critères d'attribution (au moins UN des critères) :

1. KYC monétisation validé : identité vérifiée via Mangopay KYC
2. Célébrité / Média officiel : validation manuelle équipe RoadWave
3. Communauté significative : ≥10K abonnés + compte actif >6 mois

Affichage : - Badge bleu ✓ accolé au pseudo (partout : profil, player, recherche) - Tooltip au survol/appui long : "Compte vérifié"

Processus d'obtention :

Type | Processus
--- | ---
Automatique (KYC) | Badge attribué dès validation documents Mangopay
Manuel (célébrité) | Formulaire demande → équipe vérifie identité → validation 48-72h
Automatique (10K) | Badge attribué automatiquement à 10K abonnés si compte >6 mois

Retrait du badge : - Suspension monétisation → badge retiré temporairement - Strikes multiples → badge retiré définitivement - Usurpation identité détectée → ban + retrait

Justification : - Combat usurpations d'identité - Trust auditeurs (surtout pour médias/personnalités) - Simplicité (1 seul badge, pas de gamification excessive) - Coût : 0€ (champ boolean `verified` en DB)

### 15.3 Recherche

Décision : Recherche full-text + géo + filtres avancés

#### 15.3.1 Recherche par mot-clé

Implémentation : PostgreSQL full-text search (français)

Configuration technique :

    -- Index full-text optimisé français
    CREATE INDEX idx_content_search ON contents
    USING GIN(
        to_tsvector('french',
            coalesce(title, '') || ' ' ||
            coalesce(description, '') || ' ' ||
            coalesce(creator_pseudo, '')
        )
    );

    -- Recherche avec ranking
    SELECT
        c.*,
        ts_rank(
            to_tsvector('french', c.title || ' ' || c.description),
            plainto_tsquery('french', $search_query)
        ) AS rank
    FROM contents c
    WHERE to_tsvector('french', c.title || ' ' || c.description)
          @@ plainto_tsquery('french', $search_query)
    ORDER BY rank DESC, listen_count DESC
    LIMIT 20;

Champs indexés : - Titre du contenu (poids × 3) - Description (poids × 1) - Pseudo créateur (poids × 2) - Tags (poids × 1.5)

Fonctionnalités :

Feature | Description
--- | ---
Stemming français | "voyages" trouve "voyage", "voyager", etc.
Correction auto | Suggestion si 0 résultat
Recherches populaires | "Essayez plutôt : balade paris, audio-guide louvre"
Historique personnel | 10 dernières recherches sauvegardées
Autocomplete | Suggestions pendant frappe (top 5)

Coût : 0€ (PostgreSQL natif)

Migration future : - Si >100K contenus : Meilisearch (typo-tolerance avancée, ~20-50€/mois) - Si >1M contenus : Elasticsearch cluster

Justification : - PostgreSQL full-text = performant jusqu'à 500K contenus - Stemming français natif - 0€, aucune dépendance externe

#### 15.3.2 Recherche géographique

Décision : Recherche lieu + rayon paramétrable

Interface utilisateur :

    ┌─────────────────────────────────────┐
    │ 🔍 Recherche contenu...             │
    ├─────────────────────────────────────┤
    │ �� Lieu                             │
    │ [Paris, France              ▼]     │
    │ · Autour de moi (GPS actuel)       │
    │ · Entrer une adresse/ville         │
    │                                     │
    │ 📏 Rayon de recherche               │
    │ [●─────────────────] 50 km         │
    │ (curseur 5 km → 500 km)            │
    │                                     │
    │ 🗺️ [Afficher sur carte]            │
    └─────────────────────────────────────┘

Géocodage :

Service | Usage | Coût
--- | --- | ---
Nominatim (OSM) | MVP (API publique) | 0€ (rate limit 1 req/s)
Nominatim self-hosted | Scale (Docker) | 20-50€/mois VPS
Mapbox Geocoding | Fallback premium | 0.50€ / 1000 requêtes

Processus de recherche géo :

1. User tape "Louvre" ou "Paris"
2. Autocomplete via Nominatim → liste suggestions
3. User sélectionne → récupération coordonnées (lat, lon)
4. Requête PostGIS :

    SELECT c.*,
           ST_Distance(c.location::geography, ST_Point($lon, $lat)::geography) AS distance
    FROM contents c
    WHERE ST_DWithin(
        c.location::geography,
        ST_Point($lon, $lat)::geography,
        $radius_meters
    )
    ORDER BY distance ASC;

Affichage résultats : - Tri par défaut : distance croissante - Indication distance : "À 2.3 km" / "À 15 km" / "À 142 km" - Option carte : markers cliquables (clustering si >50 résultats)

Coût : - MVP : 0€ (Nominatim public) - Scale : 20-50€/mois (Nominatim self-hosted Docker)

Justification : - Essentiel pour tourisme / planification trajet - OpenStreetMap = pas de dépendance Google - PostGIS = performant (index GIST natif)

#### 15.3.3 Filtres avancés

Décision : 7 catégories de filtres combinables

Interface filtres :

    ┌─────────────────────────────────────┐
    │ Filtres                        [×]  │
    ├─────────────────────────────────────┤
    │ Type de contenu                     │
    │ ☐ Contenu court (<5 min)            │
    │ ☐ Podcast (>5 min)                  │
    │ ☐ Radio live                        │
    │ ☐ Audio-guide                       │
    │                                     │
    │ Durée                               │
    │ ○ Toutes durées                     │
    │ ○ <5 min                            │
    │ ○ 5-15 min                          │
    │ ○ 15-30 min                         │
    │ ○ >30 min                           │
    │                                     │
    │ Classification âge                  │
    │ ☐ Tout public                       │
    │ ☐ 13+                               │
    │ ☐ 16+                               │
    │ ☐ 18+                               │
    │                                     │
    │ Géo-pertinence                      │
    │ ☐ Ancré (lieu précis)               │
    │ ☐ Contextuel (zone large)           │
    │ ☐ Neutre (national)                 │
    │                                     │
    │ Tags (multi-sélection)              │
    │ ☐ Automobile   ☐ Voyage             │
    │ ☐ Famille      ☐ Histoire           │
    │ ☐ Économie     ☐ Sciences           │
    │ ... (liste complète tags)           │
    │                                     │
    │ Date de publication                 │
    │ ○ Toutes dates                      │
    │ ○ Dernières 24h                     │
    │ ○ Cette semaine                     │
    │ ○ Ce mois                           │
    │ ○ Cette année                       │
    │                                     │
    │ Abonnement                          │
    │ ○ Tous les contenus                 │
    │ ○ Gratuits uniquement               │
    │ ○ Premium uniquement 👑             │
    │                                     │
    │ ──────────────────────────────      │
    │ [Réinitialiser] [Appliquer]         │
    └─────────────────────────────────────┘

Options de tri :

Tri | Algorithme
--- | ---
Pertinence | Score recherche × (1 + log(listen_count + 1))
Popularité | Écoutes complètes derniers 30j DESC
Récent | Date publication DESC
Proximité | Distance GPS ASC (si recherche géo active)
Durée | Durée audio ASC ou DESC

Sauvegarde de recherches :

- Bouton "💾 Sauvegarder cette recherche"
- Nom personnalisable : "Podcasts voyage Paris"
- Maximum 5 recherches sauvegardées
- Accès rapide : onglet "Recherches sauvegardées" dans page recherche
- Notifications optionnelles : "3 nouveaux contenus dans 'Podcasts voyage Paris'"

Performances :

    -- Index composites pour filtres
    CREATE INDEX idx_content_filters ON contents (
        content_type,
        duration,
        age_rating,
        geo_type,
        published_at
    );

    -- Index GIN pour tags
    CREATE INDEX idx_content_tags ON contents USING GIN(tags);

Coût : 0€ (PostgreSQL + index standards)

Justification : - Filtres essentiels pour découvrabilité - Combinables = puissance maximale - Sauvegarde = gain temps utilisateurs réguliers

#### 15.3.4 Page de résultats

Décision : Liste avec previews enrichies

Layout résultats :

    ┌─────────────────────────────────────────┐
    │ 🔍 "voyage paris"                       │
    │ 42 résultats · Tri : Pertinence ▼       │
    │ [Filtres] [Carte]                       │
    ├─────────────────────────────────────────┤
    │ ┌─────────────────────────────────────┐ │
    │ │ [Cover     ] Balade à Paris          │ │
    │ │ [16:9      ] @paris_stories ✓        │ │
    │ │ [Image     ] 12 min · 🎧 2.3K        │ │
    │ │              📍 Paris 5e · Ancré     │ │
    │ │              🏷️ #Voyage #Histoire    │ │
    │ │              [▶️ Écouter] [⋮]        │ │
    │ └─────────────────────────────────────┘ │
    │                                         │
    │ ┌─────────────────────────────────────┐ │
    │ │ [Cover     ] Secrets Montmartre      │ │
    │ │ [16:9      ] @explore_paris          │ │
    │ │ [Image     ] 8 min · 🎧 5.1K         │ │
    │ │              📍 Paris 18e · Guide    │ │
    │ │              🏷️ #Voyage #Art         │ │
    │ │              [▶️ Écouter] [⋮]        │ │
    │ └─────────────────────────────────────┘ │
    │                                         │
    │ [Charger plus] (20 suivants)            │
    └─────────────────────────────────────────┘

Informations par résultat :

Élément | Affichage
--- | ---
Cover image | 16:9, 120×68 px, lazy loading
Titre | Tronqué 2 lignes max
Créateur | @pseudo + badge ✓ si vérifié, cliquable → profil
Durée | Format : "3 min" / "12 min" / "1h 24 min"
Écoutes | Arrondi : "2.3K" / "54K" / "1.2M"
Localisation | Ville + type géo (Ancré/Contextuel/Neutre)
Tags | Maximum 3 premiers tags
Badge Premium | 👑 si contenu premium
Distance | Si recherche géo : "À 2.3 km"

Actions contextuelles [⋮] : - Partager - Ajouter à une playlist (future feature) - Télécharger (offline) - Signaler

Pagination : - 20 résultats par page - Infinite scroll (charger automatiquement si scroll >80%) - Bouton "Charger 20 suivants" en bas (fallback si scroll auto désactivé)

Vue carte (alternative) : - Bouton toggle "Liste / Carte" - Map Leaflet (OpenStreetMap) - Markers cliquables → popup avec preview - Clustering si >50 résultats proches

Coût : 0€ (Leaflet open source + OSM tiles gratuit)

Justification : - Équilibre information / compacité - Lazy loading = performances - Infinite scroll = UX moderne

## Récapitulatif Section 15

Point | Décision | Coût | Complexité
--- | --- | --- | ---
15.1.1 Bouton partager | Disponible partout (⬆️), menu natif OS | 0€ | Faible
15.1.2 Lien partagé | Web player + deep link + Open Graph SEO | 0€ | Moyenne
15.1.3 Premium partagé | Preview 30s + paywall overlay | 0€ | Faible
15.2.1 Page profil | Profil public complet (stats + bio + contenus + tri) | 0€ | Faible
15.2.2 Stats publiques | Arrondies (abonnés, écoutes, durée totale) | 0€ | Faible
15.2.3 Badge vérifié | ✓ si KYC/célébrité/>10K abonnés | 0€ | Faible
15.3.1 Recherche texte | PostgreSQL full-text french + stemming | 0€ | Moyenne
15.3.2 Recherche géo | Lieu + rayon (Nominatim OSM) | 0-50€/mois | Moyenne
15.3.3 Filtres | 7 catégories combinables + sauvegarde recherches | 0€ | Moyenne
15.3.4 Page résultats | Liste enrichie + vue carte Leaflet + infinite scroll | 0€ | Moyenne

Coût total MVP : 0-50€/mois (Nominatim self-hosted optionnel)

## Points d'attention pour Gherkin

- Tester partage contenu public vs Premium (preview 30s)
- Tester deep linking iOS/Android (ouverture app si installée)
- Tester Open Graph (aperçu correct sur WhatsApp, Twitter, Facebook)
- Tester profil public (stats arrondies, badge vérifié)
- Tester recherche full-text français (stemming, accents)
- Tester recherche géo + rayon (PostGIS distance)
- Tester combinaison filtres multiples (AND logic)
- Tester sauvegarde recherches (max 5)
- Tester pagination infinite scroll + fallback bouton
- Tester vue carte Leaflet (clustering, markers cliquables)

## 16. Audio-guides multi-séquences

### 16.1 Types d'audio-guides et modes de déplacement

Décision : 4 modes distincts avec détection automatique

#### 16.1.1 Classification par mode

Mode | Vitesse détection | Déclenchement | Use case
--- | --- | --- | ---
🚶 Piéton | <5 km/h | Manuel (bouton "Suivant") | Musées, visites urbaines, monuments
🚗 Voiture | >10 km/h | Auto GPS + Manuel possible | Safari-parc, routes touristiques, circuits auto
🚴 Vélo | 5-25 km/h | Auto GPS + Manuel possible | Pistes cyclables, circuits vélo, parcours nature
🚌 Transport | Variable | Auto GPS + Manuel possible | Bus touristiques, trains panoramiques

Détection automatique : - Vitesse moyenne calculée sur 30 secondes - Suggestion mode au démarrage : "Détection : 🚗 Voiture. Est-ce correct ? [Oui] [Changer]" - User peut forcer mode manuellement (settings)

Justification : - Flexibilité maximale créateurs et utilisateurs - Expériences optimisées par type de déplacement - Gestion cas limites (vélo lent vs piéton rapide)

#### 16.1.2 Création d'un audio-guide (côté créateur)

Formulaire création :

    ┌────────────────────────────────────────┐
    │ Nouvel audio-guide multi-séquences     │
    ├────────────────────────────────────────┤
    │ Titre : [Safari du Paugre]            │
    │ Description : [Découvrez les animaux   │
    │               du parc en voiture...]    │
    │                                        │
    │ Mode de déplacement : *obligatoire     │
    │ ○ 🚶 Piéton (navigation manuelle)      │
    │ ● 🚗 Voiture (GPS auto + manuel)       │
    │ ○ 🚴 Vélo (GPS auto + manuel)          │
    │ ○ 🚌 Transport (GPS auto + manuel)     │
    │                                        │
    │ Vitesse recommandée : 30-50 km/h       │
    │ (si voiture/vélo/transport)            │
    │                                        │
    │ ────────────────────────────────────   │
    │                                        │
    │ Séquences (ordre lecture) :            │
    │                                        │
    │ 1. [📍] Introduction - Point d'accueil │
    │    Lat: 43.1234, Lon: 2.5678          │
    │    Rayon déclenchement : 30m           │
    │    Durée : 2:15                        │
    │    [🎵 Audio uploadé] [✏️] [🗑️]       │
    │                                        │
    │ 2. [📍] Enclos des lions               │
    │    Lat: 43.1245, Lon: 2.5690          │
    │    Rayon déclenchement : 30m           │
    │    Durée : 3:42                        │
    │    [📤 Upload audio] [✏️] [🗑️]        │
    │                                        │
    │ 3. [📍] Enclos des girafes             │
    │    [+ Ajouter point GPS]               │
    │                                        │
    │ [+ Ajouter séquence]                   │
    │                                        │
    │ 📊 Statistiques :                      │
    │ · 2 séquences complètes                │
    │ · 5:57 durée totale                    │
    │ · 320m distance totale                 │
    │                                        │
    │ [🗺️ Aperçu sur carte]                 │
    │ [✅ Publier audio-guide]               │
    └────────────────────────────────────────┘

Métadonnées obligatoires :

Champ | Requis | Détails
--- | --- | ---
Titre audio-guide | ✅ | 5-100 caractères
Description | ✅ | 10-500 caractères
Mode déplacement | ✅ | Piéton / Voiture / Vélo / Transport
Nombre séquences | ✅ | Minimum 2, maximum 50
Point GPS par séquence | ✅ (sauf piéton) | Latitude, longitude (WGS84)
Rayon déclenchement | ✅ (sauf piéton) | 10-100m selon mode
Vitesse recommandée | ❌ | Optionnel, affichée utilisateur
Tags | ✅ | 1-3 parmi liste prédéfinie
Classification âge | ✅ | Tout public / 13+ / 16+ / 18+
Zone diffusion | ✅ | Polygon géographique

Wizard de création : - Étape 1 : Infos générales (titre, description, mode) - Étape 2 : Ajout séquences une par une - Étape 3 : Preview carte (trace + points) - Étape 4 : Validation modération (3 premiers audio-guides)

Justification : - Contrôle total créateur sur expérience - Carte preview aide visualiser parcours - Wizard guidé = réduction friction création

### 16.2 Mode Piéton (manuel)

Décision : Navigation manuelle avec pub auto-play

#### 16.2.1 Passage entre séquences

Séquence normale (sans pub) :

1. Séquence 1 se termine
2. Player se met en pause automatique
3. Message affiché : "Séquence 1 terminée. Appuyez sur Suivant quand vous êtes prêt."
4. User appuie sur [▶|] → Séquence 2 démarre immédiatement

Séquence avec publicité (1 pub / 5 séquences) :

1. Séquence 2 se termine
2. Publicité s'enchaîne automatiquement (pas d'attente bouton)
3. Pub se lit (skippable après 5s)
4. Pub se termine → Player se met en pause automatique
5. Message : "Séquence 3 prête. Appuyez sur Suivant."
6. User appuie sur [▶|] → Séquence 3 démarre

Schéma flux :

    Séquence 1 [fin] → PAUSE → User clique → Séquence 2 [fin] → PUB AUTO-PLAY → PAUSE → User clique → Séquence 3

Fréquence pub : - Gratuits : 1 pub toutes les 5 séquences (paramétrable admin 1/3 à 1/10) - Premium : 0 pub

Justification : - Pub s'insère naturellement (pas d'attente utilisateur pour déclencher) - User garde contrôle rythme visite (pause après pub) - Monétisation effective créateurs - Premium reste attractif (0 interruption)

#### 16.2.2 Navigation et contrôles

Décision : Liberté totale utilisateur

Contrôles disponibles :

Bouton | Fonction | Comportement
--- | --- | ---
[▶|] Suivant | Passe séquence suivante | Immédiat, même si séquence actuelle pas terminée
[|◀] Précédent | Retour séquence précédente | Saut direct séquence avant (pas de logique "replay si >10s")
[⏸️] Pause | Pause temporaire | Reprend à position exacte
[▶️] Play | Reprend lecture | Continue position actuelle
Liste séquences | Navigation libre | Tap séquence → saut direct (même séquences non écoutées)

Interface liste séquences :

    ┌────────────────────────────────────────┐
    │ 🚶 Audio-guide Piéton                  │
    │ Musée du Louvre                        │
    ├────────────────────────────────────────┤
    │ [Cover image]                          │
    │                                        │
    │ ▶️ 0:00 ──●────────── 3:42            │
    │                                        │
    │ Séquence 3/12 : La Joconde            │
    │                                        │
    │ [|◀] [⏸️] [▶|]                        │
    │                                        │
    │ ────────────────────────────────────   │
    │                                        │
    │ 📋 Liste des séquences                 │
    │                                        │
    │ ✅ 1. Introduction (2:15)              │
    │    Écouté le 15/01/2026                │
    │                                        │
    │ ✅ 2. Pyramide du Louvre (1:48)        │
    │    Écouté le 15/01/2026                │
    │                                        │
    │ ▶️ 3. La Joconde (3:42) - EN COURS    │
    │    ──●──────────── 1:22/3:42          │
    │                                        │
    │ ⭕ 4. Vénus de Milo (2:58)             │
    │                                        │
    │ ⭕ 5. Code d'Hammurabi (4:12)          │
    │                                        │
    │ ⭕ 6. Victoire de Samothrace (3:25)    │
    │                                        │
    │ ... +6 séquences                       │
    │                                        │
    │ [Tout afficher ▼]                      │
    └────────────────────────────────────────┘

Navigation libre : - User peut sauter séquences déjà connues - User peut revenir en arrière à tout moment - User peut aller directement à séquence 8 (même si 4-7 non écoutées)

Sauvegarde progression : - Checkmarks ✅ sur séquences écoutées >80% - Position exacte sauvegardée dans séquence en cours

Justification : - Utilisateur contrôle 100% son rythme - Adapté musées : visitor peut voir physiquement une œuvre lointaine et vouloir écouter sa description - Pas de frustration (liberté totale)

### 16.3 Mode Voiture (GPS automatique)

Décision : GPS auto avec navigation manuelle conservée

#### 16.3.1 Déclenchement et contrôles

Distinction audio-guides vs contenus géolocalisés simples :

⚠️ Important : Les audio-guides multi-séquences fonctionnent différemment des contenus géolocalisés simples.

Type | Séquences | Déclenchement | Notification | Enchaînement | Comptabilité quota
--- | --- | --- | --- | --- | ---
Contenu géolocalisé simple | 1 séquence unique | Notification 7s avant (temps ETA) | Sonore + icône | Fin → retour buffer normal | 1 contenu = 1 quota
Audio-guide multi-séquences | 2 à 50 séquences | Au point GPS exact (distance 30m) | Ding + toast 2s | Séquences s'enchaînent auto | 1 audio-guide = 1 quota (toutes séquences)

Fonctionnement GPS automatique :

1. User démarre audio-guide en voiture (voir section 16.1 pour démarrage)
2. Séquence 1 démarre automatiquement au point GPS défini (rayon 30m)
3. Séquence 1 se termine
4. Affichage progress bar : distance temps réel + ETA jusqu'au prochain point
5. User roule vers point GPS suivant
6. Arrivée au point GPS suivant (rayon 30m) → déclenchement automatique séquence suivante
7. Notification sonore discrète : "Ding" (0.3s) + toast 2s : "Enclos des girafes"
8. Séquence suivante démarre immédiatement (pas de décompte)

Pas de système "7 secondes avant" pour les audio-guides : - Contrairement aux contenus géolocalisés simples (voir 05-interactions-navigation.md) - Les séquences se déclenchent au point GPS exact (rayon 30m) - Raison : expérience guidée continue, user sait qu'il suit un parcours

Navigation manuelle CONSERVÉE :

Bouton | État | Comportement
--- | --- | ---
[▶|] Suivant | ✅ Toujours actif | Passe séquence suivante immédiatement (même hors point GPS)
[|◀] Précédent | ✅ Toujours actif | Retour séquence précédente (même hors point GPS)
[⏸️] Pause | ✅ | Pause temporaire
Liste séquences | ✅ | Saut direct possible

Use cases navigation manuelle :

Situation | Solution manuelle
--- | ---
Embouteillage (séquence finie, point GPS loin) | User clique Suivant → avance manuellement
Point GPS inaccessible (route fermée) | User clique Suivant → skip point
Envie réécouter séquence précédente | User clique Précédent → retour
Passager manipule l'app | Passager navigue librement

Avertissement sécurité :

- Si vitesse >10 km/h ET user clique bouton (Suivant/Précédent) :
- Toast 3 secondes : "⚠️ Manipulation en conduite détectée. Pour votre sécurité, demandez à un passager."
- Action quand même exécutée (pas de blocage)
- Justification : sensibilisation sans bloquer (passager peut légitimement manipuler)

Schéma flux :

    Point GPS 1 (30m) → Séquence 1 AUTO → User roule → Distance affichée → Point GPS 2 (30m) → Séquence 2 AUTO
                                                        ↓
                                              User clique Suivant (manuel) → Séquence 2 immédiate

Justification : - Flexibilité maximale : GPS optimise expérience MAIS user garde contrôle - Gestion cas limites : routes fermées, détours, embouteillages - Sécurité : warning sensibilise sans bloquer (passager légitime)

#### 16.3.2 Affichage distance et guidage

Décision : Distance + direction (PAS de carte miniature)

Interface en conduite :

    ┌────────────────────────────────────────┐
    │ 🚗 Audio-guide Voiture                 │
    │ Safari du Paugre                       │
    ├────────────────────────────────────────┤
    │                                        │
    │ ▶️ 0:00 ──●────────── 2:15            │
    │                                        │
    │ Séquence 2/8 : Les lions               │
    │                                        │
    │ ────────────────────────────────────   │
    │                                        │
    │ 📍 Prochain point                      │
    │                                        │
    │    Enclos des girafes                  │
    │                                        │
    │ ┌────────────────────────────────┐    │
    │ │                                │    │
    │ │          ↗️                     │    │
    │ │      (direction)               │    │
    │ │                                │    │
    │ │       320 mètres               │    │
    │ │      ≈ 40 secondes              │    │
    │ │                                │    │
    │ └────────────────────────────────┘    │
    │                                        │
    │ Vitesse actuelle : 28 km/h             │
    │ Vitesse recommandée : 20-30 km/h       │
    │                                        │
    │ [|◀] [⏸️] [▶|] [📋 Liste]            │
    └────────────────────────────────────────┘

Affichage entre deux séquences :

Quand une séquence se termine et qu'il reste un point GPS suivant, l'interface bascule en mode "attente prochain point" :

    ┌────────────────────────────────────────┐
    │ 🚗 Audio-guide Voiture                 │
    │ Safari du Paugre                       │
    ├────────────────────────────────────────┤
    │                                        │
    │ ✅ Séquence 2/8 terminée               │
    │    Les lions                           │
    │                                        │
    │ ────────────────────────────────────   │
    │                                        │
    │ 📍 Prochain point                      │
    │                                        │
    │    Enclos des girafes                  │
    │                                        │
    │ ┌────────────────────────────────┐    │
    │ │         [Progress bar]         │    │
    │ │     ████████░░░░░░░░░ 65%      │    │
    │ │                                │    │
    │ │          ↗️                     │    │
    │ │      (direction)               │    │
    │ │                                │    │
    │ │       320 mètres               │    │
    │ │      ≈ 40 secondes              │    │
    │ │                                │    │
    │ └────────────────────────────────┘    │
    │                                        │
    │ Vitesse actuelle : 28 km/h             │
    │                                        │
    │ [|◀] [▶️ Rejouer séq.] [▶|]           │
    └────────────────────────────────────────┘

Progress bar dynamique : - Se remplit au fur et à mesure qu'on se rapproche du point - Calcul : `progress = 100 - (distance_actuelle / distance_initiale * 100)` - Exemple : distance initiale 500m, distance actuelle 175m → progress = 65% - Couleur : vert (#4CAF50) pour la partie remplie, gris (#E0E0E0) pour le reste

Bouton "Rejouer séq." : - Permet de réécouter la séquence qui vient de se terminer - User clique → séquence actuelle redémarre depuis 0:00 - Utile si distraction pendant l'écoute

Informations affichées :

Info | Mise à jour | Format
--- | --- | ---
Distance | Chaque seconde | "320 m" / "1.2 km"
ETA | Chaque seconde | "≈ 40 secondes" / "≈ 2 minutes"
Direction | Chaque 5s | Flèche indique direction (8 directions : ↑ ↗ → ↘ ↓ ↙ ← ↖)
Vitesse actuelle | Chaque seconde | "28 km/h"
Vitesse recommandée | Statique | "20-30 km/h" (définie par créateur)
Progress bar | Chaque seconde | Pourcentage parcouru vers prochain point

Calcul direction :

    // Calcul angle entre position actuelle et prochain point
    const currentGPS = getCurrentLocation();
    const nextPoint = audioGuide.sequences[currentIndex + 1].location;

    const angle = calculateBearing(currentGPS, nextPoint); // 0-360°

    // Conversion en flèche (8 directions)
    const arrows = ['↑', '↗', '→', '↘', '↓', '↙', '←', '↖'];
    const index = Math.round(angle / 45) % 8;
    const direction = arrows[index];

Calcul ETA :

    const distance = calculateDistance(currentGPS, nextPoint); // mètres
    const currentSpeed = getCurrentSpeed(); // km/h

    if (currentSpeed > 5) {
      const eta = (distance / 1000) / currentSpeed * 3600; // secondes
      return formatETA(eta); // "≈ 40 secondes" ou "≈ 2 minutes"
    } else {
      return "En attente de déplacement";
    }

Justification : - Distance + ETA = info essentielle sans surcharge visuelle - Direction (flèche) = aide se repérer sans carte complexe - Simplicité = moins distraction conducteur - Économie batterie (pas de rendu carte)

#### 16.3.3 Rayon de déclenchement et tolérance

Décision : Rayon configurable créateur avec défauts intelligents

Rayons par défaut :

Mode | Rayon déclenchement | Rayon "point manqué" | Justification
--- | --- | --- | ---
🚗 Voiture | 30 mètres | 100 mètres | Vitesse élevée = anticipation
🚴 Vélo | 50 mètres | 75 mètres | Vitesse variable, arrêts fréquents
🚌 Transport | 100 mètres | 150 mètres | Arrêts bus/train, moins précis

Configuration créateur :

- Curseur rayon : 10m → 200m
- Défaut pré-sélectionné selon mode choisi
- Preview visuel : cercle sur carte (lors création)
- Suggestion auto : "Recommandé : 30m pour voiture à 30 km/h"

Gestion point manqué :

    User passe à 110m du point GPS
    (hors rayon déclenchement 30m MAIS dans rayon tolérance 100m)
        ↓
    Toast : "⚠️ Point manqué : Enclos des girafes"
        ↓
    Popup 5 secondes :
    ┌────────────────────────────────────┐
    │ Point manqué                       │
    │                                    │
    │ "Enclos des girafes"               │
    │ Vous êtes passé à 110m du point    │
    │                                    │
    │ [🔊 Écouter quand même]            │
    │ [⏭️ Passer au suivant]             │
    │ [🔙 Faire demi-tour]               │
    └────────────────────────────────────┘

Actions popup :

Bouton | Comportement
--- | ---
Écouter quand même | Lance séquence immédiatement (même hors zone)
Passer au suivant | Skip séquence, continue vers prochain point
Faire demi-tour | Lance navigation GPS externe (Google Maps / Waze) vers point manqué

Si user au-delà rayon tolérance (>100m) : - Aucun popup (point trop loin, probablement hors itinéraire) - User peut naviguer manuellement (bouton Suivant)

Justification : - Flexibilité créateur (ajuste selon terrain, vitesse prévue) - Gestion intelligente imprévus (détours, routes fermées) - User pas bloqué (toujours moyen avancer)

### 16.4 Modes Vélo et Transport

Décision : Même logique voiture avec tolérances ajustées

Différences par rapport à mode voiture :

Paramètre | Voiture | Vélo | Transport
--- | --- | --- | ---
Rayon déclenchement | 30m | 50m | 100m
Rayon tolérance "point manqué" | 100m | 75m | 150m
Vitesse recommandée affichée | 20-50 km/h | 10-25 km/h | Variable (selon ligne)
Warning sécurité | >10 km/h | >5 km/h | Désactivé

Mode Vélo spécificités :

- Rayon plus large : vitesse variable, nombreux arrêts (feux, piétons)
- Warning sécurité dès 5 km/h (vélo en mouvement)
- Tolérance GPS moins stricte (tracé moins prévisible qu'auto)

Mode Transport spécificités :

- Rayon très large : arrêts fréquents (bus, train), ligne fixe
- Pas de warning sécurité (user = passager, pas conducteur)
- Vitesse recommandée = "Selon ligne" (pas de valeur fixe)
- Tolérance horaire : si bus en retard, point peut se déclencher avec 2-3 min de délai

Comportement identique voiture :

- Navigation manuelle conservée (boutons actifs)
- Affichage distance + ETA + direction
- Gestion point manqué
- Pub entre séquences

Justification : - Vélo : moins de contrôle qu'auto (obstacles, arrêts), nécessite tolérance - Transport : moins de contrôle utilisateur (suit ligne fixe), rayon large compense - Même UX globale = cohérence

### 16.5 Publicités dans audio-guides

Décision : Pub auto-play entre séquences TOUS modes

#### 16.5.1 Règles universelles

Insertion publicité :

- Fréquence : 1 pub toutes les 5 séquences (paramétrable admin 1/3 à 1/10)
- Gratuits uniquement, Premium 0 pub
- Pub s'enchaîne automatiquement après séquence
- Skippable après 5 secondes (règle standard RoadWave)
- Volume normalisé -14 LUFS (comme pubs normales)

Comportement MODE PIÉTON :

    Séquence 2 [fin]
        → Pub AUTO-PLAY
        → Pub se termine
        → PAUSE AUTO
        → Message "Séquence 3 prête. Appuyez sur Suivant."
        → User clique [▶|]
        → Séquence 3 démarre

Comportement MODE VOITURE/VÉLO/TRANSPORT :

    Séquence 2 [fin]
        → Pub AUTO-PLAY
        → Pub se termine
        → ATTENTE point GPS suivant OU user clique Suivant
        → Séquence 3 démarre

Schéma complet :

Mode | Après séquence normale | Après pub
--- | --- | ---
Piéton | Pause + attente user | Pause + attente user
Voiture | Attente GPS OU user clique Suivant | Attente GPS OU user clique Suivant
Vélo | Attente GPS OU user clique Suivant | Attente GPS OU user clique Suivant
Transport | Attente GPS OU user clique Suivant | Attente GPS OU user clique Suivant

Justification : - Monétisation équitable créateurs (tous modes participent) - Pub s'insère naturellement (auto-play, pas d'attente utilisateur) - User garde contrôle : piéton clique Suivant, voiture peut skip manuel - Premium reste attractif (expérience 0 interruption) - Modèle économique viable

#### 16.5.2 Métriques pub audio-guides

Dashboard créateur :

Métrique | Affichage
--- | ---
Impressions pub | Nombre de pubs insérées dans audio-guides
Écoutes complètes pub | Nombre de pubs écoutées >80%
Taux skip pub | % pubs skippées avant 5s vs après
Revenus pub audio-guides | 3€ / 1000 écoutes complètes (6% CA pub)

Distinction contenus normaux vs audio-guides : - Dashboard sépare : "Revenus contenus classiques" / "Revenus audio-guides" - Permet créateur voir performance par type

Justification : - Transparence créateur (comprend revenus) - Incite création audio-guides (nouvelle source revenus)

### 16.6 Reprise et sauvegarde progression

Décision : Sauvegarde complète automatique avec popup intelligente

#### 16.6.1 Sauvegarde automatique

Données sauvegardées :

Info | Détail | Utilité
--- | --- | ---
Audio-guide ID | Identifiant unique | Retrouver audio-guide
Séquence actuelle | Index (ex: 3/12) | Reprise position
Position dans séquence | Timestamp exact (ex: 1:42/3:20) | Reprise exacte
Séquences écoutées | Liste avec checkmarks ✅ | Historique progression
Date dernière écoute | Timestamp | Proposer reprise si <30j
GPS dernière position | Coordonnées optionnelles | Info contextuelle (non utilisée pour reprise)

Stockage :

Environnement | Technologie | Utilité
--- | --- | ---
Local | SQLite mobile | Fonctionnement offline
Cloud | PostgreSQL (sync auto) | Multi-device (reprendre sur autre appareil)

Synchronisation : - Sauvegarde locale : chaque fin de séquence + chaque 30s - Sync cloud : à la reconnexion réseau (batch)

Justification : - Expérience fluide (pas de perte progression) - Multi-device (démarrer sur iPhone, continuer sur iPad) - Offline-first (fonctionne sans réseau)

#### 16.6.2 Interface de reprise

Conditions popup : - Dernière écoute <30 jours - Progression >0% et <100% (pas terminé)

Popup reprise :

    ┌────────────────────────────────────────┐
    │ Reprendre l'audio-guide ?              │
    ├────────────────────────────────────────┤
    │ 🚗 Safari du Paugre                    │
    │ @safari_createur                       │
    │                                        │
    │ Progression : 3/8 séquences écoutées   │
    │ Dernière écoute : il y a 2 jours       │
    │                                        │
    │ Vous étiez à :                         │
    │ "Les lions" (1:42/3:20)                │
    │                                        │
    │ [▶️ Reprendre] [🔄 Recommencer]        │
    │ [📋 Voir toutes les séquences]         │
    └────────────────────────────────────────┘

Actions :

Bouton | Comportement
--- | ---
Reprendre | Continue séquence 3 à position 1:42 exacte
Recommencer | Reset progression, démarre séquence 1 depuis 0:00
Voir séquences | Affiche liste complète, user choisit séquence départ

Expiration progression : - Progression conservée 30 jours - Après 30j : popup "Audio-guide expiré. Recommencez depuis le début ?" - Suppression données progression (mais historique "écouté" préservé)

Justification : - Contexte clair : user sait exactement où il en est - Flexibilité : reprendre OU recommencer (choix utilisateur) - 30 jours = raisonnable pour tourisme multi-jours ou retour ultérieur

#### 16.6.3 Multi-device

Scénario :

1. User démarre audio-guide sur iPhone (séquences 1-3)
2. Progression sync cloud
3. Lendemain : user ouvre app sur iPad
4. Popup : "Reprendre Safari du Paugre sur cet appareil ?"
5. User clique Reprendre → continue séquence 4

Conflit de version : - Si modifications simultanées 2 appareils (rare) : dernière modification gagne - Toast : "Progression mise à jour depuis votre autre appareil"

Justification : - Confort utilisateur (change d'appareil librement) - Use case réel : planning trajet sur tablette, écoute sur smartphone en voiture

## Récapitulatif Section 16

Point | Décision | Coût | Complexité
--- | --- | --- | ---
16.1 Types audio-guides | 4 modes (piéton/voiture/vélo/transport) avec détection auto | 0€ | Moyenne
16.1.2 Création | Formulaire séquences + GPS + rayon + wizard guidé | 0€ | Moyenne
16.2.1 Piéton - Passages | Manuel AVEC pub auto-play entre séquences, pause après | 0€ | Faible
16.2.2 Piéton - Navigation | Liberté totale (skip, retour, saut direct liste) | 0€ | Faible
16.3.1 Voiture - Déclenchement | GPS auto + boutons manuels actifs (warning sécurité si >10 km/h) | 0€ | Moyenne
16.3.2 Voiture - Affichage | Distance + ETA + direction (flèche) + vitesse (PAS de carte) | 0€ | Faible
16.3.3 Voiture - Rayon | Configurable créateur (défauts 30m/50m/100m selon mode) | 0€ | Faible
16.4 Vélo & Transport | Mêmes règles avec tolérances ajustées + warning adapté | 0€ | Faible
16.5 Publicités | 1/5 séquences, auto-play TOUS modes, skippable 5s | 0€ | Faible
16.6.1 Sauvegarde | Complète (séquence + position + historique) local + cloud | 0€ | Faible
16.6.2 Reprise | Popup intelligente avec choix (reprendre/recommencer), expiration 30j | 0€ | Faible
16.6.3 Multi-device | Sync cloud PostgreSQL (reprendre sur autre appareil) | 0€ | Faible

Coût total MVP : 0€ (GPS natif, calcul distance PostGIS)

## Points d'attention pour Gherkin

- Tester 4 modes audio-guides (détection vitesse auto)
- Tester création séquences avec points GPS + rayon configurable
- Tester mode piéton : pause après séquence + pub auto-play + pause après pub + clic Suivant
- Tester navigation libre piéton (skip, retour, saut direct liste)
- Tester mode voiture : déclenchement GPS auto rayon 30m
- Tester navigation manuelle voiture : boutons actifs + warning si vitesse >10 km/h
- Tester affichage distance + ETA + direction (flèche 8 directions)
- Tester rayon tolérance "point manqué" (popup 3 actions)
- Tester mode vélo (rayon 50m) et transport (rayon 100m)
- Tester insertion pub 1/5 séquences tous modes avec auto-play
- Tester sauvegarde progression locale + sync cloud
- Tester popup reprise (3 boutons : reprendre/recommencer/voir liste)
- Tester expiration progression 30 jours
- Tester multi-device : démarrer iPhone, continuer iPad
- Tester gestion conflit progression simultanée 2 appareils

# Annexe : Fonctionnalités reportées Post-MVP

Date : 2026-01-19 Statut : Fonctionnalités validées mais reportées après le MVP

## Sommaire

1. Classification politique et équilibre éditorial
2. Système de pourboires créateurs

## 1. Classification politique et équilibre éditorial

> ⚠️ Reporté post-MVP pour raisons de coût, complexité et risques juridiques.

### Contexte du report

Raisons : - Coût modération : Classification manuelle humaine très coûteuse (~2000€/mois pour 1-2 modérateurs senior full-time) - Risque juridique : Accusations de biais éditorial, contentieux DSA - Complexité technique : Dashboard audit, logs 3 ans, alertes déséquilibre - Controverse : Peut créer polémique dès le lancement - Pas essentiel MVP : L'application fonctionne sans ce système

Version MVP (actuelle) : - Tag "Politique" simple (comme "Économie", "Sport") - Pas de classification gauche/droite - Pas d'équilibrage imposé - Option utilisateur "Masquer politique" → 0% contenus politiques

### Spécifications complètes (future implémentation)

Échelle de classification (5 niveaux) : - 🔴 Extrême gauche (anticapitalisme radical, révolution) - 🟠 Gauche (écologie, social, critique capitalisme modérée) - ⚪ Centre/Neutre (pas de positionnement politique clair) - 🔵 Droite (sécurité, tradition, économie libérale) - 🟣 Extrême droite (nationalisme radical, conservatisme extrême) - 🟢 Non politique (enfants, musique, fiction, culture générale)

Qui classifie : - ❌ Pas de classification automatique IA (outil informatif uniquement, jamais décisionnaire) - ✅ Modérateurs senior après transcription - ✅ Créateur peut contester via processus d'appel

Affichage : - Badge politique visible : au choix de l'utilisateur (paramètre "Afficher orientation politique") - Par défaut : badges masqués (UX neutre)

Règles de diffusion (équilibre imposé) :

Préférence utilisateur | Répartition | Justification
--- | --- | ---
Équilibré (défaut) | 35% gauche / 35% droite / 30% centre-neutre | Neutralité plateforme
Plutôt gauche | 50% gauche / 20% droite / 30% centre-neutre | Préférence respectée avec minimum opposition
Plutôt droite | 50% droite / 20% gauche / 30% centre-neutre | Préférence respectée avec minimum opposition
Masquer politique | 0% gauche / 0% droite / 100% centre-neutre + non politique | Option apolitique

Audit et conformité DSA : - Rapport hebdomadaire automatique : % gauche/droite/centre diffusé par utilisateur - Alerte si déséquilibre global plateforme (>55% d'un bord) - Logs conservés 3 ans (exigence Digital Services Act EU) - Dashboard admin : visualisation répartition temps réel

Sanctions mauvaise classification : - Classification volontairement incorrecte = Strike 1 - Récidive = Strike 2 (suspension 7j) - Détection via signalements utilisateurs + audit modération

Justification : - Conformité juridique DSA (obligation neutralité plateforme EU) - Protection contre accusations de biais éditorial - Transparence auditable - Coût : temps modération humaine (incompressible)

### Conditions de réintégration

Prérequis : 1. Base utilisateurs stable et revenus suffisants pour financer modération 2. Équipe modération dédiée (2+ modérateurs senior formés) 3. Dashboard admin audit DSA opérationnel 4. Système de logs et archivage 3 ans en place 5. Validation juridique du processus de classification

Chronologie estimée : - Phase 1 (Post-MVP+3 mois) : Validation demande utilisateurs via sondages - Phase 2 (Post-MVP+6 mois) : Recrutement modérateurs + développement dashboard - Phase 3 (Post-MVP+9 mois) : Tests bêta avec utilisateurs volontaires - Phase 4 (Post-MVP+12 mois) : Déploiement progressif si résultats positifs

## 2. Système de pourboires créateurs

> ⚠️ Reporté post-MVP - Fonctionnalité crypto (Lightning Network) prévue ultérieurement.

### Contexte du report

Raisons : - Complexité technique : Intégration Lightning Network, gestion wallets crypto - Réglementation : Incertitude juridique crypto en EU (MiCA 2025) - Focus MVP : Priorité sur monétisation via abonnements Premium et publicités - Adoption utilisateurs : Nécessite éducation et adoption crypto préalables

Version MVP (actuelle) : - Monétisation créateurs via : - Partage revenus publicités (3€ CPM) - 70% revenus abonnements Premium

### Spécifications complètes (future implémentation)

Système prévu : Micro-dons via Lightning Network (Bitcoin Layer 2)

Fonctionnement : 1. Auditeur peut envoyer pourboire pendant ou après écoute 2. Montants suggérés : 0.10€, 0.50€, 1€, 5€ (personnalisable) 3. Transaction instantanée via Lightning Network (frais <0.01€) 4. Créateur reçoit directement dans wallet Lightning 5. Conversion EUR/BTC automatique (optionnelle)

Avantages Lightning Network : - ✅ Frais quasi-nuls (<1%) vs 1.8% Mangopay - ✅ Transactions instantanées (<1 seconde) - ✅ Micropaiements possibles (dès 0.01€) - ✅ International sans frais supplémentaires - ✅ Pas d'intermédiaire (peer-to-peer)

Contraintes : - ❌ Adoption crypto limitée (2-5% population EU en 2026) - ❌ Volatilité BTC (nécessite conversion EUR immédiate) - ❌ UX complexe pour utilisateurs non-crypto - ❌ Réglementation MiCA en évolution

Alternatives étudiées : - Ko-fi / Buy Me a Coffee : simple mais frais 5% - PayPal/Stripe : frais 2.9% + 0.30€ (non viable pour micropaiements) - Mangopay : déjà utilisé, mais frais élevés pour petits montants

### Conditions de réintégration

Prérequis : 1. Réglementation MiCA stabilisée et conforme 2. Adoption crypto suffisante dans la base utilisateurs (>10%) 3. Intégration Lightning Network validée techniquement 4. UX simplifiée pour utilisateurs non-crypto (onboarding dédié) 5. Demande créateurs confirmée via sondages

Chronologie estimée : - Phase 1 (Post-MVP+6 mois) : Étude de marché et demande utilisateurs - Phase 2 (Post-MVP+12 mois) : Développement intégration Lightning - Phase 3 (Post-MVP+15 mois) : Tests bêta avec créateurs volontaires - Phase 4 (Post-MVP+18 mois) : Déploiement public si résultats positifs

## Autres fonctionnalités candidates Post-MVP

Liste non exhaustive de fonctionnalités évoquées mais non encore spécifiées :

- Mode offline avancé : Téléchargement automatique zones fréquentes
- Playlists collaboratives : Co-création de playlists géolocalisées
- API publique créateurs : Intégration RSS, podcasts existants
- Gamification : Badges, défis géolocalisés, leaderboards
- Mode nuit : Interface sombre automatique
- Statistiques avancées créateurs : Démographie, retention, heatmaps GPS

Ces fonctionnalités seront spécifiées et priorisées selon les retours utilisateurs MVP.

## Suivi et validation

Responsable : Product Owner Révision : Trimestrielle Critères de priorisation : 1. Demande utilisateurs (votes, sondages) 2. Impact business (revenus, rétention) 3. Faisabilité technique (complexité, ressources) 4. Conformité légale (RGPD, DSA, MiCA) 5. Différenciation concurrentielle

# Audio-guides multi-séquences pour piétons

> En tant qu'auditeur à pied Je veux profiter d'audio-guides structurés lors de mes visites Afin de découvrir des lieux de manière autonome et à mon rythme

29 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et que je suis connecté en tant qu'auditeur Et que je suis en mode piéton (vitesse <5 km/h)

## 1. Détection d'audio-guide à proximité

Étant donné que je me trouve à 80 mètres du Musée du Louvre Et que 3 audio-guides sont disponibles pour ce lieu

Quand le système détecte ma position

Alors je reçois une notification push:

## 2. Rayon de détection de 100m

Étant donné qu'un audio-guide est centré aux coordonnées GPS du Louvre

Quand je suis à exactement 100m du centre

Alors la notification est déclenchée Et quand je suis à 101m, aucune notification n'est envoyée

## 3. Page de sélection des audio-guides

Étant donné que j'ai tapé sur la notification audio-guide

Quand la page de sélection s'affiche

Alors je vois une liste de guides disponibles:

    | titre | créateur | nb_sequences | durée | note | écoutes |
    |---|---|---|---|---|---|
    | Visite complète | Créateur A | 12 | 45 min | 4.8 | 1.2K |
    | Œuvres majeures | Créateur B | 5 | 20 min | 4.9 | 3.5K |
    | Visite famille | Créateur C | 8 | 30 min | 4.7 | 850 |

## 4. Sélection d'un audio-guide

Étant donné que je suis sur la page de sélection

Quand je tape sur "Visite complète (45 min)"

Alors l'interface de lecture d'audio-guide s'ouvre Et la séquence 1 commence automatiquement Et je vois la liste complète des 12 séquences

## 5. Interface de lecture audio-guide

Étant donné que j'ai sélectionné un audio-guide de 12 séquences

Quand l'interface s'affiche

Alors je vois:

    | élément | exemple |
    |---|---|
    | Titre guide | 🎨 Visite complète • Musée du Louvre |
    | Piste actuelle | Piste 2/12 |
    | Titre séquence | "La Joconde - Histoire et mystères" |
    | Barre de progression | 3:24 / 6:50 |
    | Liste séquences | ✅ 1. Intro, ▶️ 2. Joconde, ⏸️ 3. Vénus... |
    | Boutons navigation | Précédent, Play/Pause, Suivant |

## 6. Navigation vers séquence suivante

Étant donné que j'écoute la séquence 2

Quand je tape sur "Suivant"

Alors la séquence 3 commence immédiatement Et le titre de la séquence s'affiche: "Vénus de Milo" Et la barre de progression se réinitialise

## 7. Navigation vers séquence précédente

Étant donné que j'écoute la séquence 5

Quand je tape sur "Précédent"

Alors la séquence 4 recommence depuis le début Et je peux réécouter cette séquence

## 8. Saut direct à une séquence spécifique

Étant donné que j'écoute la séquence 2 Et que la liste des séquences est affichée

Quand je tape sur "7. Peintures Renaissance"

Alors la séquence 7 démarre immédiatement Et je passe directement de la séquence 2 à la 7

## 9. Commande vocale "Suivant"

Étant donné que j'écoute la séquence 3

Quand je dis "Suivant" via la commande vocale

Alors la séquence 4 démarre Et la commande vocale fonctionne même si l'écran est verrouillé

## 10. Commande vocale "Précédent"

Étant donné que j'écoute la séquence 6

Quand je dis "Précédent" via la commande vocale

Alors la séquence 5 démarre depuis le début

## 11. Pause et reprise à la position exacte

Étant donné que j'écoute la séquence 4 à la position 2:30

Quand je mets en pause Et que j'attends 5 minutes Et que je reprends la lecture

Alors la séquence reprend exactement à 2:30 Et aucune donnée n'est perdue

## 12. Guidage vocal automatique entre séquences

Étant donné que la séquence 2 se termine

Quand la transition vers la séquence 3 se produit

Alors j'entends un message vocal: Et la séquence 3 ne démarre pas automatiquement (navigation manuelle)

## 13. Avertissement si éloignement du point d'intérêt

Étant donné que je suis dans le guide du Louvre Et que je devrais être devant la Vénus de Milo (séquence 3)

Quand je m'éloigne de plus de 50m de ce point

Alors j'entends un message vocal: Et un bouton "Voir le plan" apparaît dans l'interface

## 14. Sauvegarde automatique de la progression

Étant donné que j'écoute la séquence 5 à la position 1:45

Quand je ferme l'application brutalement Et que je la rouvre 10 minutes plus tard

Alors je vois une popup "Reprendre la visite du Musée du Louvre ?" Et si je choisis "Reprendre", je retourne à la séquence 5 à 1:45

## 15. Option de recommencer depuis le début

Étant donné que j'ai une progression sauvegardée à la séquence 7

Quand je rouvre le guide

Alors je vois 2 options:

    | option | action |
    |---|---|
    | Reprendre à la séquence 7 | Reprend à la position exacte |
    | Recommencer depuis le début | Retourne à la séquence 1 |

## 16. Expiration de la sauvegarde après 30 jours

Étant donné que j'ai une progression sauvegardée depuis 30 jours

Quand j'essaie de reprendre le guide

Alors la sauvegarde est considérée comme expirée Et je recommence depuis la séquence 1 Et je vois le message "Votre précédente visite date de plus de 30 jours. Recommençons depuis le début."

## 17. Synchronisation multi-device de la progression

Étant donné que j'écoute un guide sur mon iPhone à la séquence 4

Quand je ferme l'app et ouvre sur mon iPad

Alors je vois la progression synchronisée Et je peux reprendre à la séquence 4 sur l'iPad

## 18. Marquage "Terminé" après toutes les séquences

Étant donné que j'écoute la dernière séquence (12/12)

Quand cette séquence se termine

Alors le guide est marqué "✅ Terminé" dans mon historique Et je vois un message de félicitation: Et le créateur gagne les statistiques d'écoute complète

## 19. Création d'audio-guide par un créateur

Étant donné que je suis un créateur

Quand je crée un nouvel audio-guide

Alors je dois:

    | étape | détail |
    |---|---|
    | Uploader plusieurs fichiers | 1 fichier MP3 par séquence |
    | Numéroter les séquences | Séquence 1, Séquence 2, etc. |
    | Titrer chaque séquence | "Introduction", "La Joconde", etc. |
    | Définir point GPS unique | Centre du lieu (ex: Louvre) |
    | Définir rayon de détection | Par défaut 100m |

Et la durée totale est calculée automatiquement

## 20. Structure JSON de stockage audio-guide

Étant donné qu'un créateur publie un audio-guide du Louvre

Quand les métadonnées sont stockées en base

Alors le format JSON contient:

## 21. Limitation du nombre de séquences

Étant donné que je crée un audio-guide

Quand j'essaie d'ajouter plus de 50 séquences

Alors je vois le message "Maximum 50 séquences par audio-guide" Et je dois structurer mon contenu différemment ou créer plusieurs guides

## 22. Quitter le guide et sauvegarder

Étant donné que j'écoute la séquence 6

Quand je tape sur le bouton "×" (fermer)

Alors je vois une confirmation: Et si je confirme, la progression est enregistrée Et je retourne à l'écran principal

## 23. Statistiques créateur pour audio-guides

Étant donné que je suis créateur d'un audio-guide

Quand je consulte mes statistiques

Alors je vois:

    | métrique | exemple valeur |
    |---|---|
    | Nombre de démarrages | 1250 |
    | Nombre de complétions (100%) | 387 (31%) |
    | Séquence la plus skippée | Séquence 8 |
    | Durée moyenne d'écoute | 28 min (sur 45) |

## 24. Audio-guide multilingue (post-MVP)

Étant donné qu'un créateur peut publier plusieurs versions linguistiques

Quand un touriste anglophone visite le Louvre

Alors il voit les guides disponibles en anglais Et peut choisir parmi les guides traduits Mais cette fonctionnalité n'est pas disponible en MVP

## 25. Publicité entre séquences d'audio-guide

Étant donné que je suis un utilisateur gratuit Et que j'écoute un audio-guide

Quand je passe de la séquence 5 à la séquence 6

Alors une publicité peut être insérée (1 pub toutes les 5 séquences) Et la publicité est skippable après 5 secondes Et les utilisateurs Premium ne voient pas de publicité

## 26. Audio-guide en mode offline

Étant donné que j'ai téléchargé un audio-guide complet

Quand je visite le lieu sans connexion internet

Alors toutes les séquences sont disponibles hors ligne Et la navigation fonctionne normalement Et seule la sauvegarde cloud est différée jusqu'à reconnexion

## 27. Notation d'un audio-guide après écoute

Étant donné que j'ai terminé un audio-guide

Quand je ferme l'interface

Alors je vois une popup "Notez cette visite" Et je peux donner une note de 1 à 5 étoiles Et cette note contribue à la note globale visible par les autres utilisateurs

## 28. Filtrage par langue dans la page de sélection

Étant donné que plusieurs audio-guides sont disponibles en différentes langues

Quand j'accède à la page de sélection

Alors je peux filtrer par langue Et par défaut, les guides dans ma langue système sont affichés en premier

## 29. Réutilisation de l'infrastructure existante

Étant donné qu'un audio-guide est techniquement un contenu structuré

Alors il réutilise:

    | composant | usage |
    |---|---|
    | Stockage Bunny | Hébergement fichiers MP3 séquences |
    | Streaming HLS | Diffusion audio adaptative |
    | Cache Redis | Métadonnées guides + progressions |
    | PostgreSQL | Stockage structure JSON guides |

Et aucune infrastructure dédiée n'est nécessaire

# Impact des abonnements sur l'algorithme

> En tant qu'auditeur Je veux que les contenus de mes créateurs suivis soient favorisés Afin de ne pas rater leurs publications tout en découvrant de nouveaux contenus

16 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et que je suis connecté en tant qu'auditeur Et que je suis abonné au créateur "JeanDupont"

## 1. Boost de +30% appliqué au score final

Étant donné un contenu du créateur "JeanDupont" avec:

    | score_geo | 0.5 |
    |---|---|
    | score_interet | 0.6 |
    | score_engage | 0.5 |

Quand le score final est calculé

Alors le score de base est 0.53 Et le boost abonnement de +30% est appliqué Et le score final avec boost est 0.69

## 2. Contenu non-suivi peut battre contenu suivi

Étant donné que je suis à Paris Et que 2 contenus sont disponibles:

    | contenu | createur_suivi | score_geo | score_interet | score_engage | score_final_base | score_avec_boost |
    |---|---|---|---|---|---|---|
    | Contenu A | Non | 0.9 | 0.8 | 0.7 | 0.80 | 0.80 |
    | Contenu B | Oui | 0.5 | 0.6 | 0.5 | 0.53 | 0.69 |

Quand l'algorithme sélectionne le prochain contenu

Alors le Contenu A est proposé en premier

## 3. Contenu suivi remporte grâce au boost

Étant donné que je suis à Paris Et que 2 contenus sont disponibles:

    | contenu | createur_suivi | score_final_base | score_avec_boost |
    |---|---|---|---|
    | Contenu A | Non | 0.70 | 0.70 |
    | Contenu B | Oui | 0.60 | 0.78 |

Quand l'algorithme sélectionne le prochain contenu

Alors le Contenu B est proposé en premier

## 4. Contenu suivi avec faible engagement ne domine pas

Étant donné que je suis abonné au créateur "CreateurMoyen" Et qu'il publie un contenu avec très faible engagement (score 0.30) Et qu'un contenu viral d'un créateur non-suivi a un score de 0.85

Quand l'algorithme sélectionne le prochain contenu

Alors le contenu viral est proposé en premier (0.85)

## 5. Pas de file dédiée aux abonnements

Étant donné que je suis abonné à 50 créateurs

Quand l'algorithme génère ma file d'attente de 5 contenus

Alors les contenus suivis et non-suivis sont mélangés Et tous entrent en compétition selon leurs scores (avec boost si abonnement) Et il n'y a pas de section séparée "Contenus de vos abonnements"

## 6. Vérification du calcul du boost

Étant donné un contenu d'un créateur suivi Et que le score final de base est calculé à 0.65

Quand le boost abonnement est appliqué

Alors le multiplicateur utilisé est exactement 1.3 Et le score final avec boost est 0.845 (0.65 × 1.3) Et le résultat est arrondi à 2 décimales: 0.85

## 7. Boost appliqué à tous les contenus du créateur suivi

Étant donné que je suis abonné au créateur "JeanDupont" Et qu'il a publié 10 contenus différents

Quand l'algorithme évalue chacun de ces contenus

Alors le boost de +30% est appliqué à tous les 10 contenus Et chaque contenu bénéficie du même multiplicateur 1.3

## 8. Plusieurs créateurs suivis en compétition

Étant donné que je suis abonné à "Créateur A" et "Créateur B" Et que les 2 ont des contenus disponibles dans ma zone:

    | createur | score_base | score_avec_boost |
    |---|---|---|
    | Créateur A | 0.70 | 0.91 |
    | Créateur B | 0.65 | 0.85 |

Quand l'algorithme sélectionne le prochain contenu

Alors le contenu du Créateur A est proposé en premier (0.91 > 0.85) Et les 2 bénéficient du boost, mais le meilleur score gagne

## 9. Contenu national d'un créateur suivi

Étant donné que je suis abonné à "MediaNational" Et qu'il publie un contenu de type "National" (score_geo 0.2)

Quand le score est calculé avec:

    | score_geo | score_interet | score_engage |
    |---|---|---|
    | 0.2 | 0.7 | 0.6 |

Alors le score de base est environ 0.50 Et avec le boost abonnement, le score devient 0.65 Et le contenu peut être proposé malgré son score géo faible

## 10. Transparence du boost dans les paramètres

Quand j'accède aux paramètres de l'algorithme de recommandation

Alors je vois l'information: "Les contenus de vos créateurs suivis bénéficient d'un boost de +30%" Et je comprends que ce n'est pas une priorité absolue Et que la découverte de nouveaux contenus reste possible

## 11. Boost désactivé si désabonnement

Étant donné que je suis abonné au créateur "JeanDupont" Et qu'un de ses contenus bénéficiait du boost +30%

Quand je me désabonne de "JeanDupont"

Alors ses contenus n'ont plus le boost Et leur score revient au score de base sans multiplicateur

## 12. Contenu d'un créateur nouvellement suivi

Étant donné que je viens de m'abonner à "NouveauCreateur" Et qu'il a publié un contenu il y a 2 jours

Quand l'algorithme recalcule les scores

Alors le boost de +30% est immédiatement appliqué à ce contenu Et il peut apparaître dans ma prochaine file d'attente

## 13. Impact sur le taux de contenu suivi dans le feed

Étant donné que je suis abonné à 30 créateurs Et que j'écoute 100 contenus sur une semaine

Quand j'analyse la répartition

Alors environ 40-50% des contenus proviennent de créateurs suivis Et 50-60% proviennent de créateurs non-suivis (découverte)

## 14. Contenu suivi hors zone géographique

Étant donné que je suis à Paris Et que je suis abonné à un créateur de Marseille Et qu'il publie un contenu ancré à Marseille (hors de portée)

Quand l'algorithme évalue ce contenu

Alors le score géo est quasi nul (0.05) Et même avec boost +30%, le score reste très faible Et le contenu n'est probablement pas proposé

## 15. Performance de calcul du boost

Étant donné que je suis abonné à 100 créateurs Et que l'algorithme évalue 1000 contenus potentiels

Quand le calcul des scores avec boost est effectué

Alors le temps de calcul reste inférieur à 50ms Et la requête SQL utilise un JOIN sur la table abonnements

## 16. Boost combiné avec d'autres facteurs

Étant donné un contenu d'un créateur suivi Et que le contenu bénéficie aussi de:

    | facteur | impact |
    |---|---|
    | Score d'engagement élevé | +20% |
    | Contenu récent (<24h) | +10% |
    | Boost abonnement | +30% |

Quand le score final est calculé

Alors le boost abonnement s'applique au score final (après tous les autres calculs) Et les boosts ne s'additionnent pas, le boost abonnement est un multiplicateur final

# Limites d'abonnements et désabonnement

> En tant qu'auditeur Je veux gérer mes abonnements de manière équilibrée Afin de suivre mes créateurs préférés sans être submergé

27 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et que je suis connecté en tant qu'auditeur

## 1. Limite maximale de 200 abonnements

Étant donné que je suis abonné à 199 créateurs

Quand j'essaie de m'abonner à un 200ème créateur

Alors l'abonnement réussit Et je suis maintenant abonné à 200 créateurs

## 2. Impossible de dépasser 200 abonnements

Étant donné que je suis déjà abonné à 200 créateurs

Quand j'essaie de m'abonner à un nouveau créateur

Alors l'action échoue Et je vois le message:

## 3. Suggestion de désabonnement de créateurs inactifs

Étant donné que je suis abonné à 200 créateurs Et que j'essaie de m'abonner à un nouveau créateur

Quand je vois le message de limite atteinte

Alors je vois aussi une suggestion: Et un bouton "Désabonner" est proposé pour ce créateur

## 4. Liste triable des abonnements

Étant donné que je suis abonné à 150 créateurs

Quand j'accède à ma liste d'abonnements

Alors je peux trier par:

    | critère | ordre |
    |---|---|
    | Date d'abonnement | Plus récent / Plus ancien |
    | Nombre de contenus écoutés | Plus écoutés / Moins écoutés |
    | Dernière activité créateur | Plus récent / Plus ancien |
    | Ordre alphabétique | A-Z / Z-A |

## 5. Abonnement initial augmente les jauges de +5%

Étant donné que mes jauges d'intérêt sont:

    | catégorie | valeur initiale |
    |---|---|
    | Automobile | 60% |
    | Voyage | 55% |

Et qu'un créateur tague ses contenus "Automobile" et "Voyage"

Quand je m'abonne à ce créateur

Alors mes jauges évoluent:

    | catégorie | nouvelle valeur |
    |---|---|
    | Automobile | 65% (+5%) |
    | Voyage | 60% (+5%) |

## 6. Abonnement avec créateur ayant 3 tags

Étant donné qu'un créateur tague ses contenus:

    | tags |
    |---|
    | Automobile, Voyage, Technologie |

Et que mes jauges sont toutes à 50%

Quand je m'abonne à ce créateur

Alors les 3 jauges augmentent de +5%:

    | catégorie | nouvelle valeur |
    |---|---|
    | Automobile | 55% |
    | Voyage | 55% |
    | Technologie | 55% |

## 7. Désabonnement diminue les jauges de -5%

Étant donné que je suis abonné à un créateur avec tags "Politique" et "Économie" Et que mes jauges sont:

    | catégorie | valeur actuelle |
    |---|---|
    | Politique | 70% |
    | Économie | 65% |

Quand je me désabonne de ce créateur

Alors mes jauges évoluent:

    | catégorie | nouvelle valeur |
    |---|---|
    | Politique | 65% (-5%) |
    | Économie | 60% (-5%) |

## 8. Désabonnement sans confirmation

Étant donné que je consulte le profil d'un créateur suivi

Quand je clique sur "Se désabonner"

Alors le désabonnement est immédiat Et aucune popup de confirmation n'apparaît

## 9. Réabonnement possible immédiatement

Étant donné que je viens de me désabonner d'un créateur

Quand je consulte à nouveau son profil

Alors le bouton "S'abonner" est affiché Et je peux me réabonner immédiatement Et mes jauges augmentent à nouveau de +5%

## 10. Effet symétrique abonnement/désabonnement

Étant donné qu'un créateur a les tags "Musique" et "Culture" Et que ma jauge Musique est à 50%

Quand je m'abonne puis me désabonne immédiatement

Alors ma jauge revient exactement à 50% Et il n'y a pas de perte ou gain net

## 11. Abonnement ne dépasse pas 100% de jauge

Étant donné que ma jauge Automobile est à 97% Et qu'un créateur tague ses contenus "Automobile"

Quand je m'abonne à ce créateur

Alors ma jauge Automobile passe à 100% (limite max) Et l'augmentation effective est de +3% seulement

## 12. Désabonnement ne descend pas sous 0%

Étant donné que ma jauge Politique est à 3% Et que je suis abonné à un créateur avec tag "Politique"

Quand je me désabonne de ce créateur

Alors ma jauge Politique passe à 0% (limite min) Et la diminution effective est de -3% seulement

## 13. Créateur ne voit pas qui est abonné (privacy)

Étant donné que je suis abonné au créateur "JeanDupont"

Quand "JeanDupont" consulte ses statistiques

Alors il voit le nombre total d'abonnés (ex: "1,247 abonnés") Mais il ne voit pas la liste des utilisateurs abonnés Et mon identité reste privée

## 14. Créateur voit uniquement le nombre total d'abonnés

Étant donné que je suis créateur Et que j'ai 523 abonnés

Quand je consulte mes statistiques

Alors je vois "523 abonnés" Mais je ne peux pas:

    | action interdite |
    |---|
    | Voir la liste des abonnés |
    | Contacter mes abonnés individuellement |
    | Voir leurs profils |

## 15. Pas d'abonnement mutuel visible

Étant donné que je suis abonné au créateur "Alice" Et qu'"Alice" est abonnée à mon compte créateur

Quand je consulte le profil d'"Alice"

Alors je ne vois pas d'indication qu'elle est abonnée à moi Et il n'y a pas de badge "Abonné mutuellement"

## 16. Performance avec 200 abonnements

Étant donné que je suis abonné à 200 créateurs

Quand l'algorithme calcule ma recommandation

Alors la requête SQL utilise un JOIN sur la table abonnements Et la table est indexée sur user_id et creator_id Et le temps de calcul reste inférieur à 50ms

## 17. Impact sur la recommandation avec beaucoup d'abonnements

Étant donné que je suis abonné à 150 créateurs très actifs Et qu'ils publient collectivement 100 contenus par jour

Quand l'algorithme génère ma file de 5 contenus

Alors environ 60-70% des contenus proviennent de créateurs suivis (grâce au boost +30%) Mais 30-40% proviennent de nouveaux créateurs (découverte)

## 18. Notification de désabonnement au créateur (non implémenté)

Étant donné que je me désabonne d'un créateur

Alors le créateur ne reçoit aucune notification Et il ne peut pas savoir qui s'est désabonné

## 19. Statistiques d'abonnements pour l'utilisateur

Étant donné que je suis abonné à 87 créateurs

Quand j'accède à mes statistiques d'abonnements

Alors je vois:

    | métrique | exemple valeur |
    |---|---|
    | Nombre total d'abonnements | 87 / 200 |
    | Créateurs les plus écoutés | Top 10 avec % écoute |
    | Créateurs non écoutés depuis 6 mois | 12 créateurs |
    | Nouveaux contenus non écoutés | 23 contenus |

## 20. Recherche dans la liste d'abonnements

Étant donné que je suis abonné à 120 créateurs

Quand j'accède à ma liste d'abonnements

Alors je peux chercher par nom de créateur Et les résultats sont filtrés en temps réel Et je trouve rapidement un créateur spécifique

## 21. Export de la liste d'abonnements (RGPD)

Étant donné que je demande l'export de mes données

Quand l'export est généré

Alors la liste de mes abonnements est incluse:

## 22. Suppression compte utilisateur et impact sur abonnements

Étant donné que je suis abonné à 50 créateurs

Quand je supprime définitivement mon compte

Alors tous mes abonnements sont supprimés Et le compteur d'abonnés de chaque créateur est décrémenté de -1 Et les jauges n'existent plus (données supprimées)

## 23. Suppression compte créateur et impact sur abonnés

Étant donné que je suis abonné au créateur "Bob"

Quand "Bob" supprime son compte créateur

Alors je suis automatiquement désabonné Et mes jauges diminuent de -5% pour les tags de "Bob" Et je ne vois plus "Bob" dans ma liste d'abonnements

## 24. Limite 200 justifiée par usage réaliste

Étant donné que la moyenne d'abonnements sur YouTube est de ~50-100 chaînes Et que Twitter limite à 5000 follows (mais moyenne ~150)

Quand RoadWave fixe la limite à 200

Alors cela couvre largement 99% des utilisateurs Et évite les abus (comptes spam suivant tout le monde)

## 25. Table PostgreSQL optimisée pour abonnements

Étant donné la structure de table subscriptions:

Alors les requêtes d'abonnements sont O(1) avec index Et le count d'abonnés par créateur est rapide Et la vérification "est abonné ?" est instantanée

## 26. Détection d'abonnements abusifs

Étant donné qu'un utilisateur s'abonne à 200 créateurs en moins de 5 minutes

Quand le système détecte cette activité suspecte

Alors un rate limiting est appliqué (max 10 abonnements/minute) Et l'utilisateur voit "Trop d'actions rapides. Veuillez réessayer dans 1 minute" Et cela prévient les bots de spam

## 27. Badge créateur vérifié visible dans abonnements

Étant donné que je suis abonné à 3 créateurs dont 1 vérifié

Quand je consulte ma liste d'abonnements

Alors le créateur vérifié a un badge ✓ bleu Et les créateurs non vérifiés n'ont pas de badge

# Notifications contextuelles selon le mode de déplacement

> En tant qu'auditeur Je veux recevoir des notifications adaptées à mon contexte Afin d'être informé sans être distrait en conduisant

28 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et que je suis connecté en tant qu'auditeur Et que j'ai activé les notifications

## 1. Détection automatique du contexte en voiture

Étant donné que ma vitesse GPS est de 50 km/h

Quand le système détecte mon contexte

Alors je suis identifié comme "En voiture" Et les notifications push sont désactivées Et seules les notifications in-app sont actives

## 2. Détection automatique du contexte à pied

Étant donné que ma vitesse GPS est de 3 km/h

Quand le système détecte mon contexte

Alors je suis identifié comme "À pied" Et les notifications push sont activées Et l'interface tactile et vocale sont disponibles

## 3. Zone de transition 5-10 km/h

Étant donné que ma vitesse GPS varie entre 5 et 10 km/h

Quand le système détecte mon contexte

Alors un algorithme de lissage est appliqué sur 30 secondes Et le mode est déterminé selon la vitesse moyenne Et les changements de mode ne sont pas trop fréquents

## 4. Nouveau contenu créateur suivi - Mode voiture

Étant donné que je suis en voiture (vitesse >10 km/h) Et que je suis abonné au créateur "JeanDupont"

Quand "JeanDupont" publie un nouveau contenu dans ma zone

Alors je ne reçois pas de notification push Mais je vois un badge compteur in-app Et le contenu apparaît dans ma file avec boost +30%

## 5. Nouveau contenu créateur suivi - Mode piéton

Étant donné que je suis à pied (vitesse <5 km/h) Et que je suis abonné au créateur "JeanDupont" Et que je suis situé en Île-de-France

Quand "JeanDupont" publie un contenu géolocalisé en Île-de-France

Alors je reçois une notification push:

## 6. Live créateur suivi - Mode voiture

Étant donné que je suis en voiture Et que je suis abonné au créateur "RadioLive"

Quand "RadioLive" démarre un live dans ma zone

Alors je ne reçois pas de notification push Mais je vois un badge compteur in-app Et le live peut apparaître dans ma recommandation automatiquement

## 7. Live créateur suivi - Mode piéton

Étant donné que je suis à pied Et que je suis abonné au créateur "RadioLive" Et que je suis situé dans la zone du live

Quand "RadioLive" démarre un live

Alors je reçois une notification push:

## 8. Audio-guide disponible à proximité - Mode piéton

Étant donné que je suis à pied

Quand je passe à moins de 100m d'un lieu avec audio-guides

Alors je reçois une notification push:

## 9. Audio-guide disponible à proximité - Mode voiture

Étant donné que je suis en voiture

Quand je passe à moins de 100m d'un lieu avec audio-guides

Alors je reçois une notification audio (bip) Et une annonce vocale: "Audio-guide disponible" Mais pas de notification push (sécurité)

## 10. Filtrage géographique des notifications

Étant donné que je suis abonné au créateur "CreateurMarseille" Et que je suis situé à Paris

Quand "CreateurMarseille" publie un contenu ancré à Marseille

Alors je ne reçois pas de notification Et cela évite la frustration de contenus non écoutables

## 11. Contenu national notifie tous les abonnés

Étant donné que je suis abonné au créateur "MediaNational" Et que je suis situé n'importe où en France

Quand "MediaNational" publie un contenu de type "National"

Alors je reçois une notification (si mode piéton)

## 12. Limite de 10 notifications push par jour

Étant donné que je suis abonné à 50 créateurs actifs Et que j'ai déjà reçu 10 notifications push aujourd'hui

Quand un 11ème contenu est publié

Alors je ne reçois pas de notification push individuelle Mais une notification groupée: "🎧 3 nouveaux contenus de créateurs suivis"

## 13. Paramétrage de la limite quotidienne

Étant donné que la limite par défaut est de 10 notifications/jour

Quand j'accède aux paramètres de notifications

Alors je peux modifier la limite entre 5 et 20 Et si je choisis 15, je recevrai jusqu'à 15 notifications/jour

## 14. Mode silencieux nocturne par défaut

Étant donné que le mode silencieux est activé de 22h à 8h par défaut Et qu'il est 23h30

Quand un créateur suivi publie un contenu

Alors je ne reçois pas de notification push Mais les notifications sont empilées Et je les vois le lendemain matin à 8h01

## 15. Exception du mode silencieux pour les lives

Étant donné que le mode silencieux est activé (22h-8h) Et qu'il est 23h00 Et que j'ai activé "Notifications importantes uniquement" (lives uniquement)

Quand un créateur suivi démarre un live

Alors je reçois quand même la notification push du live

## 16. Désactivation complète des notifications

Étant donné que j'accède aux paramètres de notifications

Quand je désactive toutes les notifications

Alors je ne reçois plus aucune notification push Et les badges in-app sont également désactivés Et seule la recommandation algorithmique reste active

## 17. Notification "Nouveaux contenus" activée par défaut

Étant donné que je crée un nouveau compte Et que je m'abonne à mon premier créateur

Quand je consulte les préférences de notifications

Alors "Nouveaux contenus" est activé par défaut Et "Lives" est activé par défaut Et "Audio-guides proximité" est activé par défaut

## 18. Désactivation sélective par type de notification

Étant donné que j'ai activé toutes les notifications

Quand je désactive uniquement "Nouveaux contenus"

Alors je ne reçois plus de notifications pour nouveaux contenus Mais je reçois toujours les notifications de lives Et les notifications d'audio-guides restent actives

## 19. Notification groupée après limite dépassée

Étant donné que j'ai reçu 10 notifications push aujourd'hui Et que 5 nouveaux contenus sont publiés dans l'heure suivante

Quand la 11ème notification devrait être envoyée

Alors les 5 contenus sont regroupés en une seule notification:

## 20. Détail de la notification groupée

Étant donné que j'ai reçu une notification groupée "3 nouveaux contenus"

Quand je tape sur la notification

Alors l'app s'ouvre sur une liste des 3 contenus:

    | créateur | titre |
    |---|---|
    | JeanDupont | "Actualité du jour" |
    | MarieDurand | "Podcast économie" |
    | PaulMartin | "Anecdote historique" |

Et je peux choisir lequel écouter en premier

## 21. Personnalisation des plages horaires du mode silencieux

Étant donné que le mode silencieux est 22h-8h par défaut

Quand j'accède aux paramètres

Alors je peux modifier les heures: par exemple 23h-7h Et le mode silencieux s'applique dans la nouvelle plage horaire

## 22. Format notification nouveau contenu complet

Étant donné que je suis à pied Et qu'un créateur suivi publie un contenu

Quand je reçois la notification push

Alors elle contient:

    | élément | exemple |
    |---|---|
    | Emoji | 🎧 |
    | Créateur | JeanDupont |
    | Action | a publié |
    | Titre | "Les secrets du Louvre" |
    | CTA | Tap pour écouter |

## 23. Format notification live complet

Étant donné que je suis à pied Et qu'un créateur suivi démarre un live

Quand je reçois la notification push

Alors elle contient:

    | élément | exemple |
    |---|---|
    | Emoji | 🔴 |
    | Créateur | RadioLive |
    | Action | est en direct |
    | Titre | "Débat politique ce soir" |
    | CTA | Tap pour rejoindre |

## 24. Notification disparaît si contenu supprimé

Étant donné que j'ai reçu une notification pour un contenu Et que je n'ai pas encore tapé dessus

Quand le créateur supprime le contenu

Alors la notification est automatiquement retirée de mon centre de notifications Et si je tape dessus par erreur, je vois "Contenu non disponible"

## 25. Badge compteur in-app en mode voiture

Étant donné que je suis en voiture Et que 5 créateurs suivis publient des contenus

Quand j'ouvre l'application

Alors je vois un badge "5" sur l'onglet "Nouveautés" Et en consultant l'onglet, je vois les 5 nouveaux contenus Et le badge disparaît après consultation

## 26. Coût des notifications push Firebase

Étant donné que je reçois 10 notifications push par jour Et que je suis actif 365 jours par an

Quand le système calcule le coût

Alors 3650 notifications/an sont envoyées Et Firebase Cloud Messaging est gratuit jusqu'à plusieurs millions de notifications Et le coût reste 0€ pour le volume MVP/Growth

## 27. Deep link depuis notification push

Étant donné que je reçois une notification push pour un contenu

Quand je tape sur la notification

Alors l'app s'ouvre directement sur le contenu Et la lecture démarre automatiquement (si j'étais à pied)

## 28. Notification refusée si permissions désactivées au niveau OS

Étant donné que j'ai désactivé les notifications dans les paramètres iOS/Android

Quand un créateur suivi publie un contenu

Alors aucune notification push n'est envoyée Et l'app propose de réactiver les permissions dans les paramètres Mais les badges in-app continuent de fonctionner

# Création d'audio-guide multi-séquences

> En tant que créateur de contenu Je veux créer des audio-guides avec plusieurs séquences géolocalisées Afin d'offrir des expériences guidées adaptées aux différents modes de déplacement

35 scénarios (32 standards, 3 plans)

> Contexte commun à tous les scénarios

> Étant donné que l'application RoadWave est démarrée Et que le créateur "guide@example.com" est connecté Et que son compte est vérifié

## 1. 📋 Plan: Détection automatique du mode selon la vitesse

Étant donné que l'utilisateur se déplace à km/h

Quand la vitesse est calculée sur 30 secondes

Alors le mode est suggéré automatiquement

📊 Exemples de données:

vitesse | mode
--- | ---
3 | Piéton
15 | Vélo
35 | Voiture
50 | Voiture

## 2. Suggestion de mode au démarrage avec confirmation

Étant donné qu'un audio-guide "Safari du Paugre" est disponible Et que l'utilisateur se déplace à 35 km/h

Quand l'audio-guide démarre

Alors une popup s'affiche:

## 3. Changement manuel du mode détecté

Étant donné que le mode "Voiture" est suggéré automatiquement

Quand l'utilisateur clique sur "Changer"

Alors les 4 modes sont proposés:

    | mode | emoji |
    |---|---|
    | Piéton | 🚶 |
    | Voiture | 🚗 |
    | Vélo | 🚴 |
    | Transport | 🚌 |

## 4. 📋 Plan: Caractéristiques par mode de déplacement

Étant donné un audio-guide configuré en mode

Alors les paramètres suivants sont appliqués:

    | paramètre | valeur |
    |---|---|
    | Vitesse détection | <vitesse_detection> |
    | Déclenchement | <declenchement> |

📊 Exemples de données:

mode | vitesse_detection | declenchement
--- | --- | ---
Piéton | <5 km/h | Manuel (bouton Suivant)
Voiture | >10 km/h | Auto GPS + Manuel
Vélo | 5-25 km/h | Auto GPS + Manuel
Transport | Variable | Auto GPS + Manuel

## 5. Accès au formulaire de création d'audio-guide

Étant donné que le créateur est sur son dashboard

Quand il clique sur "Créer un audio-guide"

Alors le formulaire de création s'affiche Et le wizard guidé en 4 étapes est visible:

    | étape | description |
    |---|---|
    | 1 | Infos générales |
    | 2 | Ajout séquences |
    | 3 | Preview carte |
    | 4 | Validation modération |

## 6. Étape 1 - Informations générales obligatoires

Étant donné que le créateur est sur l'étape 1 du wizard

Quand il complète le formulaire

Alors les champs suivants sont obligatoires:

    | champ | contrainte |
    |---|---|
    | Titre | 5-100 caractères |
    | Description | 10-500 caractères |
    | Mode de déplacement | Choix parmi 4 |
    | Tags | 1-3 tags |
    | Classification âge | Tout public/13+/16+/18+ |

## 7. Sélection du mode de déplacement

Étant donné que le créateur crée un audio-guide

Quand il sélectionne le mode "🚗 Voiture (GPS auto + manuel)"

Alors le champ "Vitesse recommandée" s'affiche Et la plage suggérée est "30-50 km/h"

## 8. Validation du titre

Étant donné que le créateur entre un titre

Quand le titre contient moins de 5 caractères

Alors un message d'erreur "Minimum 5 caractères" s'affiche Et le bouton "Suivant" est désactivé

## 9. Validation de la description

Étant donné que le créateur entre une description

Quand la description contient 520 caractères

Alors un message d'erreur "Maximum 500 caractères" s'affiche Et les 20 caractères en trop sont surlignés en rouge

## 10. Étape 2 - Ajout de la première séquence

Étant donné que le créateur est sur l'étape 2 "Ajout séquences"

Quand il clique sur "Ajouter séquence"

Alors le formulaire de séquence s'affiche avec:

    | champ | requis | note |
    |---|---|---|
    | Titre séquence | ✅ | 5-80 caractères |
    | Audio | ✅ | Upload MP3/AAC, max 200 MB |
    | Point GPS | ✅* | *Sauf mode piéton |
    | Rayon déclenchement | ✅* | *Sauf mode piéton, 10-200m |

## 11. Ajout du point GPS pour une séquence

Étant donné que le créateur ajoute une séquence en mode "Voiture"

Quand il clique sur "📍 Ajouter point GPS"

Alors une carte s'affiche Et il peut:

    | action |
    |---|
    | Cliquer sur la carte |
    | Entrer coordonnées manuelles |
    | Utiliser sa position actuelle |

## 12. Configuration du rayon de déclenchement avec preview

Étant donné qu'un point GPS est défini à (43.1234, 2.5678)

Quand le créateur ajuste le curseur de rayon

Alors le rayon varie de 10m à 200m Et un cercle visuel est affiché sur la carte Et la valeur actuelle s'affiche "30m"

## 13. 📋 Plan: Rayon par défaut selon le mode

Étant donné un audio-guide en mode

Quand le créateur ajoute un point GPS

Alors le rayon par défaut est

📊 Exemples de données:

mode | rayon_defaut
--- | ---
Voiture | 30m
Vélo | 50m
Transport | 100m

## 14. Suggestion intelligente du rayon

Étant donné un audio-guide en mode "Voiture" avec vitesse recommandée 30 km/h

Quand le créateur ajoute un point GPS

Alors une suggestion s'affiche: "Recommandé : 30m pour voiture à 30 km/h"

## 15. Upload audio pour une séquence

Étant donné que le créateur crée une séquence "Introduction"

Quand il upload un fichier audio de 5 MB

Alors le fichier est vérifié:

    | vérification | règle |
    |---|---|
    | Format | MP3, AAC, M4A |
    | Taille max | 200 MB |
    | Durée max | 15 minutes |

## 16. Ordre des séquences modifiable

Étant donné un audio-guide avec 5 séquences:

    | ordre | titre |
    |---|---|
    | 1 | Introduction |
    | 2 | Les lions |
    | 3 | Les girafes |
    | 4 | Les éléphants |
    | 5 | Conclusion |

Quand le créateur glisse "Les éléphants" en position 2

Alors l'ordre devient:

    | ordre | titre |
    |---|---|
    | 1 | Introduction |
    | 2 | Les éléphants |
    | 3 | Les lions |
    | 4 | Les girafes |
    | 5 | Conclusion |

## 17. Nombre minimum de séquences requis

Étant donné un audio-guide avec seulement 1 séquence

Quand le créateur tente de passer à l'étape suivante

Alors un message d'erreur s'affiche: "Minimum 2 séquences requis" Et le bouton "Suivant" est désactivé

## 18. Nombre maximum de séquences

Étant donné un audio-guide avec 50 séquences

Quand le créateur tente d'ajouter une 51ème séquence

Alors un message d'erreur s'affiche: "Maximum 50 séquences par audio-guide" Et le bouton "+ Ajouter séquence" est désactivé

## 19. Étape 3 - Preview carte avec tracé et points

Étant donné un audio-guide avec 5 séquences géolocalisées

Quand le créateur accède à l'étape 3 "Preview carte"

Alors une carte Leaflet s'affiche Et les éléments suivants sont visibles:

    | élément | description |
    |---|---|
    | Markers numérotés | 1, 2, 3, 4, 5 sur chaque point |
    | Tracé entre points | Ligne pointillée connectant les points |
    | Cercles de déclenchement | Rayon visuel autour de chaque point |

## 20. Statistiques du parcours

Étant donné un audio-guide avec les séquences suivantes:

    | séquence | durée | distance_au_suivant |
    |---|---|---|
    | 1 | 2:15 | 150m |
    | 2 | 3:42 | 200m |
    | 3 | 4:10 | 320m |

Quand les statistiques sont calculées

Alors le résumé suivant est affiché:

    | métrique | valeur |
    |---|---|
    | Séquences | 3 complètes |
    | Durée totale | 10:07 |
    | Distance totale | 670m |

## 21. Modification d'une séquence depuis la carte

Étant donné que la preview carte est affichée

Quand le créateur clique sur le marker "2"

Alors une popup s'affiche avec:

    | information |
    |---|
    | Titre: "Les lions" |
    | Durée: 3:42 |
    | Rayon: 30m |
    | [✏️ Modifier] |
    | [🗑️ Supprimer] |

## 22. Zone de diffusion géographique

Étant donné un audio-guide avec des points dans Paris

Quand le créateur définit la zone de diffusion

Alors il peut choisir:

    | type | exemple |
    |---|---|
    | Polygon | Tracé manuel sur carte |
    | Ville | Paris (API Nominatim) |
    | Département | 75 - Paris |
    | Région | Île-de-France |

## 23. Étape 4 - Publication et validation modération

Étant donné un créateur qui publie ses 3 premiers audio-guides

Quand il clique sur "✅ Publier audio-guide"

Alors un message s'affiche:

## 24. Publication directe pour créateurs expérimentés

Étant donné un créateur ayant publié 5 audio-guides validés Et aucun strike actif

Quand il publie un nouvel audio-guide

Alors l'audio-guide est publié immédiatement Et il devient visible pour les utilisateurs Et aucune validation manuelle n'est requise

## 25. Mode piéton sans points GPS obligatoires

Étant donné un audio-guide en mode "🚶 Piéton"

Quand le créateur ajoute une séquence

Alors le champ "Point GPS" est optionnel Et le champ "Rayon déclenchement" est masqué Et un message info s'affiche: "Mode manuel : les séquences se déclenchent au clic utilisateur"

## 26. Sauvegarde brouillon automatique

Étant donné que le créateur édite un audio-guide depuis 5 minutes

Quand il ajoute une nouvelle séquence

Alors l'audio-guide est sauvegardé en brouillon automatiquement Et un toast "Brouillon sauvegardé" s'affiche brièvement

## 27. Reprise d'un brouillon

Étant donné un audio-guide en brouillon "Safari du Paugre" Et qu'il contient 3 séquences complètes

Quand le créateur retourne sur son dashboard

Alors le brouillon est visible avec le statut "📝 Brouillon" Et un bouton "Continuer" est disponible Et la progression "3/5 séquences" est affichée

## 28. Suppression d'un brouillon

Étant donné un audio-guide en brouillon

Quand le créateur clique sur "🗑️ Supprimer"

Alors une confirmation s'affiche:

## 29. Modification d'un audio-guide publié

Étant donné un audio-guide publié "Safari du Paugre"

Quand le créateur clique sur "✏️ Modifier"

Alors il peut modifier:

    | élément modifiable | élément non modifiable |
    |---|---|
    | Titre | Mode de déplacement |
    | Description | Points GPS |
    | Tags | Rayons déclenchement |
    | Séquences (ordre) |  |

Et un avertissement s'affiche: "Les modifications structurelles nécessitent une nouvelle publication"

## 30. Duplication d'un audio-guide existant

Étant donné un audio-guide publié "Visite Paris"

Quand le créateur clique sur "📋 Dupliquer"

Alors une copie est créée avec le titre "Visite Paris (copie)" Et toutes les séquences sont copiées Et le statut est "📝 Brouillon" Et le créateur peut modifier avant publication

## 31. Upload audio échoue (format non supporté)

Étant donné que le créateur upload un fichier "audio.wav"

Quand le format est vérifié

Alors un message d'erreur s'affiche: "Format non supporté. Utilisez MP3, AAC ou M4A" Et le fichier est rejeté

## 32. Upload audio échoue (taille trop grande)

Étant donné que le créateur upload un fichier de 250 MB

Quand la taille est vérifiée

Alors un message d'erreur s'affiche: "Fichier trop volumineux. Maximum 200 MB" Et le fichier est rejeté

## 33. Points GPS trop éloignés (alerte cohérence)

Étant donné un audio-guide en mode "Piéton" Et une séquence au Louvre (Paris)

Quand le créateur ajoute une séquence à Lyon

Alors un avertissement s'affiche:

## 34. Pas de connexion lors de la sauvegarde

Étant donné que le créateur édite un audio-guide Et que la connexion réseau est perdue

Quand il tente de sauvegarder

Alors le brouillon est sauvegardé localement Et un message s'affiche: "Sauvegarde locale. Sera synchronisée à la reconnexion" Et une icône "☁️ Hors ligne" s'affiche

## 35. Reprise après perte de connexion

Étant donné un brouillon sauvegardé localement

Quand la connexion réseau est rétablie

Alors le brouillon est synchronisé automatiquement Et un toast "✅ Audio-guide synchronisé" s'affiche

# Intégration audio-guides avec autres fonctionnalités

> En tant qu'utilisateur Je veux utiliser les audio-guides avec toutes les fonctionnalités de l'app Afin d'avoir une expérience complète et cohérente

39 scénarios (38 standards, 1 plan)

> Contexte commun à tous les scénarios

> Étant donné que l'application RoadWave est démarrée Et que l'utilisateur "jean@example.com" est connecté

## 1. Téléchargement complet d'un audio-guide

Étant donné un audio-guide "Visite du Louvre" avec 12 séquences

Quand l'utilisateur clique sur "⬇️ Télécharger pour écouter hors ligne"

Alors toutes les 12 séquences sont téléchargées Et les métadonnées (titres, descriptions, GPS) sont sauvegardées Et les images (cover, miniatures) sont mises en cache

## 2. Affichage de la progression du téléchargement

Étant donné qu'un téléchargement d'audio-guide est en cours

Quand l'utilisateur consulte l'état

Alors la progression s'affiche:

## 3. Téléchargement uniquement en WiFi (par défaut)

Étant donné que l'option "Télécharger uniquement en WiFi" est activée

Quand l'utilisateur lance un téléchargement sur réseau mobile

Alors un avertissement s'affiche:

## 4. Gestion de l'espace de stockage

Étant donné que l'appareil a 500 MB d'espace libre Et qu'un audio-guide pèse 380 MB

Quand l'utilisateur lance le téléchargement

Alors un avertissement s'affiche:

## 5. Liste des audio-guides téléchargés

Étant donné que l'utilisateur a téléchargé 3 audio-guides

Quand il accède à "Bibliothèque > Téléchargés"

Alors il voit:

    | audio_guide | taille | date_telechargement |
    |---|---|---|
    | Visite du Louvre | 380 MB | 2026-01-20 |
    | Safari du Paugre | 245 MB | 2026-01-18 |
    | Circuit Loire à Vélo | 520 MB | 2026-01-15 |

## 6. Lecture hors connexion complète

Étant donné qu'un audio-guide est téléchargé Et que l'utilisateur active le mode avion

Quand il lance l'audio-guide

Alors toutes les séquences sont lisibles Et les métadonnées sont accessibles Et les images s'affichent normalement Et la progression est sauvegardée localement

## 7. GPS fonctionne en mode avion (mode voiture)

Étant donné qu'un audio-guide voiture est téléchargé Et que le mode avion est activé (avec GPS actif)

Quand l'utilisateur se déplace

Alors les déclenchements GPS fonctionnent normalement Et la distance/ETA sont calculés

## 8. Suppression d'audio-guide téléchargé

Étant donné qu'un audio-guide téléchargé pèse 380 MB

Quand l'utilisateur clique sur "🗑️ Supprimer téléchargement"

Alors une confirmation s'affiche Et si confirmé, les 380 MB sont libérés Et l'audio-guide reste accessible en streaming

## 9. Mise à jour automatique si nouvelle version

Étant donné qu'un audio-guide téléchargé a été mis à jour par le créateur

Quand l'utilisateur se connecte en WiFi

Alors une notification s'affiche:

## 10. Ajout d'audio-guide à une playlist

Étant donné que l'utilisateur consulte un audio-guide

Quand il clique sur "➕ Ajouter à une playlist"

Alors ses playlists s'affichent:

    | playlist |
    |---|
    | 🗺️ Voyages en France |
    | 🏛️ Musées parisiens |
    | + Créer nouvelle playlist |

## 11. Comportement audio-guide dans une playlist

Étant donné une playlist contenant 2 audio-guides et 1 podcast

Quand la lecture atteint un audio-guide

Alors l'audio-guide démarre à la séquence 1 (ou progression sauvegardée) Et les séquences se jouent normalement

Quand l'audio-guide se termine (dernière séquence)

Alors le contenu suivant de la playlist démarre

## 12. Audio-guide marqué comme "Favori"

Étant donné qu'un utilisateur aime un audio-guide

Quand il clique sur "⭐ Ajouter aux favoris"

Alors l'audio-guide est ajouté à la section "Favoris" Et il est facilement accessible depuis le menu principal

## 13. Collections thématiques d'audio-guides

Étant donné que RoadWave propose des collections éditoriales

Quand l'utilisateur accède à "Collections"

Alors il voit des collections comme:

    | collection | nombre_audio_guides |
    |---|---|
    | 🏛️ Musées de France | 12 |
    | 🦁 Parcs animaliers | 8 |
    | 🚴 Circuits vélo | 15 |
    | 🚗 Routes touristiques | 10 |

## 14. Bouton partager sur page audio-guide

Étant donné qu'un utilisateur consulte un audio-guide

Quand il clique sur "⬆️ Partager"

Alors le menu de partage natif s'ouvre Et le lien généré est "https://roadwave.fr/share/ag/louvre_123"

## 15. Page web de partage pour audio-guide

Étant donné qu'un lien d'audio-guide partagé est ouvert sur le web

Quand la page se charge

Alors elle affiche:

    | élément | exemple |
    |---|---|
    | Cover image 16:9 | Photo du Louvre |
    | Titre | "Visite du Louvre" |
    | Créateur | "@art_guide ✓" |
    | Badge type | "🎧 Audio-guide • 12 séquences" |
    | Durée totale | "45 minutes" |
    | Mode | "🚶 Piéton" |
    | Description | Texte complet |
    | Preview séquence 1 | Player HTML5 (séquence intro) |
    | Carte avec points GPS | Leaflet avec 12 markers |
    | CTA téléchargement | Boutons App Store / Google Play |

## 16. Deep link vers audio-guide spécifique

Étant donné que l'app est installée Et qu'un lien "https://roadwave.fr/share/ag/louvre_123" est cliqué

Quand le système détecte l'app

Alors l'app s'ouvre directement sur l'audio-guide Et l'utilisateur peut démarrer immédiatement

## 17. Partage avec séquence spécifique

Étant donné qu'un utilisateur est sur la séquence 5 "La Joconde"

Quand il partage l'audio-guide

Alors le lien généré est "https://roadwave.fr/share/ag/louvre_123?seq=5" Et le destinataire est dirigé vers la séquence 5 directement

## 18. Note globale de l'audio-guide

Étant donné qu'un utilisateur termine un audio-guide

Quand la dernière séquence se termine

Alors une popup de notation s'affiche:

## 19. Note moyenne affichée sur la page

Étant donné qu'un audio-guide a reçu 150 notes Et que la moyenne est 4.3/5

Quand la page est affichée

Alors la note "⭐ 4.3 (150 avis)" est visible

## 20. Commentaires triés par pertinence

Étant donné qu'un audio-guide a 50 commentaires

Quand l'utilisateur consulte les avis

Alors les commentaires sont triés par défaut selon:

    | critère | poids |
    |---|---|
    | Note élevée | 30% |
    | Récent | 30% |
    | Likes reçus | 40% |

## 21. Réponse du créateur aux commentaires

Étant donné qu'un utilisateur laisse un commentaire négatif

Quand le créateur consulte son dashboard

Alors il peut répondre au commentaire Et sa réponse apparaît en dessous avec badge "Créateur"

## 22. Audio-guides similaires recommandés

Étant donné qu'un utilisateur termine "Visite du Louvre"

Quand il consulte les recommandations

Alors l'algorithme suggère des audio-guides basés sur:

    | critère | exemple |
    |---|---|
    | Tags similaires | #Art #Histoire #Musée |
    | Créateur identique | Autres audio-guides de @art_guide |
    | Localisation proche | Autres musées parisiens |
    | Mode de déplacement | Autres audio-guides piéton |

## 23. Suggestion géographique contextuelle

Étant donné qu'un utilisateur est à Paris (GPS détecté)

Quand il ouvre l'onglet "Audio-guides"

Alors les audio-guides parisiens sont mis en avant Et un filtre "🗺️ Autour de moi" est pré-appliqué

## 24. Badge "Populaire dans votre région"

Étant donné qu'un audio-guide a >100 écoutes dans la région Île-de-France Et que l'utilisateur est en Île-de-France

Quand l'audio-guide est affiché

Alors un badge "🔥 Populaire près de chez vous" est visible

## 25. Préchargement de la séquence suivante

Étant donné que la séquence 3 est en cours à 2:30/3:42

Quand il reste 60 secondes de lecture

Alors la séquence 4 est préchargée en arrière-plan Et la transition est instantanée (0 latence)

## 26. Buffer adaptatif selon connexion

Étant donné qu'un utilisateur est sur réseau 4G

Quand la séquence démarre

Alors 30 secondes d'audio sont bufferisées initialement Et le buffering continue en arrière-plan

## 27. 📋 Plan: Buffer selon qualité réseau

Étant donné qu'un utilisateur est sur réseau

Quand une séquence démarre

Alors secondes sont bufferisées

📊 Exemples de données:

reseau | buffer_secondes
--- | ---
WiFi | 60
5G | 45
4G | 30
3G | 20

## 28. Compression audio adaptative

Étant donné qu'un utilisateur est sur connexion lente (3G)

Quand une séquence est streamée

Alors le CDN sert la version 64 kbps (au lieu de 128 kbps) Et la qualité reste acceptable pour la voix

## 29. Cache intelligent des séquences jouées

Étant donné qu'un utilisateur a écouté les séquences 1-5

Quand il clique sur "Précédent" pour réécouter la séquence 4

Alors la séquence 4 est chargée depuis le cache local Et le chargement est instantané (pas de stream)

## 30. Nettoyage automatique du cache

Étant donné que le cache audio occupe 500 MB Et que la limite configurée est 300 MB

Quand le nettoyage automatique s'exécute

Alors les séquences les plus anciennes (non téléchargées) sont supprimées Et le cache revient à 280 MB

## 31. Tracking des événements clés

Étant donné qu'un utilisateur écoute un audio-guide

Quand il interagit avec l'application

Alors les événements suivants sont trackés:

    | événement | données |
    |---|---|
    | audio_guide_started | audio_guide_id, mode, user_id |
    | sequence_completed | sequence_id, completion_rate, duration |
    | audio_guide_completed | audio_guide_id, total_time, sequences_count |
    | point_gps_triggered | point_id, distance, auto_or_manual |
    | point_gps_missed | point_id, distance, action_taken |
    | paywall_displayed | audio_guide_id, sequence_number |
    | premium_conversion | source: audio_guide_paywall |

## 32. Heatmap des abandons par séquence

Étant donné qu'un audio-guide a été écouté 1000 fois

Quand le créateur consulte la heatmap

Alors il voit pour chaque séquence:

    | sequence | starts | completions | abandon_rate |
    |---|---|---|---|
    | 1 | 1000 | 950 | 5% |
    | 2 | 950 | 920 | 3% |
    | 3 | 920 | 850 | 8% |
    | ... | ... | ... | ... |
    | 12 | 650 | 580 | 11% |

## 33. Attribution GPS auto vs manuel

Étant donné un audio-guide voiture avec 8 points GPS

Quand les statistiques sont calculées

Alors le créateur voit:

    | mode_declenchement | nombre |
    |---|---|
    | GPS automatique | 542 |
    | Manuel | 123 |
    | Point manqué | 89 |

## 34. Audio-guide avec une seule séquence (edge case)

Étant donné un audio-guide avec seulement 1 séquence

Quand il est publié

Alors un avertissement s'affiche:

## 35. Séquence manquante ou corrompue

Étant donné qu'une séquence 5 a un fichier audio corrompu

Quand l'utilisateur tente de la lire

Alors un message d'erreur s'affiche Et un bouton "⏭️ Passer à la suivante" est disponible Et le créateur reçoit une notification de l'erreur

## 36. GPS désactivé puis réactivé en cours de route

Étant donné un audio-guide voiture en cours Et que l'utilisateur désactive le GPS

Quand il le réactive 10 minutes plus tard

Alors le déclenchement automatique reprend Et les points GPS manqués entre-temps ne déclenchent pas de popup

## 37. Modification d'audio-guide avec utilisateurs en cours

Étant donné qu'un audio-guide a 50 utilisateurs en cours d'écoute

Quand le créateur modifie une séquence

Alors les utilisateurs actuels conservent l'ancienne version Et les nouveaux utilisateurs obtiennent la nouvelle version Et un message informe les utilisateurs lors de la prochaine ouverture

## 38. Suppression d'audio-guide par le créateur

Étant donné qu'un audio-guide a 20 utilisateurs avec progression

Quand le créateur supprime l'audio-guide

Alors une confirmation stricte est demandée Et si confirmé, les progressions utilisateurs sont archivées (30 jours) Et l'audio-guide devient inaccessible

## 39. Signalement d'audio-guide pour contenu inapproprié

Étant donné qu'un utilisateur signale un audio-guide

Quand le signalement est modéré Et jugé valide

Alors l'audio-guide est dépublié temporairement Et le créateur reçoit une notification d'explication Et il peut corriger puis republier

# Audio-guide mode piéton (navigation manuelle)

> En tant qu'utilisateur à pied Je veux naviguer manuellement entre les séquences d'un audio-guide Afin de contrôler mon rythme de visite

29 scénarios (28 standards, 1 plan)

> Contexte commun à tous les scénarios

> Étant donné que l'application RoadWave est démarrée Et que l'utilisateur "jean@example.com" est connecté (gratuit) Et qu'un audio-guide piéton "Visite du Louvre" est disponible avec 12 séquences

## 1. Fin de séquence normale avec pause automatique

Étant donné que la séquence 1 "Introduction" est en cours de lecture

Quand la séquence se termine à 2:15

Alors le player se met en pause automatiquement Et le message suivant s'affiche: "Séquence 1 terminée. Appuyez sur Suivant quand vous êtes prêt." Et la barre de progression indique "1/12 complétée"

## 2. Passage manuel à la séquence suivante

Étant donné que la séquence 1 est terminée et le player en pause

Quand l'utilisateur appuie sur le bouton [▶|] "Suivant"

Alors la séquence 2 "Pyramide du Louvre" démarre immédiatement Et aucune latence n'est observée

## 3. Séquence avec publicité (1/5 séquences)

Étant donné que la séquence 5 se termine Et que c'est la 5ème séquence (1 pub toutes les 5)

Quand la séquence se termine

Alors la publicité s'enchaîne automatiquement (sans attente bouton) Et la publicité se lit normalement Et elle est skippable après 5 secondes

## 4. Fin de publicité avec pause automatique

Étant donné qu'une publicité est en cours de lecture

Quand la publicité se termine

Alors le player se met en pause automatiquement Et le message suivant s'affiche: "Séquence 6 prête. Appuyez sur Suivant." Et l'utilisateur doit cliquer sur [▶|] pour continuer

## 5. Flux complet séquence → pub → séquence

Étant donné que la séquence 5 démarre

Quand la séquence 5 se termine

Alors la publicité démarre automatiquement

Quand la publicité se termine

Alors le player se met en pause

Quand l'utilisateur clique sur [▶|]

Alors la séquence 6 démarre

## 6. 📋 Plan: Fréquence de publicité configurable

Étant donné que l'utilisateur gratuit écoute un audio-guide Et que la fréquence pub est configurée à

Quand il termine la séquence

Alors une publicité est insérée :

📊 Exemples de données:

frequence | numero_sequence | pub_inseree
--- | --- | ---
1/5 | 5 | Oui
1/5 | 10 | Oui
1/5 | 4 | Non
1/3 | 3 | Oui
1/3 | 6 | Oui

## 7. Utilisateur Premium sans publicités

Étant donné que l'utilisateur "premium@example.com" est abonné Premium Et qu'il écoute un audio-guide piéton

Quand il termine la séquence 5

Alors aucune publicité n'est insérée Et le player se met en pause immédiatement Et le message "Séquence 6 prête. Appuyez sur Suivant." s'affiche

## 8. Boutons de contrôle disponibles en mode piéton

Étant donné qu'un audio-guide piéton est en lecture

Quand l'utilisateur consulte les contrôles

Alors les boutons suivants sont visibles:

    | bouton | fonction |
    |---|---|
    | [▶\ | ] Suivant | Passe à la séquence suivante |
    | [\ | ◀] Précédent | Retour à la séquence précédente |
    | [⏸️] Pause | Pause temporaire |
    | [▶️] Play | Reprend la lecture |
    | [📋] Liste | Affiche toutes les séquences |

## 9. Passage à la séquence suivante pendant la lecture

Étant donné que la séquence 3 "La Joconde" est en cours à 1:42/3:42

Quand l'utilisateur clique sur [▶|] "Suivant"

Alors la séquence 4 "Vénus de Milo" démarre immédiatement Et la séquence 3 n'est pas marquée comme écoutée (car <80%)

## 10. Retour à la séquence précédente (saut direct)

Étant donné que la séquence 5 est en cours de lecture

Quand l'utilisateur clique sur [|◀] "Précédent"

Alors la séquence 4 démarre depuis le début (0:00) Et il n'y a pas de logique "replay si >10s" (contrairement au contenu classique)

## 11. Pause et reprise pendant une séquence

Étant donné que la séquence 2 est en cours à 1:15/1:48

Quand l'utilisateur clique sur [⏸️] "Pause"

Alors la lecture se met en pause Et la position 1:15 est conservée

Quand l'utilisateur clique sur [▶️] "Play"

Alors la lecture reprend exactement à 1:15

## 12. Interface liste des séquences

Étant donné qu'un audio-guide de 12 séquences est en cours

Quand l'utilisateur clique sur [📋] "Liste séquences"

Alors une liste complète s'affiche avec:

    | élément | exemple |
    |---|---|
    | Numéro et titre | "3. La Joconde" |
    | Durée | (3:42) |
    | État | ✅ Écouté / ▶️ En cours / ⭕ À écouter |
    | Date écoute (si écouté) | "Écouté le 15/01/2026" |

## 13. Séquence en cours dans la liste

Étant donné que la séquence 3 est en cours à 1:22/3:42

Quand la liste des séquences est affichée

Alors la séquence 3 affiche:

## 14. Navigation libre vers séquence non encore écoutée

Étant donné que l'utilisateur est sur la séquence 3 Et que les séquences 4 à 12 n'ont pas été écoutées

Quand l'utilisateur clique sur "8. Les Appartements de Napoléon"

Alors la séquence 8 démarre immédiatement depuis 0:00 Et les séquences 4 à 7 restent marquées ⭕ "À écouter"

## 15. Retour à une séquence déjà écoutée

Étant donné que la séquence 2 "Pyramide du Louvre" a été écoutée à 100% Et qu'elle est marquée ✅ "Écouté"

Quand l'utilisateur clique dessus dans la liste

Alors la séquence 2 démarre depuis 0:00 Et le statut ✅ est conservé

## 16. Checkmarks sur séquences écoutées >80%

Étant donné que l'utilisateur écoute la séquence 2 de durée 1:48

Quand il écoute jusqu'à 1:30 (83% de complétion) Et qu'il passe à la séquence suivante

Alors la séquence 2 est marquée ✅ "Écouté" Et la date d'écoute est enregistrée

## 17. Pas de checkmark si séquence écoutée <80%

Étant donné que l'utilisateur écoute la séquence 3 de durée 3:42

Quand il écoute jusqu'à 1:30 (40% de complétion) Et qu'il passe à la séquence suivante

Alors la séquence 3 reste marquée ⭕ "À écouter"

## 18. Bouton "Tout afficher" si plus de 6 séquences

Étant donné un audio-guide avec 12 séquences

Quand la liste est affichée

Alors seules les 6 premières séquences sont visibles initialement Et un bouton "Tout afficher ▼" est présent

Quand l'utilisateur clique sur "Tout afficher ▼"

Alors les 6 séquences restantes sont affichées

## 19. Saut vers séquence spécifique depuis la barre de progression

Étant donné qu'un audio-guide est en cours

Quand l'utilisateur clique sur "3/12" dans la barre de progression

Alors la liste des séquences s'ouvre Et la séquence en cours (3) est mise en surbrillance

## 20. Position exacte sauvegardée automatiquement

Étant donné que la séquence 5 est en cours à 2:34/4:10

Quand l'utilisateur quitte l'application

Alors la position 2:34 dans la séquence 5 est sauvegardée Et la sauvegarde est effectuée localement (SQLite) Et la sauvegarde est synchronisée sur le cloud (PostgreSQL)

## 21. Reprise après fermeture de l'application

Étant donné que l'utilisateur a quitté l'app à la séquence 5 position 2:34

Quand il rouvre l'audio-guide

Alors une popup de reprise s'affiche

Quand il clique sur "▶️ Reprendre"

Alors la lecture reprend à la séquence 5 position 2:34 exacte

## 22. Visiteur qui connaît déjà certaines œuvres

Étant donné qu'un visiteur du Louvre démarre l'audio-guide Et qu'il connaît déjà "La Joconde" (séquence 3)

Quand il arrive à la séquence 3 Et qu'il clique sur [▶|] "Suivant" après 10 secondes

Alors la séquence 4 démarre immédiatement Et la séquence 3 n'est pas marquée comme écoutée

## 23. Visiteur qui veut voir une œuvre éloignée

Étant donné qu'un visiteur est à la séquence 2 Et qu'il aperçoit "La Victoire de Samothrace" (séquence 8) physiquement

Quand il ouvre la liste et clique sur la séquence 8

Alors la séquence 8 démarre immédiatement Et il peut écouter la description même si les séquences 3-7 ne sont pas écoutées

## 24. Visiteur qui prend une pause café

Étant donné qu'un visiteur écoute la séquence 6

Quand il clique sur [⏸️] "Pause" Et qu'il ferme l'application pendant 30 minutes Quand il rouvre l'application

Alors la séquence 6 reprend à la position exacte où il s'était arrêté

## 25. Visiteur qui revient le lendemain

Étant donné qu'un visiteur a écouté les séquences 1-5 hier Et qu'il revient au musée aujourd'hui

Quand il ouvre l'audio-guide

Alors une popup propose "▶️ Reprendre" (séquence 6) Et les séquences 1-5 sont marquées ✅ "Écouté"

## 26. Séquence audio corrompue ou indisponible

Étant donné que la séquence 7 a un fichier audio corrompu

Quand l'utilisateur tente de la lire

Alors un message d'erreur s'affiche:

## 27. Perte de connexion pendant le chargement

Étant donné que l'utilisateur lance la séquence 4 Et que la connexion réseau est perdue

Quand le chargement échoue

Alors un message s'affiche: "Connexion perdue. Vérifiez votre réseau." Et un bouton "🔄 Réessayer" est disponible

## 28. Batterie faible en cours de visite

Étant donné que la batterie de l'appareil est à 5%

Quand l'utilisateur écoute une séquence

Alors une notification système s'affiche: "Batterie faible. Progression sauvegardée." Et la position est sauvegardée localement toutes les 10 secondes

## 29. Mode piéton sans points GPS (pas d'alerte localisation)

Étant donné un audio-guide en mode piéton Et que le GPS est désactivé

Quand l'utilisateur démarre l'audio-guide

Alors aucune alerte GPS ne s'affiche Et l'audio-guide fonctionne normalement (navigation 100% manuelle)

# Audio-guide mode voiture (GPS automatique)

> En tant qu'utilisateur en voiture Je veux que les séquences se déclenchent automatiquement selon ma position GPS Afin de profiter d'une expérience guidée hands-free

45 scénarios (40 standards, 5 plans)

> Contexte commun à tous les scénarios

> Étant donné que l'application RoadWave est démarrée Et que l'utilisateur "jean@example.com" est connecté (gratuit) Et qu'un audio-guide voiture "Safari du Paugre" est disponible avec 8 séquences Et que le GPS est activé

## 1. Distinction audio-guides vs contenus géolocalisés simples

Étant donné que l'utilisateur est en mode voiture

Quand il écoute un contenu géolocalisé simple (1 séquence unique)

Alors une notification avec compteur 7→1 est affichée 7s avant le point Et il doit valider avec "Suivant" + décompte 5s Et ce contenu compte 1/6 dans le quota horaire

Quand il démarre un audio-guide multi-séquences

Alors les séquences se déclenchent au point GPS exact (rayon 30m) Et aucun compteur 7s n'est affiché (juste notification "Ding" + toast 2s) Et l'audio-guide entier compte 1/6 dans le quota

## 2. Démarrage automatique au premier point GPS

Étant donné que l'utilisateur démarre l'audio-guide "Safari du Paugre" Et que le point de départ est à (43.1234, 2.5678) avec rayon 30m

Quand l'utilisateur entre dans le rayon de 30m

Alors la séquence 1 "Introduction - Point d'accueil" démarre automatiquement Et une notification sonore "Ding" est jouée (non intrusif) Et un toast s'affiche brièvement pendant 2s: "Introduction - Point d'accueil" Et aucun compteur 7→1 n'est affiché (contrairement aux contenus géolocalisés simples)

## 3. Déclenchement automatique séquence suivante

Étant donné que la séquence 1 est terminée Et que l'utilisateur se déplace vers le point GPS 2 (43.1245, 2.5690)

Quand l'utilisateur entre dans le rayon de 30m du point 2

Alors la séquence 2 "Enclos des lions" démarre automatiquement Et une notification "Ding" + toast "Enclos des lions" s'affiche

## 4. Navigation manuelle conservée (bouton Suivant actif)

Étant donné que la séquence 1 est en cours Et que l'utilisateur est encore loin du point GPS 2 (distance 500m)

Quand l'utilisateur clique sur [▶|] "Suivant"

Alors la séquence 2 démarre immédiatement Et aucune vérification GPS n'est effectuée

## 5. Navigation manuelle conservée (bouton Précédent actif)

Étant donné que la séquence 3 est en cours

Quand l'utilisateur clique sur [|◀] "Précédent"

Alors la séquence 2 démarre depuis le début Et aucune vérification GPS n'est effectuée

## 6. Tous les boutons de contrôle restent actifs

Étant donné qu'un audio-guide voiture est en cours

Quand l'utilisateur consulte les contrôles

Alors les boutons suivants sont actifs:

    | bouton | état | comportement |
    |---|---|---|
    | [▶\ | ] Suivant | ✅ | Passe séquence suivante immédiate |
    | [\ | ◀] Précédent | ✅ | Retour séquence précédente |
    | [⏸️] Pause | ✅ | Pause temporaire |
    | [📋] Liste | ✅ | Saut direct possible |

## 7. Use case - Embouteillage (séquence finie, point GPS loin)

Étant donné que la séquence 3 "Enclos des girafes" est terminée Et que le point GPS 4 est à 2 km de distance (embouteillage)

Quand l'utilisateur clique manuellement sur [▶|] "Suivant"

Alors la séquence 4 démarre immédiatement Et l'utilisateur peut continuer l'expérience sans attendre d'atteindre le point GPS

## 8. Use case - Route fermée (point GPS inaccessible)

Étant donné que le point GPS 5 est sur une route fermée Et que l'utilisateur ne peut pas s'en approcher

Quand l'utilisateur clique sur [▶|] "Suivant"

Alors la séquence 5 démarre quand même Et l'audio-guide continue normalement

## 9. Use case - Passager manipule l'application

Étant donné que l'utilisateur est passager (non conducteur) Et que la vitesse du véhicule est 45 km/h

Quand le passager clique sur [▶|] "Suivant"

Alors la séquence suivante démarre Et un avertissement s'affiche pendant 3 secondes

## 10. Avertissement sécurité si vitesse >10 km/h

Étant donné que la vitesse actuelle est 35 km/h

Quand l'utilisateur clique sur un bouton (Suivant ou Précédent)

Alors l'action est exécutée immédiatement (pas de blocage) Et un toast s'affiche pendant 3 secondes:

## 11. 📋 Plan: Avertissement selon la vitesse

Étant donné que la vitesse actuelle est km/h

Quand l'utilisateur clique sur un bouton de navigation

Alors l'avertissement est affiché :

📊 Exemples de données:

vitesse | avertissement
--- | ---
5 | Non
10 | Non
11 | Oui
35 | Oui
90 | Oui

## 12. Affichage entre deux séquences avec progress bar

Étant donné que la séquence 2 "Les lions" vient de se terminer Et que le prochain point GPS 3 "Enclos des girafes" est à 500m

Quand l'interface bascule en mode "attente prochain point"

Alors l'écran affiche:

    | élément | description |
    |---|---|
    | Statut séquence | "✅ Séquence 2/8 terminée" |
    | Nom séquence | "Les lions" |
    | Progress bar | Barre dynamique remplie selon distance (0%) |
    | Distance prochain point | "500 mètres" |
    | ETA | "≈ 1 minute 30" |
    | Direction | ↗️ |
    | Vitesse actuelle | "28 km/h" |
    | Bouton "Rejouer séq." | Permet de réécouter la séquence qui vient de finir |

## 13. Progress bar dynamique vers le prochain point

Étant donné que la distance initiale vers le prochain point était 500m Et que la séquence précédente est terminée

Quand l'utilisateur se rapproche du prochain point Et que la distance actuelle est 175m

Alors la progress bar affiche "65%" remplie Et le calcul est: 100 - (175 / 500 * 100) = 65% Et la barre se met à jour chaque seconde

## 14. Bouton "Rejouer séq." pour réécouter

Étant donné que la séquence 3 vient de se terminer Et que l'interface "attente prochain point" est affichée

Quand l'utilisateur clique sur [▶️ Rejouer séq.]

Alors la séquence 3 redémarre depuis 0:00 Et l'utilisateur peut la réécouter (utile si distraction)

## 15. Interface en conduite avec distance et ETA

Étant donné que la séquence 2 est en cours Et que le prochain point GPS 3 "Enclos des girafes" est à 320m Et que la vitesse actuelle est 28 km/h

Quand l'interface est affichée

Alors les informations suivantes sont visibles:

    | information | valeur |
    |---|---|
    | Nom prochain point | "Enclos des girafes" |
    | Distance | "320 mètres" |
    | ETA | "≈ 40 secondes" |
    | Direction | ↗️ (flèche direction) |
    | Vitesse actuelle | "28 km/h" |
    | Vitesse recommandée | "20-30 km/h" |

## 16. Mise à jour de la distance en temps réel

Étant donné que la distance au prochain point est 500m

Quand 10 secondes s'écoulent et que l'utilisateur se rapproche

Alors la distance est mise à jour chaque seconde Et la nouvelle distance "450m" s'affiche

## 17. Mise à jour de l'ETA en temps réel

Étant donné que l'ETA est "≈ 2 minutes" Et que la vitesse est constante à 30 km/h

Quand l'utilisateur se rapproche du point

Alors l'ETA est recalculé chaque seconde Et il diminue progressivement: "≈ 1 minute 50", "≈ 1 minute 40", etc.

## 18. 📋 Plan: Format d'affichage de la distance

Étant donné que la distance au prochain point est

Quand l'interface est mise à jour

Alors la distance affichée est ""

📊 Exemples de données:

distance_metres | affichage
--- | ---
50 | 50 m
320 | 320 m
980 | 980 m
1200 | 1.2 km
5400 | 5.4 km

## 19. 📋 Plan: Format d'affichage de l'ETA

Étant donné que l'ETA calculé est secondes

Quand l'interface est mise à jour

Alors l'ETA affiché est ""

📊 Exemples de données:

secondes | affichage
--- | ---
30 | ≈ 30 secondes
75 | ≈ 1 minute
150 | ≈ 2 minutes
400 | ≈ 6 minutes

## 20. Calcul de la direction (flèche 8 directions)

Étant donné que la position actuelle est (43.1234, 2.5678) Et que le prochain point est au nord-est (angle 45°)

Quand la direction est calculée

Alors la flèche "↗" est affichée

## 21. 📋 Plan: Flèches de direction selon l'angle

Étant donné que l'angle vers le prochain point est °

Quand la direction est calculée

Alors la flèche "" est affichée

📊 Exemples de données:

angle | fleche
--- | ---
0 | ↑
45 | ↗
90 | →
135 | ↘
180 | ↓
225 | ↙
270 | ←
315 | ↖

## 22. Mise à jour de la direction toutes les 5 secondes

Étant donné que la direction actuelle est ↑ (nord) Et que l'utilisateur tourne vers l'est

Quand 5 secondes s'écoulent

Alors la direction est recalculée Et la nouvelle flèche ↗ (nord-est) s'affiche

## 23. Message "En attente de déplacement" si vitesse <5 km/h

Étant donné que la vitesse actuelle est 2 km/h (arrêté)

Quand l'ETA est calculé

Alors le message "En attente de déplacement" s'affiche Et l'ETA n'est pas calculé (car vitesse insuffisante)

## 24. Simplicité de l'interface (pas de carte miniature)

Étant donné qu'un audio-guide voiture est en cours

Quand l'interface est affichée

Alors aucune carte miniature n'est présente Et seuls les éléments essentiels sont affichés:

    | élément |
    |---|
    | Distance |
    | ETA |
    | Direction (flèche) |
    | Vitesse |
    | Contrôles audio |

## 25. Rayon de déclenchement par défaut en mode voiture

Étant donné un audio-guide voiture

Quand un point GPS est défini

Alors le rayon de déclenchement est 30 mètres par défaut Et le rayon de tolérance "point manqué" est 100 mètres

## 26. Déclenchement dans le rayon (30m)

Étant donné que le point GPS 3 est défini avec rayon 30m

Quand l'utilisateur entre à 25m du point

Alors la séquence 3 se déclenche automatiquement

## 27. Pas de déclenchement hors rayon

Étant donné que le point GPS 3 a un rayon de 30m

Quand l'utilisateur passe à 45m du point

Alors la séquence 3 ne se déclenche pas automatiquement

## 28. Point manqué dans rayon de tolérance (100m)

Étant donné que l'utilisateur passe à 60m du point GPS 4 (hors rayon 30m) Et que 60m < 100m (rayon tolérance)

Quand le point est détecté comme manqué

Alors un toast s'affiche: "⚠️ Point manqué : Enclos des éléphants" Et une popup s'affiche pendant 5 secondes avec 3 options

## 29. Popup "Point manqué" avec 3 actions

Étant donné qu'un point GPS a été manqué (distance 60m)

Quand la popup s'affiche

Alors les options suivantes sont disponibles:

    | bouton | icône | comportement |
    |---|---|---|
    | Écouter quand même | 🔊 | Lance séquence immédiatement (même hors zone) |
    | Passer au suivant | ⏭️ | Skip séquence, continue vers prochain point |
    | Faire demi-tour | 🔙 | Ouvre GPS externe (Google Maps/Waze) vers point |

## 30. Action "Écouter quand même"

Étant donné qu'un point GPS est manqué

Quand l'utilisateur clique sur "🔊 Écouter quand même"

Alors la séquence correspondante démarre immédiatement Et l'utilisateur peut continuer sa route

## 31. Action "Passer au suivant"

Étant donné qu'un point GPS 5 est manqué

Quand l'utilisateur clique sur "⏭️ Passer au suivant"

Alors la séquence 5 est ignorée (non écoutée) Et l'application attend le point GPS 6 Et la distance vers le point 6 s'affiche

## 32. Action "Faire demi-tour"

Étant donné qu'un point GPS est manqué à (43.1250, 2.5700)

Quand l'utilisateur clique sur "🔙 Faire demi-tour"

Alors l'application détecte l'app GPS installée (Google Maps ou Waze) Et ouvre la navigation GPS externe vers (43.1250, 2.5700)

## 33. Point manqué au-delà du rayon de tolérance (>100m)

Étant donné que l'utilisateur passe à 150m du point GPS 6

Quand la distance est détectée

Alors aucune popup ne s'affiche (point trop loin) Et l'utilisateur peut naviguer manuellement avec [▶|]

## 34. 📋 Plan: Gestion selon la distance au point

Étant donné un point GPS avec rayon 30m et tolérance 100m

Quand l'utilisateur passe à du point

Alors le comportement est

📊 Exemples de données:

distance | comportement
--- | ---
20m | Déclenchement automatique séquence
40m | Rien (hors rayon, pas encore tolérance)
60m | Popup "Point manqué" avec 3 options
110m | Rien (trop loin, hors tolérance)

## 35. Configuration rayon personnalisé par le créateur

Étant donné qu'un créateur définit un rayon de 50m (au lieu de 30m)

Quand un utilisateur entre à 45m du point

Alors la séquence se déclenche automatiquement Et le rayon personnalisé est respecté

## 36. Rayon minimum et maximum configurables

Étant donné qu'un créateur configure un rayon

Quand il ajuste le curseur

Alors les valeurs disponibles sont de 10m à 200m Et le rayon par défaut suggéré est 30m pour la voiture

## 37. Safari-parc avec déclenchement automatique fluide

Étant donné qu'un utilisateur roule dans un safari à 20 km/h

Quand il passe devant "Enclos des lions" (point GPS 2)

Alors la séquence 2 démarre automatiquement sans intervention Et il peut se concentrer sur la conduite et l'observation

## 38. Détour imprévu (travaux sur la route)

Étant donné qu'un utilisateur prend un détour à cause de travaux Et que le point GPS 4 devient inaccessible

Quand il est loin du point (>100m) Et qu'il clique manuellement sur [▶|]

Alors la séquence 4 démarre quand même Et l'expérience continue sans blocage

## 39. Passager qui navigue librement

Étant donné qu'un passager utilise l'application Et que le conducteur roule à 50 km/h

Quand le passager clique sur "Précédent" pour réécouter

Alors l'action est exécutée immédiatement Et un warning apparaît brièvement (sensibilisation)

## 40. Embouteillage prolongé

Étant donné que la séquence 3 est terminée depuis 10 minutes Et que l'utilisateur est bloqué dans un embouteillage Et que le point GPS 4 est encore à 1.5 km

Quand l'utilisateur clique sur [▶|]

Alors la séquence 4 démarre immédiatement Et l'utilisateur peut passer le temps en écoutant

## 41. GPS désactivé en mode voiture

Étant donné qu'un audio-guide voiture est démarré Et que le GPS est désactivé

Quand l'application détecte l'absence de GPS

Alors une alerte s'affiche:

## 42. Action "Passer en mode Manuel"

Étant donné que le GPS est désactivé

Quand l'utilisateur clique sur "Passer en mode Manuel"

Alors l'audio-guide bascule en navigation 100% manuelle Et les boutons [▶|] et [|◀] permettent de naviguer Et aucun déclenchement GPS n'est tenté

## 43. Précision GPS insuffisante

Étant donné que le signal GPS a une précision de ±150m

Quand l'utilisateur approche d'un point GPS avec rayon 30m

Alors un avertissement s'affiche:

## 44. Perte signal GPS en cours de route

Étant donné qu'un audio-guide voiture est en cours

Quand le signal GPS est perdu (tunnel, parking souterrain)

Alors un toast s'affiche: "Signal GPS perdu. Navigation manuelle active." Et les boutons de navigation restent actifs

Quand le signal GPS revient

Alors un toast s'affiche: "Signal GPS rétabli" Et le déclenchement automatique est réactivé

## 45. Dépassement de la vitesse recommandée

Étant donné qu'un audio-guide recommande 20-30 km/h Et que l'utilisateur roule à 65 km/h

Quand la vitesse est détectée

Alors l'affichage vitesse est en orange: "⚠️ 65 km/h" Et un message info s'affiche: "Vitesse élevée. Risque de manquer des points."

# Audio-guides modes vélo et transport

> En tant qu'utilisateur à vélo ou en transport en commun Je veux profiter d'un guidage GPS adapté à mon mode de déplacement Afin d'avoir une expérience optimisée avec tolérances appropriées

27 scénarios (24 standards, 3 plans)

> Contexte commun à tous les scénarios

> Étant donné que l'application RoadWave est démarrée Et que l'utilisateur "jean@example.com" est connecté Et que le GPS est activé

## 1. 📋 Plan: Paramètres par mode de déplacement

Étant donné un audio-guide configuré en mode

Alors les paramètres suivants sont appliqués:

    | paramètre | valeur |
    |---|---|
    | Rayon déclenchement | <rayon_declenchement> |
    | Rayon tolérance "point manqué" | <rayon_tolerance> |
    | Vitesse recommandée | <vitesse_recommandee> |
    | Seuil warning sécurité | <seuil_warning> |

📊 Exemples de données:

mode | rayon_declenchement | rayon_tolerance | vitesse_recommandee | seuil_warning
--- | --- | --- | --- | ---
Voiture | 30m | 100m | 20-50 km/h | >10 km/h
Vélo | 50m | 75m | 10-25 km/h | >5 km/h
Transport | 100m | 150m | Variable | Désactivé

## 2. Déclenchement automatique avec rayon 50m (mode vélo)

Étant donné un audio-guide vélo "Circuit des châteaux de la Loire" Et que le point GPS 3 a un rayon de 50m

Quand l'utilisateur à vélo entre à 45m du point

Alors la séquence 3 "Château de Chambord" se déclenche automatiquement

## 3. Rayon plus large justifié pour le vélo

Étant donné qu'un cycliste roule sur piste cyclable Et que sa vitesse varie entre 8 et 22 km/h (arrêts fréquents) Et que le tracé est moins prévisible qu'en voiture

Quand un point GPS avec rayon 50m est défini

Alors le rayon plus large compense la variabilité de trajectoire

## 4. Warning sécurité dès 5 km/h en vélo

Étant donné un audio-guide vélo en cours Et que la vitesse actuelle est 12 km/h

Quand l'utilisateur clique sur [▶|] "Suivant"

Alors l'action est exécutée Et un warning s'affiche: "⚠️ Manipulation en déplacement détecté. Pour votre sécurité, arrêtez-vous."

## 5. 📋 Plan: Warning vélo selon la vitesse

Étant donné que la vitesse actuelle à vélo est km/h

Quand l'utilisateur clique sur un bouton de navigation

Alors le warning est affiché :

📊 Exemples de données:

vitesse | warning
--- | ---
0 | Non
4 | Non
6 | Oui
15 | Oui
25 | Oui

## 6. Tolérance GPS moins stricte en vélo

Étant donné qu'un cycliste passe à 65m du point GPS 4 Et que le rayon de déclenchement est 50m Et que le rayon de tolérance est 75m

Quand la distance est détectée

Alors la popup "Point manqué" s'affiche avec 3 options Et le système tolère l'écart (trajectoire vélo moins prévisible)

## 7. Affichage adapté au vélo

Étant donné un audio-guide vélo en cours

Quand l'interface est affichée

Alors les informations suivantes sont visibles:

    | information | valeur |
    |---|---|
    | Icône mode | 🚴 |
    | Distance prochain point | "450 m" |
    | ETA | "≈ 2 minutes" |
    | Direction | ↗️ |
    | Vitesse actuelle | "18 km/h" |
    | Vitesse recommandée | "10-25 km/h" |

## 8. Cas d'usage - Piste cyclable avec arrêts fréquents

Étant donné qu'un cycliste suit un circuit nature Et qu'il s'arrête régulièrement (feux, photos, fatigue)

Quand il s'arrête à 40m d'un point GPS (rayon 50m)

Alors la séquence se déclenche automatiquement Et le rayon large permet le déclenchement malgré l'arrêt

## 9. Cas d'usage - Circulation mixte piétons/vélos

Étant donné qu'un cycliste roule sur voie partagée Et qu'il doit ralentir fréquemment pour éviter les piétons

Quand sa vitesse varie entre 5 et 20 km/h

Alors le système s'adapte avec le rayon 50m Et le déclenchement reste fiable

## 10. Déclenchement automatique avec rayon 100m (mode transport)

Étant donné un audio-guide transport "Ligne touristique Paris" Et que le point GPS "Tour Eiffel" a un rayon de 100m

Quand le bus touristique entre à 85m du point

Alors la séquence "Tour Eiffel" se déclenche automatiquement

## 11. Rayon très large justifié pour le transport

Étant donné qu'un bus touristique suit une ligne fixe Et qu'il effectue des arrêts fréquents (stations) Et que l'utilisateur n'a aucun contrôle sur la trajectoire

Quand un point GPS avec rayon 100m est défini

Alors le rayon large compense les arrêts et la ligne fixe

## 12. Pas de warning sécurité en mode transport

Étant donné un audio-guide transport en cours Et que le bus roule à 50 km/h

Quand l'utilisateur clique sur [▶|] "Suivant"

Alors l'action est exécutée immédiatement Et aucun warning n'est affiché

## 13. Vitesse recommandée "Selon ligne"

Étant donné un audio-guide transport

Quand l'interface est affichée

Alors la vitesse recommandée indique "Selon ligne" Et aucune valeur fixe n'est affichée (car ligne de transport varie)

## 14. Tolérance horaire pour retards

Étant donné qu'un bus touristique est en retard de 3 minutes Et qu'il arrive au point GPS "Musée du Louvre" avec retard

Quand il entre dans le rayon de 100m

Alors la séquence se déclenche normalement Et le système tolère le retard (pas de pénalité temporelle)

## 15. Tolérance spatiale très large (150m)

Étant donné qu'un bus passe à 120m du point GPS "Arc de Triomphe" Et que le rayon de déclenchement est 100m Et que le rayon de tolérance est 150m

Quand la distance est détectée

Alors la popup "Point manqué" s'affiche avec 3 options

## 16. Affichage adapté au transport

Étant donné un audio-guide transport en cours

Quand l'interface est affichée

Alors les informations suivantes sont visibles:

    | information | valeur |
    |---|---|
    | Icône mode | 🚌 |
    | Distance prochain point | "1.2 km" |
    | ETA | "≈ 3 minutes" |
    | Direction | → |
    | Vitesse actuelle | "35 km/h" |
    | Vitesse recommandée | "Selon ligne" |

## 17. Cas d'usage - Bus touristique hop-on hop-off

Étant donné un bus touristique "Paris Open Tour" Et qu'il suit un circuit fixe avec 15 arrêts

Quand il approche de chaque arrêt

Alors la séquence correspondante se déclenche automatiquement Et l'utilisateur n'a rien à faire (expérience passive)

## 18. Cas d'usage - Train panoramique

Étant donné un train touristique "Ligne des Alpes" Et qu'il roule à vitesse variable (20-80 km/h)

Quand il passe près de points d'intérêt

Alors les séquences se déclenchent avec rayon 100m Et le système compense la vitesse élevée

## 19. Navigation manuelle conservée (vélo et transport)

Étant donné un audio-guide en mode

Quand l'utilisateur clique sur [▶|] ou [|◀]

Alors les boutons manuels fonctionnent normalement Et aucune vérification GPS n'est effectuée

## 20. Affichage distance + ETA + direction (tous modes)

Étant donné un audio-guide en mode

Quand l'interface est affichée

Alors les informations distance, ETA et direction sont affichées Et le format est identique au mode voiture

## 21. Gestion "Point manqué" identique

Étant donné un audio-guide en mode

Quand un point GPS est manqué (dans rayon tolérance)

Alors la popup avec 3 options s'affiche:

    | option |
    |---|
    | 🔊 Écouter quand même |
    | ⏭️ Passer au suivant |
    | 🔙 Faire demi-tour |

## 22. 📋 Plan: Insertion publicité dans tous les modes

Étant donné un utilisateur gratuit écoute un audio-guide en mode

Quand la séquence 5 se termine (1 pub / 5 séquences)

Alors la publicité s'enchaîne automatiquement Et elle est skippable après 5 secondes

📊 Exemples de données:

mode
Voiture
Vélo
Transport
Piéton

## 23. GPS imprécis en forêt (vélo)

Étant donné un cycliste dans une forêt dense Et que la précision GPS est ±80m

Quand il approche d'un point GPS avec rayon 50m

Alors un avertissement s'affiche:

## 24. Bus dévié de son itinéraire (transport)

Étant donné un bus touristique avec déviation Et que plusieurs points GPS deviennent inaccessibles

Quand l'utilisateur est informé

Alors un message s'affiche:

## 25. Changement de mode en cours de route

Étant donné un audio-guide démarré en mode "Vélo"

Quand l'utilisateur décide de continuer à pied Et qu'il ouvre les paramètres

Alors il peut changer le mode vers "Piéton" Et les rayons sont reconfigurés automatiquement Et une confirmation s'affiche:

## 26. Détection automatique incohérente

Étant donné qu'un utilisateur marche rapidement (7 km/h) Et que le système détecte "Vélo" par erreur

Quand la suggestion s'affiche

Alors l'utilisateur peut cliquer sur "Changer" Et sélectionner manuellement "Piéton"

## 27. Batterie en mode vélo longue distance

Étant donné un circuit vélo de 50 km avec 20 séquences Et que l'utilisateur roule pendant 3 heures

Quand la batterie atteint 15%

Alors une notification suggère:

# Audio-guides Premium et monétisation

> En tant que créateur Je veux pouvoir proposer des audio-guides Premium Afin de monétiser mon contenu de qualité

31 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'application RoadWave est démarrée Et que le créateur "guide@example.com" est connecté et vérifié

## 1. Création d'un audio-guide Premium

Étant donné que le créateur crée un audio-guide "Visite VIP Versailles"

Quand il accède aux paramètres de monétisation (étape 4)

Alors il peut choisir:

    | option | description |
    |---|---|
    | Gratuit | Accessible à tous (avec pubs) |
    | Premium | Réservé abonnés Premium |

## 2. Badge Premium visible sur l'audio-guide

Étant donné un audio-guide configuré en Premium

Quand il est affiché dans les résultats de recherche

Alors un badge "👑 Premium" est visible Et la cover image a un cadre doré subtil

## 3. Preview 3 premières séquences pour utilisateurs gratuits

Étant donné un audio-guide Premium "Visite VIP Versailles" avec 15 séquences Et qu'un utilisateur gratuit ouvre l'audio-guide

Quand il consulte la liste des séquences

Alors les séquences affichent:

    | séquence | état |
    |---|---|
    | 1 | ✅ Accessible (preview) |
    | 2 | ✅ Accessible (preview) |
    | 3 | ✅ Accessible (preview) |
    | 4 | 🔒 Réservé Premium |
    | 5-15 | 🔒 Réservé Premium |

## 4. Écoute des 3 premières séquences sans blocage

Étant donné un utilisateur gratuit Et un audio-guide Premium avec preview

Quand il écoute les séquences 1, 2 et 3

Alors aucune publicité n'est insérée (preview = teasing) Et l'écoute est fluide

## 5. Paywall après la 3ème séquence

Étant donné qu'un utilisateur gratuit termine la séquence 3

Quand la séquence se termine

Alors un overlay paywall s'affiche immédiatement:

## 6. Bouton "Passer Premium" vers tunnel d'abonnement

Étant donné que l'overlay paywall Premium est affiché

Quand l'utilisateur clique sur "Passer Premium"

Alors il est redirigé vers la page d'abonnement Mangopay Et l'audio-guide actuel est marqué en "pending" (reprise après souscription)

## 7. Reprise automatique après souscription Premium

Étant donné qu'un utilisateur s'est abonné Premium depuis un paywall audio-guide

Quand l'abonnement est activé

Alors il est redirigé vers l'audio-guide automatiquement Et la séquence 4 démarre immédiatement Et un toast de bienvenue s'affiche: "✨ Bienvenue Premium ! Profitez de votre audio-guide"

## 8. Utilisateur Premium - Accès complet immédiat

Étant donné qu'un utilisateur Premium ouvre un audio-guide Premium

Quand il consulte la liste des séquences

Alors toutes les 15 séquences sont accessibles Et aucun paywall ne s'affiche Et aucune publicité n'est insérée

## 9. Pas de preview si l'audio-guide a <3 séquences

Étant donné un audio-guide Premium avec seulement 2 séquences

Quand un utilisateur gratuit tente de l'ouvrir

Alors un paywall s'affiche immédiatement (avant lecture) Et aucune preview n'est disponible

## 10. Rémunération créateur pour audio-guide Premium

Étant donné un créateur avec un audio-guide Premium Et que 50 utilisateurs Premium ont écouté l'audio-guide ce mois

Quand la répartition des revenus est calculée

Alors le créateur reçoit 70% des revenus proportionnels Et la formule est: (Écoutes créateur / Total écoutes Premium) × 70% pool Premium

## 11. Dashboard revenus par audio-guide

Étant donné qu'un créateur a 3 audio-guides Premium publiés

Quand il consulte son dashboard revenus

Alors il voit pour chaque audio-guide:

    | audio_guide | ecoutes_mois | revenus_estime |
    |---|---|---|
    | Visite VIP Versailles | 142 | 45.20 € |
    | Secrets du Louvre | 89 | 28.50 € |
    | Châteaux de la Loire | 203 | 64.80 € |

## 12. Comparaison gratuit vs Premium

Étant donné qu'un créateur a publié 2 audio-guides:

    | titre | type | ecoutes_mois | revenus |
    |---|---|---|---|
    | Tour de Paris | Gratuit | 1200 | 12.50 € |
    | Visite VIP Versailles | Premium | 142 | 45.20 € |

Quand il consulte son dashboard

Alors il peut comparer les performances Et constater que Premium génère plus de revenus par écoute

## 13. Seuil minimum de paiement (20€)

Étant donné qu'un créateur a généré 18€ de revenus ce mois

Quand le paiement mensuel est traité

Alors le montant est reporté au mois suivant Et un message s'affiche: "Seuil minimum non atteint (20€). Montant reporté."

## 14. Paiement automatique mensuel

Étant donné qu'un créateur a généré 138.50€ de revenus en janvier

Quand le 5 février arrive

Alors le paiement est initié automatiquement via Mangopay Et le créateur reçoit une notification: "Paiement de 138.50€ en cours" Et les fonds arrivent sous 2-3 jours ouvrés

## 15. Insertion publicité toutes les 5 séquences (gratuit)

Étant donné un audio-guide gratuit avec 12 séquences Et un utilisateur gratuit

Quand il termine la séquence 5

Alors une publicité démarre automatiquement

Quand il termine la séquence 10

Alors une deuxième publicité démarre

## 16. Publicité après séquence en mode piéton (avec pause)

Étant donné un audio-guide piéton gratuit

Quand la séquence 5 se termine

Alors la publicité démarre automatiquement (pas d'attente bouton) Et la pub est skippable après 5 secondes

Quand la publicité se termine

Alors le player se met en pause Et l'utilisateur doit cliquer sur [▶|] pour continuer

## 17. Publicité en mode voiture/vélo/transport (automatique)

Étant donné un audio-guide voiture gratuit

Quand la séquence 5 se termine

Alors la publicité démarre automatiquement

Quand la publicité se termine

Alors la séquence 6 démarre automatiquement (pas de pause)

## 18. Publicités géolocalisées dans audio-guides

Étant donné un audio-guide dans la région "Île-de-France"

Quand une publicité doit être insérée

Alors l'API publicitaire filtre par:

    | critère | valeur |
    |---|---|
    | Géolocalisation | Île-de-France |
    | Catégorie | Tourisme, Culture |
    | Langue | Français |

## 19. Comptabilisation des impressions pub pour créateur

Étant donné qu'un audio-guide gratuit génère 200 écoutes complètes Et que chaque écoute complète = 2 publicités (séq. 5 et 10)

Quand les revenus pub sont calculés

Alors 400 impressions sont comptabilisées Et le créateur reçoit 0.80€ (400 × 0.002€)

## 20. CTA Premium après audio-guide gratuit complété

Étant donné qu'un utilisateur gratuit complète un audio-guide gratuit

Quand il termine la dernière séquence

Alors un overlay s'affiche:

## 21. Recommandations d'audio-guides Premium après gratuit

Étant donné qu'un utilisateur termine un audio-guide gratuit "Tour de Paris"

Quand l'overlay de fin s'affiche

Alors 3 audio-guides Premium similaires sont suggérés:

    | titre | type | créateur |
    |---|---|---|
    | Secrets de Montmartre | Premium | @paris_stories |
    | Visite VIP Musée d'Orsay | Premium | @art_guide |
    | Paris hors des sentiers | Premium | @explore_paris |

## 22. Badge "Premium recommandé" sur audio-guides populaires

Étant donné un audio-guide Premium avec >500 écoutes et note >4.5/5

Quand il est affiché dans les résultats de recherche

Alors un badge "⭐ Premium recommandé" est visible Et il est mis en avant dans les résultats

## 23. Conversion tracking pour attribution créateur

Étant donné qu'un utilisateur découvre Premium via un audio-guide créateur

Quand il s'abonne

Alors la conversion est trackée:

    | donnée | valeur |
    |---|---|
    | source_conversion | audio_guide_paywall |
    | audio_guide_id | visite_vip_versailles_123 |
    | creator_id | guide_versailles_456 |

Et le créateur bénéficie d'un bonus de conversion

## 24. Essai gratuit 7 jours Premium via audio-guide

Étant donné qu'un utilisateur gratuit atteint le paywall d'un audio-guide Premium Et qu'il n'a jamais essayé Premium

Quand l'overlay s'affiche

Alors une offre d'essai est proposée:

## 25. Activation immédiate après essai gratuit

Étant donné qu'un utilisateur démarre un essai gratuit 7 jours

Quand l'essai est activé

Alors l'audio-guide Premium démarre immédiatement Et toutes les séquences sont débloquées Et aucune publicité n'est insérée

## 26. Rappel 2 jours avant fin d'essai

Étant donné qu'un utilisateur a démarré un essai gratuit le 15/01

Quand le 20/01 arrive (J-2)

Alors une notification est envoyée:

## 27. Créateur mix gratuit + Premium

Étant donné qu'un créateur a publié 5 audio-guides:

    | titre | type |
    |---|---|
    | Découverte de Paris | Gratuit |
    | Visite VIP Louvre | Premium |
    | Balade Montmartre | Gratuit |
    | Secrets Versailles | Premium |
    | Visite express Orsay | Gratuit |

Quand un utilisateur découvre son profil

Alors les audio-guides gratuits servent de teasing Et les audio-guides Premium sont mis en avant avec badge

## 28. Utilisateur hésite à s'abonner

Étant donné qu'un utilisateur atteint le paywall d'un audio-guide Premium Et qu'il clique sur "Découvrir d'autres audio-guides gratuits"

Quand il revient 2 jours plus tard sur le même audio-guide

Alors le paywall s'affiche à nouveau Et une réduction temporaire est proposée: "Offre spéciale : -20% premier mois"

## 29. Échec du paiement Premium via paywall

Étant donné qu'un utilisateur tente de s'abonner Premium

Quand le paiement Mangopay échoue

Alors un message d'erreur s'affiche:

## 30. Abonnement Premium expiré pendant écoute

Étant donné qu'un utilisateur Premium écoute un audio-guide Premium Et que son abonnement expire pendant l'écoute (séquence 8/15)

Quand l'expiration est détectée

Alors l'écoute continue jusqu'à la fin de la séquence en cours Et un overlay s'affiche ensuite:

## 31. Créateur change audio-guide de gratuit à Premium

Étant donné qu'un audio-guide gratuit a 50 utilisateurs avec progression

Quand le créateur le passe en Premium

Alors les utilisateurs ayant déjà commencé gardent l'accès complet Et seuls les nouveaux utilisateurs sont soumis au paywall Et un message de transparence s'affiche:

# Sauvegarde et reprise de progression audio-guide

> En tant qu'utilisateur Je veux que ma progression soit sauvegardée automatiquement Afin de pouvoir reprendre mon audio-guide là où je me suis arrêté

32 scénarios (31 standards, 1 plan)

> Contexte commun à tous les scénarios

> Étant donné que l'application RoadWave est démarrée Et que l'utilisateur "jean@example.com" est connecté

## 1. Sauvegarde automatique toutes les 10 secondes

Étant donné qu'un audio-guide "Visite du Louvre" est en cours Et que la séquence 3 est à la position 1:24

Quand 10 secondes s'écoulent

Alors la progression est sauvegardée automatiquement:

    | donnée | valeur |
    |---|---|
    | audio_guide_id | louvre_123 |
    | sequence_actuelle | 3 |
    | position_audio | 1:24 |
    | timestamp | 2026-01-22 14:35:42 |
    | sequences_ecoutees | [1, 2] |

## 2. Sauvegarde locale (SQLite) pour rapidité

Étant donné qu'une sauvegarde est déclenchée

Quand la progression est enregistrée

Alors les données sont écrites en SQLite local Et l'écriture prend moins de 50ms Et l'application reste fluide

## 3. Synchronisation cloud en arrière-plan

Étant donné qu'une sauvegarde locale est effectuée

Quand 30 secondes s'écoulent

Alors la progression est synchronisée vers PostgreSQL cloud Et la synchronisation s'effectue en arrière-plan Et elle n'impacte pas les performances

## 4. Sauvegarde immédiate lors de la fermeture

Étant donné qu'un audio-guide est en cours à la séquence 4 position 2:15

Quand l'utilisateur ferme l'application

Alors la progression est sauvegardée immédiatement (local + cloud) Et les données sont écrites avant la fermeture complète

## 5. Sauvegarde des séquences complétées

Étant donné qu'un audio-guide de 12 séquences est en cours Et que les séquences 1, 2, 4, 5 ont été écoutées à >80%

Quand la progression est sauvegardée

Alors les séquences complétées sont enregistrées:

## 6. Historique des écoutes pour statistiques

Étant donné qu'un utilisateur a écouté 3 séquences d'un audio-guide

Quand les données sont sauvegardées

Alors l'historique d'écoute inclut:

    | sequence_id | started_at | completed_at | completion_rate |
    |---|---|---|---|
    | 1 | 2026-01-22 14:10:00 | 2026-01-22 14:12:15 | 100% |
    | 2 | 2026-01-22 14:12:20 | 2026-01-22 14:14:08 | 100% |
    | 3 | 2026-01-22 14:14:15 | 2026-01-22 14:17:45 | 92% |

## 7. Popup de reprise au redémarrage

Étant donné que l'utilisateur a quitté l'app à la séquence 6 position 2:34

Quand il rouvre l'audio-guide "Visite du Louvre"

Alors une popup s'affiche:

## 8. Action "Reprendre" - Position exacte restaurée

Étant donné qu'une popup de reprise est affichée

Quand l'utilisateur clique sur "▶️ Reprendre"

Alors la séquence 6 "Vénus de Milo" se charge Et la position exacte 2:34 est restaurée Et la lecture démarre automatiquement après 1 seconde

## 9. Action "Recommencer" - Réinitialisation complète

Étant donné qu'une popup de reprise est affichée

Quand l'utilisateur clique sur "🔄 Recommencer"

Alors l'audio-guide redémarre depuis la séquence 1 position 0:00 Et toutes les séquences sont marquées ⭕ "À écouter" Et l'historique d'écoute est réinitialisé pour cette session

## 10. Reprise après 7 jours d'inactivité

Étant donné qu'un utilisateur a arrêté un audio-guide le 15/01/2026 Et qu'il le rouvre le 22/01/2026 (7 jours plus tard)

Quand l'audio-guide se charge

Alors la popup de reprise s'affiche normalement Et toutes les données de progression sont conservées

## 11. Reprise sur un autre appareil (synchronisation cloud)

Étant donné qu'un utilisateur écoute un audio-guide sur iPhone Et qu'il quitte à la séquence 4 position 1:20

Quand il ouvre le même audio-guide sur iPad

Alors la popup de reprise s'affiche avec la progression iPhone Et il peut reprendre exactement où il s'était arrêté

## 12. Conflit de synchronisation (dernier appareil gagne)

Étant donné qu'un utilisateur écoute sur iPhone à la séquence 3 Et simultanément sur iPad à la séquence 7

Quand les deux appareils synchronisent

Alors la progression la plus récente (timestamp) est conservée Et l'appareil avec ancienne progression affiche une notification:

## 13. Mode hors-ligne - Sauvegarde locale uniquement

Étant donné qu'un utilisateur écoute un audio-guide hors connexion Et qu'il atteint la séquence 5

Quand la progression est sauvegardée

Alors les données sont écrites localement (SQLite) Et une icône "☁️ Non synchronisé" s'affiche discrètement

## 14. Synchronisation automatique à la reconnexion

Étant donné que l'utilisateur a écouté hors ligne jusqu'à la séquence 8 Et que 5 progressions locales ne sont pas synchronisées

Quand la connexion réseau est rétablie

Alors les 5 progressions sont synchronisées automatiquement Et un toast s'affiche brièvement: "✅ Progression synchronisée"

## 15. Suppression de la progression (recommencer proprement)

Étant donné qu'un utilisateur est à la séquence 10/12

Quand il ouvre les paramètres de l'audio-guide Et qu'il clique sur "🔄 Réinitialiser progression"

Alors une confirmation s'affiche: Et si confirmé, la progression est effacée

## 16. Taux de complétion global de l'audio-guide

Étant donné un audio-guide de 12 séquences Et que l'utilisateur a écouté complètement 8 séquences Et partiellement 1 séquence (45%)

Quand les statistiques sont calculées

Alors le taux de complétion affiché est "67%" (8/12)

## 17. Badge "Audio-guide complété" à 100%

Étant donné un audio-guide de 12 séquences

Quand l'utilisateur écoute la 12ème séquence à 100%

Alors un badge "✅ Audio-guide complété" s'affiche Et une notification de félicitations est envoyée Et le statut "Complété le 22/01/2026" est visible dans l'historique

## 18. Temps total passé sur l'audio-guide

Étant donné qu'un utilisateur a écouté un audio-guide sur 2 sessions:

    | session | durée |
    |---|---|
    | 1 | 25 min |
    | 2 | 18 min |

Quand les statistiques sont calculées

Alors le temps total est "43 minutes" Et il est affiché dans l'historique personnel

## 19. Liste des audio-guides "En cours" dans le profil

Étant donné qu'un utilisateur a 3 audio-guides en cours:

    | audio_guide | progression |
    |---|---|
    | Visite du Louvre | 6/12 |
    | Safari du Paugre | 3/8 |
    | Circuit Loire à Vélo | 12/15 |

Quand il consulte son profil "Audio-guides"

Alors la section "📍 En cours" affiche les 3 audio-guides Et chaque élément montre la progression sous forme de barre

## 20. Liste des audio-guides "Complétés" dans le profil

Étant donné qu'un utilisateur a complété 2 audio-guides:

    | audio_guide | date_completion |
    |---|---|
    | Tour de Paris | 2026-01-15 |
    | Découverte de Lyon | 2026-01-20 |

Quand il consulte son profil "Audio-guides"

Alors la section "✅ Complétés" affiche les 2 audio-guides Et la date de complétion est visible

## 21. Badge "Complétiste" pour 10 audio-guides complétés

Étant donné qu'un utilisateur complète son 10ème audio-guide

Quand la complétion est enregistrée

Alors un badge "🏆 Complétiste" est débloqué Et il apparaît sur son profil Et une notification est envoyée:

## 22. 📋 Plan: Niveaux de badges selon nombre d'audio-guides complétés

Étant donné qu'un utilisateur complète audio-guides

Quand le badge est attribué

Alors il reçoit le badge ""

📊 Exemples de données:

nombre | badge
--- | ---
1 | 🎧 Premier audio-guide
5 | 🗺️ Explorateur
10 | 🏆 Complétiste
25 | 🌟 Expert
50 | 💎 Maître audio-guideur

## 23. Dashboard créateur - Statistiques par audio-guide

Étant donné qu'un créateur a publié l'audio-guide "Visite du Louvre"

Quand il consulte son dashboard

Alors les métriques suivantes sont affichées:

    | métrique | valeur |
    |---|---|
    | Écoutes totales | 1542 |
    | Écoutes complètes (>80%) | 892 |
    | Taux de complétion moyen | 58% |
    | Temps d'écoute total | 423h |
    | Séquence la plus écoutée | Séq. 3 |
    | Séquence la moins écoutée | Séq. 11 |

## 24. Graphique de complétion par séquence

Étant donné un audio-guide de 12 séquences

Quand le créateur consulte les statistiques détaillées

Alors un graphique en barres affiche:

    | séquence | taux_completion |
    |---|---|
    | 1 | 100% |
    | 2 | 95% |
    | 3 | 89% |
    | ... | ... |
    | 12 | 58% |

## 25. Détection des points d'abandon

Étant donné qu'un audio-guide a un taux de complétion de 58% Et que 35% des utilisateurs abandonnent à la séquence 7

Quand le créateur consulte les insights

Alors un avertissement s'affiche:

## 26. Heatmap géographique des écoutes

Étant donné un audio-guide géolocalisé

Quand le créateur consulte la heatmap

Alors une carte affiche:

    | élément | description |
    |---|---|
    | Densité d'écoutes | Zones rouge/orange/jaune selon écoutes |
    | Points GPS | Marqueurs sur chaque point |
    | Statistiques par point | Nombre d'écoutes par zone |

## 27. Temps moyen par séquence

Étant donné qu'un créateur analyse son audio-guide

Quand il consulte les statistiques temporelles

Alors il voit pour chaque séquence:

    | séquence | durée_audio | temps_ecoute_moyen | ecart |
    |---|---|---|---|
    | 1 | 2:15 | 2:10 | -5s |
    | 2 | 1:48 | 1:30 | -18s |
    | 3 | 3:42 | 3:40 | -2s |

## 28. Notification créateur pour milestone

Étant donné qu'un audio-guide atteint 1000 écoutes

Quand le seuil est franchi

Alors une notification est envoyée au créateur:

## 29. Corruption de données de sauvegarde

Étant donné qu'une sauvegarde locale (SQLite) est corrompue

Quand l'application tente de charger la progression

Alors une récupération depuis le cloud est tentée Et si réussie, les données cloud sont restaurées Et la base locale est reconstruite

## 30. Échec de synchronisation cloud

Étant donné que l'API cloud est indisponible

Quand une tentative de synchronisation est effectuée

Alors l'application continue avec sauvegarde locale uniquement Et un retry automatique est programmé dans 5 minutes Et l'icône "☁️ Non synchronisé" reste affichée

## 31. Suppression accidentelle de progression (récupération)

Étant donné qu'un utilisateur réinitialise un audio-guide par erreur

Quand il contacte le support dans les 7 jours

Alors l'équipe peut restaurer la progression depuis les backups Et les données sont récupérables (backup quotidien conservé 30 jours)

## 32. Nettoyage automatique des vieilles progressions

Étant donné qu'une progression n'a pas été mise à jour depuis 6 mois

Quand le nettoyage automatique s'exécute

Alors la progression est archivée (mais pas supprimée) Et l'utilisateur peut la restaurer via l'historique

# Classification des contenus par âge

> En tant que plateforme responsable Je veux classifier les contenus par tranche d'âge Afin de protéger les mineurs et respecter les obligations légales

13 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible

## 1. Créateur doit classifier son contenu à la publication

Étant donné que je suis un créateur connecté

Quand je crée un nouveau contenu audio

Alors je dois obligatoirement choisir une classification d'âge parmi:

    | classification | description |
    |---|---|
    | Tout public | Contenu adapté à tous les âges |
    | 13+ | Contenu mature léger |
    | 16+ | Contenu mature |
    | 18+ | Contenu adulte |

## 2. Publication impossible sans classification

Étant donné que je crée un contenu audio

Quand j'essaie de publier sans sélectionner de classification

Alors la publication échoue Et je vois le message "Vous devez sélectionner une classification d'âge"

## 3. Utilisateur 13-15 ans voit uniquement du contenu "Tout public"

Étant donné que je suis un utilisateur de 14 ans Et qu'il existe des contenus avec les classifications suivantes:

    | classification | nombre |
    |---|---|
    | Tout public | 20 |
    | 13+ | 15 |
    | 16+ | 10 |
    | 18+ | 5 |

Quand je demande des recommandations

Alors je vois uniquement les 20 contenus "Tout public" Et les autres contenus ne sont jamais proposés

## 4. Utilisateur 16-17 ans voit "Tout public" et "13+"

Étant donné que je suis un utilisateur de 17 ans Et qu'il existe des contenus avec les classifications suivantes:

    | classification | nombre |
    |---|---|
    | Tout public | 20 |
    | 13+ | 15 |
    | 16+ | 10 |
    | 18+ | 5 |

Quand je demande des recommandations

Alors je vois 35 contenus (Tout public + 13+) Et les contenus 16+ et 18+ ne sont pas proposés

## 5. Utilisateur 18+ voit tous les contenus

Étant donné que je suis un utilisateur de 25 ans Et qu'il existe des contenus avec toutes les classifications

Quand je demande des recommandations

Alors je vois tous les contenus sans restriction Et aucun filtre d'âge n'est appliqué

## 6. Mode Kids activé automatiquement pour les moins de 13 ans

Étant donné que je m'inscris avec une date de naissance "2013-01-21"

Alors le mode Kids est activé automatiquement Et je vois uniquement du contenu "Tout public" Et des protections supplémentaires sont appliquées

## 7. Modérateur reclassifie un contenu mal catégorisé

Étant donné qu'un contenu est publié avec la classification "Tout public" Et que ce contenu contient du langage inapproprié détecté en modération

Quand le modérateur reclassifie ce contenu en "16+"

Alors la nouvelle classification est appliquée immédiatement Et le contenu n'est plus visible pour les utilisateurs de moins de 16 ans Et le créateur reçoit une notification de reclassification

## 8. Strike si classification volontairement incorrecte

Étant donné qu'un créateur a publié un contenu "18+" classifié comme "Tout public" Et que ce contenu a été signalé

Quand le modérateur confirme la mauvaise classification volontaire

Alors le créateur reçoit 1 strike Et le contenu est reclassifié en "18+" Et le créateur reçoit une notification explicative

## 9. Créateur peut voir la distribution d'âge de son audience

Étant donné que je suis un créateur Et que j'ai publié des contenus avec différentes classifications

Quand je consulte mes statistiques

Alors je vois la répartition des âges de mes auditeurs:

    | tranche_age | pourcentage |
    |---|---|
    | 13-15 ans | 15% |
    | 16-17 ans | 20% |
    | 18+ ans | 65% |

## 10. Recherche filtrée par classification d'âge

Étant donné que je suis un utilisateur de 16 ans

Quand je recherche des contenus

Alors les résultats incluent uniquement:

    | classification |
    |---|
    | Tout public |
    | 13+ |

Et je ne vois pas les contenus 16+ et 18+ dans les résultats

## 11. Notification si tentative d'accès à contenu non autorisé

Étant donné que je suis un utilisateur de 14 ans Et qu'un contenu "16+" est partagé avec moi via un lien direct

Quand j'essaie d'accéder au contenu

Alors l'accès est refusé Et je vois le message "Ce contenu est réservé aux utilisateurs de 16 ans et plus"

## 12. Validation obligatoire des 3 premiers contenus inclut la classification

Étant donné que je suis un nouveau créateur Et que je publie mon premier contenu classifié "18+"

Quand le modérateur valide mon contenu

Alors il vérifie que la classification "18+" est appropriée Et peut la modifier si nécessaire avant validation

## 13. Statistiques de classification dans l'interface créateur

Étant donné que je suis un créateur

Quand je consulte mes contenus publiés

Alors je vois pour chaque contenu:

    | information | exemple |
    |---|---|
    | Classification actuelle | 13+ |
    | Nombre de signalements | 2 |
    | Reclassifications | Aucune / 1× par modérateur |

# Connexion utilisateur

> En tant qu'utilisateur existant Je veux me connecter à mon compte Afin d'accéder à mes contenus et paramètres

11 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et qu'un utilisateur existe avec: | email | mot_de_passe | |---|---| | user@test.fr | Password123 |

## 1. Connexion réussie avec identifiants valides

Quand je me connecte avec:

    | email | mot_de_passe |
    |---|---|
    | user@test.fr | Password123 |

Alors je suis connecté avec succès Et je reçois un access token valide pour 15 minutes Et je reçois un refresh token valide pour 30 jours

## 2. Connexion échouée avec email inexistant

Quand je me connecte avec l'email "inexistant@test.fr"

Alors la connexion échoue Et je vois le message "Email ou mot de passe incorrect"

## 3. Connexion échouée avec mot de passe incorrect

Quand je me connecte avec:

    | email | mot_de_passe |
    |---|---|
    | user@test.fr | MauvaisPass1 |

Alors la connexion échoue Et je vois le message "Email ou mot de passe incorrect"

## 4. Blocage après 5 tentatives échouées

Étant donné que j'ai échoué 4 tentatives de connexion

Quand j'échoue une 5ème tentative de connexion

Alors mon compte est temporairement bloqué Et je vois le message "Compte bloqué pour 15 minutes après 5 tentatives échouées" Et je reçois un email de notification de blocage

## 5. Tentative de connexion pendant le blocage

Étant donné que mon compte est bloqué suite à 5 tentatives échouées Et que seulement 5 minutes se sont écoulées

Quand j'essaie de me connecter avec les bons identifiants

Alors la connexion échoue Et je vois le message "Compte bloqué. Réessayez dans 10 minutes"

## 6. Déblocage automatique après 15 minutes

Étant donné que mon compte est bloqué suite à 5 tentatives échouées Et que 15 minutes se sont écoulées

Quand je me connecte avec les bons identifiants

Alors je suis connecté avec succès Et le compteur de tentatives est réinitialisé

## 7. Reset du compteur après connexion réussie

Étant donné que j'ai échoué 3 tentatives de connexion

Quand je me connecte avec les bons identifiants

Alors je suis connecté avec succès Et le compteur de tentatives est remis à 0

## 8. Reset automatique du compteur après 15 minutes sans blocage

Étant donné que j'ai échoué 3 tentatives de connexion Et que 15 minutes se sont écoulées sans nouvelle tentative

Quand je consulte mon compteur de tentatives

Alors le compteur est réinitialisé à 0

## 9. Déblocage via lien "Mot de passe oublié"

Étant donné que mon compte est bloqué suite à 5 tentatives échouées

Quand j'utilise la fonction "Mot de passe oublié" Et que je réinitialise mon mot de passe

Alors le blocage est levé immédiatement Et je peux me connecter avec le nouveau mot de passe

## 10. Email de notification lors d'un blocage

Étant donné que j'ai échoué 5 tentatives de connexion

Alors je reçois un email avec:

    | sujet | Tentatives de connexion suspectes détectées |
    |---|---|
    | contenu_contient | Votre compte a été temporairement bloqué |
    | lien_mot_de_passe | présent |

## 11. Connexion multi-device simultanée autorisée

Étant donné que je suis connecté sur un appareil iOS

Quand je me connecte également sur un appareil Android

Alors les deux sessions sont actives simultanément Et je peux utiliser l'application sur les deux appareils

# Inscription utilisateur

> En tant que nouvel utilisateur Je veux créer un compte avec email et mot de passe Afin d'accéder à l'application RoadWave

15 scénarios (14 standards, 1 plan)

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et que Zitadel est configuré

## 1. Inscription réussie avec données valides

Étant donné que l'email "nouveau@example.com" n'existe pas

Quand je m'inscris avec les données suivantes:

    | champ | valeur |
    |---|---|
    | email | nouveau@example.com |
    | mot_de_passe | Password123 |
    | pseudo | nouveau_user |
    | date_naissance | 1995-06-15 |

Alors mon compte est créé avec succès Et je reçois un email de vérification Et le lien de vérification expire dans 7 jours Et je suis redirigé vers l'application

## 2. Inscription avec email déjà existant

Étant donné qu'un utilisateur existe avec l'email "existant@example.com"

Quand je m'inscris avec l'email "existant@example.com"

Alors l'inscription échoue Et je vois le message "Cet email est déjà utilisé"

## 3. Inscription avec mot de passe invalide - trop court

Quand je m'inscris avec un mot de passe de moins de 8 caractères "Pass1"

Alors l'inscription échoue Et je vois le message "Le mot de passe doit contenir au moins 8 caractères"

## 4. Inscription avec mot de passe invalide - sans majuscule

Quand je m'inscris avec un mot de passe sans majuscule "password123"

Alors l'inscription échoue Et je vois le message "Le mot de passe doit contenir au moins une majuscule"

## 5. Inscription avec mot de passe invalide - sans chiffre

Quand je m'inscris avec un mot de passe sans chiffre "Password"

Alors l'inscription échoue Et je vois le message "Le mot de passe doit contenir au moins un chiffre"

## 6. Inscription avec pseudo invalide - trop court

Quand je m'inscris avec un pseudo de 2 caractères "ab"

Alors l'inscription échoue Et je vois le message "Le pseudo doit contenir entre 3 et 30 caractères"

## 7. Inscription avec pseudo invalide - caractères spéciaux

Quand je m'inscris avec un pseudo contenant des caractères spéciaux "user@123"

Alors l'inscription échoue Et je vois le message "Le pseudo ne peut contenir que des lettres, chiffres et underscores"

## 8. Inscription avec email invalide

Quand je m'inscris avec un email invalide "email.invalide"

Alors l'inscription échoue Et je vois le message "Format d'email invalide"

## 9. 📋 Plan: Inscription avec âge minimum non respecté

Étant donné la date du jour est "2026-01-21"

Quand je m'inscris avec une date de naissance ""

Alors l'inscription échoue Et je vois le message "Vous devez avoir au moins 13 ans pour créer un compte"

📊 Exemples de données:

date_naissance | age
--- | ---
2013-01-22 | 12
2015-06-15 | 10
2020-01-01 | 6

## 10. Inscription avec âge limite acceptable (13 ans)

Étant donné la date du jour est "2026-01-21"

Quand je m'inscris avec une date de naissance "2013-01-21"

Alors mon compte est créé avec succès Et le mode Kids est activé automatiquement

## 11. Inscription avec âge supérieur à 18 ans

Étant donné la date du jour est "2026-01-21"

Quand je m'inscris avec une date de naissance "1990-06-15"

Alors mon compte est créé avec succès Et j'ai accès à tous les contenus sans restriction d'âge

## 12. Données minimales requises à l'inscription

Quand je m'inscris sans fournir de nom complet Et sans fournir de photo de profil Et sans fournir de bio

Alors mon compte est créé avec succès Et un avatar par défaut est généré Et les champs optionnels sont vides

## 13. Renvoyer l'email de vérification

Étant donné que je me suis inscrit avec l'email "nouveau@example.com" Et que je n'ai pas vérifié mon email

Quand je demande à renvoyer l'email de vérification

Alors un nouvel email de vérification est envoyé Et le précédent lien est invalidé

## 14. Limite de renvoi d'email de vérification

Étant donné que je me suis inscrit avec l'email "nouveau@example.com" Et que j'ai déjà renvoyé l'email de vérification 3 fois aujourd'hui

Quand je demande à renvoyer l'email de vérification une 4ème fois

Alors la demande échoue Et je vois le message "Vous avez atteint la limite de 3 renvois par jour"

## 15. Expiration du lien de vérification

Étant donné que je me suis inscrit il y a 8 jours Et que je n'ai pas vérifié mon email

Quand j'essaie d'utiliser le lien de vérification

Alors la vérification échoue Et je vois le message "Ce lien a expiré" Et je peux demander un nouveau lien

# Récupération de compte

> En tant qu'utilisateur ayant oublié son mot de passe Je veux pouvoir réinitialiser mon mot de passe via email Afin de récupérer l'accès à mon compte

14 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et qu'un utilisateur existe avec l'email "user@test.fr"

## 1. Demander la réinitialisation du mot de passe

Quand je clique sur "Mot de passe oublié" Et que je saisis mon email "user@test.fr"

Alors je reçois un email avec un lien de réinitialisation Et le lien expire dans 1 heure Et je vois le message "Email de réinitialisation envoyé"

## 2. Email inexistant lors de la demande de réinitialisation

Quand je demande une réinitialisation pour l'email "inexistant@test.fr"

Alors je vois le même message "Email de réinitialisation envoyé" Mais aucun email n'est envoyé (sécurité - pas d'énumération d'emails)

## 3. Réinitialiser le mot de passe avec un lien valide

Étant donné que j'ai demandé une réinitialisation de mot de passe Et que j'ai reçu le lien de réinitialisation

Quand je clique sur le lien Et que je saisis un nouveau mot de passe "NouveauPass123" Et que je confirme le nouveau mot de passe "NouveauPass123"

Alors mon mot de passe est modifié avec succès Et je suis déconnecté de tous mes appareils sauf celui en cours Et je reçois un email de confirmation de changement

## 4. Lien de réinitialisation expiré

Étant donné que j'ai demandé une réinitialisation il y a 2 heures

Quand j'essaie d'utiliser le lien

Alors je vois le message "Ce lien a expiré" Et je peux demander un nouveau lien

## 5. Nouveau mot de passe ne respecte pas les règles

Étant donné que j'ai un lien de réinitialisation valide

Quand je saisis un nouveau mot de passe "faible"

Alors la réinitialisation échoue Et je vois le message "Le mot de passe doit contenir au moins 8 caractères, 1 majuscule et 1 chiffre"

## 6. Confirmation du mot de passe ne correspond pas

Étant donné que j'ai un lien de réinitialisation valide

Quand je saisis un nouveau mot de passe "NouveauPass123" Et que je confirme avec un mot de passe différent "AutrePass123"

Alors la réinitialisation échoue Et je vois le message "Les mots de passe ne correspondent pas"

## 7. Limite de demandes de réinitialisation

Étant donné que j'ai déjà demandé 3 réinitialisations dans la dernière heure

Quand je demande une 4ème réinitialisation

Alors la demande échoue Et je vois le message "Maximum 3 demandes par heure. Réessayez plus tard."

## 8. Compteur de demandes se réinitialise après 1 heure

Étant donné que j'ai demandé 3 réinitialisations Et que 1 heure s'est écoulée

Quand je demande une nouvelle réinitialisation

Alors la demande réussit Et je reçois un email avec un nouveau lien

## 9. Email de notification de changement de mot de passe

Étant donné que je viens de réinitialiser mon mot de passe

Alors je reçois un email de confirmation avec:

    | sujet | Votre mot de passe a été modifié |
    |---|---|
    | contenu_contient | Votre mot de passe a été modifié |
    | date_heure | présente |
    | appareil | présent |
    | localisation | présente |
    | action_urgence | Lien si ce n'était pas vous |

## 10. Notification push si changement depuis appareil non reconnu

Étant donné que je me suis toujours connecté depuis mon iPhone Et que je réinitialise mon mot de passe depuis un PC Windows

Alors je reçois une notification push sur mon iPhone avec:

    | titre | Mot de passe modifié |
    |---|---|
    | message | Depuis Windows - Paris, France |
    | action | Sécuriser le compte si ce n'est pas vous |

## 11. Déconnexion de tous les appareils après réinitialisation

Étant donné que je suis connecté sur 4 appareils différents Et que je réinitialise mon mot de passe depuis un navigateur web

Alors les 3 autres appareils sont déconnectés immédiatement Et seule la session du navigateur web reste active Et je vois le message "Vous avez été déconnecté des autres appareils par sécurité"

## 12. Lien de réinitialisation invalide si déjà utilisé

Étant donné que j'ai réinitialisé mon mot de passe avec un lien

Quand j'essaie de réutiliser le même lien

Alors je vois le message "Ce lien a déjà été utilisé" Et je peux demander un nouveau lien si nécessaire

## 13. Nouveau lien invalide l'ancien

Étant donné que j'ai demandé une réinitialisation et reçu un lien

Quand je demande une nouvelle réinitialisation

Alors l'ancien lien est invalidé Et seul le nouveau lien fonctionne

## 14. Réinitialisation débloque un compte bloqué

Étant donné que mon compte est bloqué après 5 tentatives de connexion

Quand je réinitialise mon mot de passe via email

Alors le blocage est levé immédiatement Et je peux me connecter avec le nouveau mot de passe Et le compteur de tentatives est remis à 0

# Gestion des sessions et tokens

> En tant qu'utilisateur connecté Je veux que mes sessions soient sécurisées et gérées automatiquement Afin de maintenir l'accès à l'application sans friction

13 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et que je suis connecté avec succès

## 1. Access token expire après 15 minutes

Étant donné que j'ai reçu un access token Et que 15 minutes se sont écoulées

Quand je fais une requête API avec cet access token

Alors la requête échoue avec le code 401 Et je vois le message "Token expiré"

## 2. Refresh automatique du token avec refresh token

Étant donné que mon access token a expiré Et que mon refresh token est valide

Quand l'application demande un nouveau access token

Alors je reçois un nouvel access token valide pour 15 minutes Et je reçois un nouveau refresh token (rotation) Et l'ancien refresh token est invalidé

## 3. Refresh token expire après 30 jours d'inactivité

Étant donné que je me suis connecté il y a 30 jours Et que je n'ai pas utilisé l'application depuis

Quand j'essaie d'utiliser mon refresh token

Alors la requête échoue Et je dois me reconnecter avec email/password

## 4. Prolongation automatique de la session si l'app est utilisée

Étant donné que je me suis connecté il y a 25 jours Et que j'utilise l'application régulièrement

Quand je fais une requête API

Alors ma session est automatiquement prolongée Et mon refresh token reste valide

## 5. Détection de token replay attack

Étant donné que j'ai rafraîchi mon token Et que j'ai reçu un nouveau refresh token

Quand j'essaie de réutiliser l'ancien refresh token

Alors la requête échoue Et je vois le message "Token invalide ou révoqué" Et toutes mes sessions sont révoquées par sécurité

## 6. Voir la liste des appareils connectés

Étant donné que je suis connecté sur 3 appareils différents

Quand je consulte la liste de mes appareils connectés

Alors je vois 3 appareils avec les informations suivantes:

    | information | exemple |
    |---|---|
    | OS | iOS 17.1 |
    | Navigateur | Safari |
    | Dernière connexion | Il y a 2 heures |
    | Localisation | Paris, France (IP visible) |

## 7. Révoquer un appareil spécifique

Étant donné que je suis connecté sur mon iPhone et mon iPad

Quand je révoque la session de mon iPad depuis les paramètres

Alors la session iPad est immédiatement déconnectée Et ma session iPhone reste active

## 8. Déconnecter tous les appareils sauf celui en cours

Étant donné que je suis connecté sur 4 appareils

Quand je clique sur "Déconnecter tous les appareils"

Alors les 3 autres appareils sont déconnectés Et seul l'appareil actuel reste connecté

## 9. Alerte de connexion depuis nouveau device

Étant donné que je me suis toujours connecté depuis Paris

Quand je me connecte depuis un nouvel appareil à Lyon

Alors je reçois une notification push sur mes autres appareils Et je reçois un email avec:

    | sujet | Nouvelle connexion détectée |
    |---|---|
    | localisation | Lyon, France |
    | appareil | Android 14 - Chrome |
    | action | Lien pour révoquer la session |

## 10. Alerte de connexion suspecte depuis pays différent

Étant donné que je me suis toujours connecté depuis la France

Quand je me connecte depuis un appareil aux États-Unis

Alors je reçois une notification push immédiate Et je reçois un email d'alerte de sécurité Et la nouvelle session nécessite une validation 2FA même si désactivée

## 11. Déconnexion après 30 jours d'inactivité totale

Étant donné que je ne me suis pas connecté depuis 30 jours

Quand j'ouvre l'application

Alors je suis automatiquement déconnecté Et je dois me reconnecter avec email/password Et je vois le message "Session expirée après 30 jours d'inactivité"

## 12. Sessions multiples simultanées autorisées

Étant donné que je suis connecté sur:

    | appareil |
    |---|
    | iPhone |
    | iPad |
    | PC Windows (Web) |

Quand je fais des actions sur les 3 appareils simultanément

Alors toutes les sessions fonctionnent sans conflit Et chaque appareil maintient sa propre session

## 13. Validation de JWT via Zitadel

Étant donné que j'ai reçu un access token JWT

Quand l'API RoadWave valide le token

Alors la validation est faite localement avec la clé publique Zitadel Et aucune requête externe n'est effectuée (performance) Et le token contient les claims suivants:

    | claim | valeur_exemple |
    |---|---|
    | sub | user-id-123 |
    | email | user@test.fr |
    | exp | timestamp + 15 minutes |
    | iss | zitadel.roadwave.com |

# Authentification à deux facteurs (2FA)

> En tant qu'utilisateur soucieux de sécurité Je veux activer la 2FA sur mon compte Afin de protéger mon accès même si mon mot de passe est compromis

16 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et que je suis connecté à mon compte

## 1. Activer la 2FA TOTP (Time-based One-Time Password)

Étant donné que la 2FA n'est pas activée sur mon compte

Quand je choisis d'activer la 2FA TOTP

Alors je vois un QR code à scanner Et je vois le secret partagé en texte clair (backup) Et je dois entrer un code de vérification depuis mon app authenticator

Quand je saisis un code TOTP valide

Alors la 2FA TOTP est activée avec succès Et je reçois des codes de backup (10 codes)

## 2. Connexion avec 2FA TOTP activée

Étant donné que la 2FA TOTP est activée sur mon compte

Quand je me connecte avec email/password

Alors je suis redirigé vers la page de saisie du code 2FA

Quand je saisis un code TOTP valide de mon authenticator

Alors je suis connecté avec succès

## 3. Connexion échouée avec code TOTP invalide

Étant donné que la 2FA TOTP est activée

Quand je me connecte avec email/password Et que je saisis un code TOTP invalide "000000"

Alors la connexion échoue Et je vois le message "Code d'authentification invalide" Et je peux réessayer

## 4. Utiliser un code de backup pour 2FA

Étant donné que la 2FA TOTP est activée Et que j'ai perdu l'accès à mon authenticator

Quand je me connecte avec email/password Et que je clique sur "Utiliser un code de backup" Et que je saisis un code de backup valide

Alors je suis connecté avec succès Et le code de backup utilisé est invalidé Et il me reste 9 codes de backup

## 5. Activer la 2FA par email

Étant donné que la 2FA n'est pas activée

Quand je choisis d'activer la 2FA par email

Alors la 2FA email est activée immédiatement Et je vois le message "2FA email activée. Vous recevrez un code à chaque connexion"

## 6. Connexion avec 2FA email

Étant donné que la 2FA email est activée

Quand je me connecte avec email/password

Alors je reçois un email avec un code à 6 chiffres Et le code expire dans 10 minutes Et je dois saisir ce code pour terminer la connexion

## 7. Code 2FA email expiré

Étant donné que la 2FA email est activée Et que je me suis connecté avec email/password Et que j'ai reçu un code 2FA par email il y a 11 minutes

Quand je saisis ce code

Alors la connexion échoue Et je vois le message "Code expiré. Demandez un nouveau code."

## 8. Renvoyer le code 2FA email

Étant donné que la 2FA email est activée Et que je suis sur la page de saisie du code 2FA

Quand je clique sur "Renvoyer le code"

Alors je reçois un nouveau code par email Et l'ancien code est invalidé

## 9. Ajouter un appareil de confiance (skip 2FA pendant 30 jours)

Étant donné que la 2FA TOTP est activée

Quand je me connecte avec email/password et code TOTP Et que je coche "Ne plus demander sur cet appareil"

Alors je suis connecté avec succès Et cet appareil est enregistré comme "appareil de confiance"

Quand je me reconnecte dans les 30 jours suivants sur ce même appareil

Alors je ne dois pas saisir de code 2FA

## 10. Appareil de confiance expire après 30 jours

Étant donné que j'ai enregistré un appareil de confiance il y a 31 jours

Quand je me connecte depuis cet appareil

Alors je dois saisir un code 2FA Et je vois le message "Appareil de confiance expiré. Veuillez vous authentifier"

## 11. Voir la liste des appareils de confiance

Étant donné que j'ai enregistré 3 appareils de confiance

Quand je consulte mes paramètres de sécurité

Alors je vois la liste de mes 3 appareils de confiance avec:

    | information | exemple |
    |---|---|
    | Nom | iPhone 13 - Safari |
    | Date ajout | 15 janvier 2026 |
    | Dernière vue | Il y a 2 heures |
    | Expire le | 14 février 2026 |

## 12. Révoquer un appareil de confiance

Étant donné que j'ai un iPhone enregistré comme appareil de confiance

Quand je révoque cet appareil depuis les paramètres

Alors l'appareil est supprimé de la liste

Quand je me reconnecte depuis cet iPhone

Alors je dois saisir un code 2FA

## 13. Révoquer tous les appareils de confiance

Étant donné que j'ai 5 appareils de confiance enregistrés

Quand je clique sur "Révoquer tous les appareils de confiance"

Alors tous les appareils sont révoqués Et je vois le message "Tous les appareils de confiance ont été révoqués"

## 14. 2FA forcée pour connexion suspecte malgré appareil de confiance

Étant donné que j'ai un appareil de confiance enregistré en France Et que je me connecte depuis ce même appareil mais avec une IP américaine

Quand je tente de me connecter

Alors la 2FA est requise malgré l'appareil de confiance Et je vois le message "Connexion suspecte détectée. Authentification requise."

## 15. Désactiver la 2FA

Étant donné que la 2FA TOTP est activée

Quand je désactive la 2FA depuis mes paramètres Et que je confirme avec mon mot de passe

Alors la 2FA est désactivée Et tous les codes de backup sont invalidés Et tous les appareils de confiance sont révoqués

## 16. Régénérer les codes de backup

Étant donné que la 2FA est activée Et que j'ai utilisé 8 codes de backup sur 10

Quand je demande à régénérer les codes de backup

Alors je reçois 10 nouveaux codes Et tous les anciens codes (utilisés ou non) sont invalidés

# Vérification d'email

> En tant qu'utilisateur inscrit Je veux vérifier mon adresse email Afin d'accéder à toutes les fonctionnalités selon mon rôle

10 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible

## 1. Auditeur avec email non vérifié - lecture illimitée

Étant donné que je suis un auditeur avec email non vérifié

Quand j'essaie d'écouter du contenu

Alors je peux écouter tous les contenus sans limite

## 2. Auditeur avec email non vérifié - création limitée à 5 contenus

Étant donné que je suis un auditeur avec email non vérifié Et que j'ai créé 4 contenus

Quand je crée un 5ème contenu

Alors le contenu est créé avec succès Mais quand j'essaie de créer un 6ème contenu Alors la création échoue Et je vois le message "Vérifiez votre email pour créer plus de contenus"

## 3. Rappel de vérification après le 3ème contenu créé

Étant donné que je suis un auditeur avec email non vérifié Et que j'ai créé 2 contenus

Quand je crée mon 3ème contenu

Alors le contenu est créé avec succès Et je vois une notification in-app "Vérifiez votre email pour débloquer la création illimitée"

## 4. Auditeur vérifie son email

Étant donné que je suis un auditeur avec email non vérifié Et que j'ai reçu un lien de vérification

Quand je clique sur le lien de vérification dans l'email

Alors mon email est marqué comme vérifié Et je vois le message "Email vérifié avec succès" Et toutes les fonctionnalités sont débloquées

## 5. Créateur doit vérifier son email sous 7 jours pour monétisation

Étant donné que je suis inscrit comme créateur Et que mon email n'est pas vérifié Et que je remplis les conditions de monétisation

Quand j'essaie d'accéder au programme de monétisation

Alors l'accès est refusé Et je vois le message "Vérifiez votre email pour accéder à la monétisation"

## 6. Créateur ne peut pas publier de contenus illimités sans vérification

Étant donné que je suis un créateur avec email non vérifié Et que j'ai créé 5 contenus

Quand j'essaie de créer un 6ème contenu

Alors la création échoue Et je vois le message "Vérifiez votre email pour publier des contenus illimités"

## 7. Créateur vérifie son email et déboque tout

Étant donné que je suis un créateur avec email non vérifié Et que j'ai reçu un lien de vérification

Quand je clique sur le lien de vérification

Alors mon email est marqué comme vérifié Et je peux publier des contenus illimités Et je peux accéder au programme de monétisation si j'en remplis les conditions

## 8. KYC impossible sans email vérifié

Étant donné que je suis un créateur avec email non vérifié

Quand j'essaie de compléter le KYC via Mangopay

Alors l'accès au KYC est refusé Et je vois le message "Vérifiez votre email avant de procéder au KYC"

## 9. Tentative de vérification avec un lien déjà utilisé

Étant donné que j'ai déjà vérifié mon email avec un lien

Quand j'essaie de réutiliser le même lien de vérification

Alors la vérification échoue Et je vois le message "Ce lien a déjà été utilisé"

## 10. Auditeur vérifié peut créer plus de 5 contenus

Étant donné que je suis un auditeur avec email vérifié Et que j'ai créé 10 contenus

Quand je crée un 11ème contenu

Alors le contenu est créé avec succès Et il n'y a pas de limite de création

# Métadonnées et publication de contenu

> En tant que créateur Je veux remplir les métadonnées de mon contenu Afin de le publier sur RoadWave

34 scénarios (32 standards, 2 plans)

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et que je suis un créateur connecté Et que mon fichier audio est encodé et prêt

## 1. Publication avec toutes les métadonnées obligatoires

Quand je remplis les métadonnées suivantes:

    | champ | valeur |
    |---|---|
    | Titre | Histoire de la Tour Eiffel |
    | Type géo | Ancré |
    | Zone | Point GPS (48.8584, 2.2945, 500m) |
    | Tags | Voyage, Culture générale |
    | Classification âge | Tout public |

Alors la publication réussit Et mon contenu est soumis pour validation

## 2. Titre valide entre 5 et 100 caractères

Quand je saisis un titre de 50 caractères

Alors le titre est accepté Et la validation passe

## 3. Titre trop court (<5 caractères)

Quand je saisis un titre de 4 caractères "Test"

Alors la publication échoue Et je vois le message "Le titre doit contenir entre 5 et 100 caractères"

## 4. Titre trop long (>100 caractères)

Quand je saisis un titre de 101 caractères

Alors la publication échoue Et je vois le message "Le titre doit contenir entre 5 et 100 caractères"

## 5. Titre à exactement 5 caractères accepté

Quand je saisis un titre de exactement 5 caractères "Titre"

Alors le titre est accepté

## 6. Titre à exactement 100 caractères accepté

Quand je saisis un titre de exactement 100 caractères

Alors le titre est accepté

## 7. Sélectionner type géo "Ancré"

Quand je sélectionne le type géo "Ancré"

Alors le système applique une pondération géo de 0.7 Et je dois définir une zone de diffusion précise

## 8. Sélectionner type géo "Contextuel"

Quand je sélectionne le type géo "Contextuel"

Alors le système applique une pondération géo de 0.5 Et je peux définir une zone ville/département/région

## 9. Sélectionner type géo "Neutre"

Quand je sélectionne le type géo "Neutre"

Alors le système applique une pondération géo de 0.2 Et je peux définir une zone nationale

## 10. Zone diffusion - Point GPS avec rayon

Quand je choisis "Point GPS" Et que je définis les coordonnées (48.8584, 2.2945) Et que je définis un rayon de 500 mètres

Alors la zone est validée Et le contenu sera diffusé dans un rayon de 500m autour du point

## 11. Zone diffusion - Rayon minimum 100m

Quand je définis un rayon de 50 mètres (< 100m)

Alors la validation échoue Et je vois le message "Le rayon doit être entre 100m et 10km"

## 12. Zone diffusion - Rayon maximum 10km

Quand je définis un rayon de 15 km (> 10km)

Alors la validation échoue Et je vois le message "Le rayon doit être entre 100m et 10km"

## 13. Zone diffusion - Ville depuis référentiel INSEE

Quand je choisis "Ville"

Alors je vois une liste de villes du référentiel INSEE

Quand je sélectionne "Paris (75000)"

Alors la zone est définie sur toute la ville de Paris

## 14. Zone diffusion - Département

Quand je choisis "Département" Et que je sélectionne "Ille-et-Vilaine (35)"

Alors la zone couvre tout le département 35

## 15. Zone diffusion - Région

Quand je choisis "Région" Et que je sélectionne "Bretagne"

Alors la zone couvre toute la région Bretagne

## 16. Zone diffusion - National

Quand je choisis "National"

Alors la zone couvre toute la France Et aucune restriction géographique n'est appliquée

## 17. Zones mutuellement exclusives

Étant donné que j'ai sélectionné "Point GPS"

Quand j'essaie de sélectionner également "Ville"

Alors la première sélection est remplacée Et seule "Ville" reste active

## 18. Sélectionner 1 tag minimum

Quand je sélectionne 1 tag "Voyage"

Alors la validation passe Et le contenu est tagué "Voyage"

## 19. Sélectionner 3 tags maximum

Quand je sélectionne 3 tags "Automobile", "Technologie", "Sport"

Alors la validation passe Et le contenu est tagué avec les 3 tags

## 20. Impossible de sélectionner 0 tag

Quand j'essaie de publier sans sélectionner de tag

Alors la publication échoue Et je vois le message "Vous devez sélectionner entre 1 et 3 tags"

## 21. Impossible de sélectionner 4 tags

Quand j'essaie de sélectionner 4 tags

Alors le 4ème tag ne peut pas être ajouté Et je vois le message "Maximum 3 tags"

## 22. Tags disponibles dans la liste

Quand je consulte la liste des tags

Alors je vois les tags suivants:

    | tag |
    |---|
    | Automobile |
    | Voyage |
    | Famille |
    | Amour |
    | Musique |
    | Économie |
    | Cryptomonnaie |
    | Politique |
    | Culture générale |
    | Sport |
    | Technologie |
    | Santé |

## 23. Classification âge obligatoire

Quand j'essaie de publier sans classification âge

Alors la publication échoue Et je vois le message "Vous devez sélectionner une classification d'âge"

## 24. 📋 Plan: Sélectionner classification âge

Quand je sélectionne la classification ""

Alors le contenu sera visible pour ""

📊 Exemples de données:

classification | public_cible
--- | ---
Tout public | Tous les utilisateurs
13+ | Utilisateurs 13 ans et plus
16+ | Utilisateurs 16 ans et plus
18+ | Utilisateurs 18 ans et plus

## 25. Image de couverture auto-générée selon type géo

Étant donné que je choisis le type géo "Ancré" Et que mon tag principal est "Voyage"

Quand la publication est soumise

Alors une image de couverture est générée automatiquement:

    | paramètre | valeur |
    |---|---|
    | Icône | 📍 (Ancré) |
    | Couleur | Bleu-vert (Voyage) |
    | Format | 800×800px PNG |

## 26. Image de couverture type Contextuel

Étant donné que je choisis "Contextuel"

Quand l'image est générée

Alors l'icône est 🌍 (Contextuel)

## 27. Image de couverture type Neutre

Étant donné que je choisis "Neutre"

Quand l'image est générée

Alors l'icône est 🎧 (Neutre)

## 28. 📋 Plan: Couleur selon tag principal

Étant donné que mon tag principal est ""

Quand l'image est générée

Alors la couleur de fond est ""

📊 Exemples de données:

tag | couleur
--- | ---
Automobile | Bleu
Voyage | Vert
Musique | Rouge
Économie | Gris
Sport | Orange

## 29. Champs optionnels non obligatoires

Quand je publie sans description Et sans image de couverture personnalisée

Alors la publication réussit Et les champs optionnels restent vides Et une image par défaut est générée

## 30. Temps de publication estimé 2 minutes

Étant donné que mon fichier audio est prêt

Quand je commence à remplir les métadonnées

Alors je peux publier en environ 2 minutes

## 31. Publication rapide sans friction

Quand je publie mon premier contenu

Alors aucun champ complexe n'est demandé Et je ne suis pas bloqué sur description ou image Et la publication est fluide

## 32. Prévisualisation avant publication

Étant donné que j'ai rempli toutes les métadonnées

Quand je clique sur "Prévisualiser"

Alors je vois un aperçu de mon contenu:

    | élément | affiché |
    |---|---|
    | Titre | ✅ |
    | Image couverture | ✅ |
    | Tags | ✅ |
    | Zone diffusion | ✅ |
    | Durée audio | ✅ |
    | Classification | ✅ |

## 33. Enregistrer brouillon

Étant donné que j'ai commencé à remplir les métadonnées

Quand je clique sur "Enregistrer brouillon"

Alors mes métadonnées sont sauvegardées Et je peux reprendre la publication plus tard

## 34. Reprendre brouillon

Étant donné que j'ai un brouillon sauvegardé

Quand j'accède à mes contenus

Alors je vois le brouillon avec statut "📝 Brouillon" Et je peux reprendre la publication

# Modification et suppression de contenu

> En tant que créateur Je veux pouvoir modifier ou supprimer mes contenus Afin de garder le contrôle sur mes publications

30 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et que je suis un créateur connecté Et que j'ai publié un contenu

## 1. Modifier le titre d'un contenu

Étant donné que mon contenu a le titre "Histoire de Paris"

Quand je modifie le titre en "Histoire fascinante de Paris"

Alors la modification est enregistrée immédiatement Et je vois le message "Titre modifié avec succès"

## 2. Correction de coquilles dans le titre

Étant donné que mon titre contient une faute "Histoore de Paris"

Quand je corrige en "Histoire de Paris"

Alors la modification est acceptée Et le titre corrigé est affiché

## 3. Ajouter une description ultérieurement

Étant donné que j'ai publié sans description

Quand j'ajoute une description "Découvrez l'histoire de la capitale"

Alors la description est enregistrée Et elle est visible sur la page du contenu

## 4. Modifier la description existante

Étant donné que mon contenu a déjà une description

Quand je modifie la description

Alors la nouvelle description remplace l'ancienne Et la modification est immédiate

## 5. Modifier les tags pour ajuster pertinence

Étant donné que mon contenu est tagué "Sport", "Musique"

Quand je change les tags en "Sport", "Santé"

Alors les nouveaux tags sont appliqués Et l'algorithme utilise les nouveaux tags pour recommandations

## 6. Personnaliser l'image de couverture

Étant donné que mon contenu a une image auto-générée

Quand j'uploade une image personnalisée 800×800px

Alors l'image personnalisée remplace l'image par défaut Et elle est visible sur le contenu

## 7. Impossible de modifier l'audio

Étant donné que mon contenu audio est publié

Quand j'essaie de remplacer le fichier audio

Alors la modification est refusée Et je vois le message "L'audio ne peut pas être modifié après publication"

## 8. Raison - Éviter fraude audio

Étant donné que je veux changer l'audio après validation

Quand j'essaie de modifier

Alors le système refuse pour éviter:

    | risque |
    |---|
    | Uploader contenu validé puis remplacer spam |
    | Fraude sur l'intégrité du contenu |

## 9. Impossible de modifier la zone de diffusion

Étant donné que mon contenu est diffusé à Paris

Quand j'essaie de changer la zone en "National"

Alors la modification est refusée Et je vois le message "La zone de diffusion ne peut pas être modifiée"

## 10. Raison - Éviter manipulation algorithme

Étant donné que je veux changer ma zone

Quand j'essaie de modifier

Alors le système refuse pour éviter:

    | manipulation |
    |---|
    | Créer "Local Paris" puis changer en "National" |
    | Boost artificiel de visibilité |

## 11. Impossible de modifier le type géo

Étant donné que mon contenu est type "Neutre" (pondération 0.2)

Quand j'essaie de changer en "Ancré" (pondération 0.7)

Alors la modification est refusée Et je vois le message "Le type géographique ne peut pas être modifié"

## 12. Raison - Éviter abus de pondération

Étant donné que je veux changer le type géo

Quand j'essaie de modifier

Alors le système refuse pour éviter:

    | abus |
    |---|
    | Créer "Neutre" puis passer en "Ancré" |
    | Manipulation de la pondération algorithme |

## 13. Impossible de modifier la classification âge

Étant donné que mon contenu est classé "Tout public"

Quand j'essaie de changer en "18+"

Alors la modification est refusée Et je vois le message "La classification d'âge ne peut pas être modifiée"

## 14. Raison - Sécurité mineurs

Étant donné que je veux changer la classification

Quand j'essaie de modifier

Alors le système refuse pour garantir:

    | protection |
    |---|
    | Classification vérifiée en modération |
    | Pas de contournement validation |
    | Sécurité des mineurs |

## 15. Solution si besoin de changer audio/zone/classification

Étant donné que je veux absolument changer l'audio

Quand je consulte les options

Alors je vois "Supprimer et republier le contenu" Et c'est la seule solution disponible

## 16. Republication après suppression - créateur <3 validations

Étant donné que je suis un nouveau créateur (2 contenus validés) Et que je supprime puis republie un contenu

Quand je republie avec les modifications

Alors le contenu repasse en file de validation Et une nouvelle validation est effectuée

## 17. Republication après suppression - créateur vérifié

Étant donné que je suis créateur vérifié (≥3 contenus validés) Et que je supprime puis republie un contenu

Quand je republie avec les modifications

Alors le contenu est publié immédiatement Et aucune validation préalable n'est requise

## 18. Suppression de contenu immédiate

Quand je clique sur "Supprimer le contenu" Et que je confirme la suppression

Alors le contenu est supprimé immédiatement Et disparaît de la liste publique

## 19. Confirmation avant suppression

Quand je clique sur "Supprimer"

Alors je vois un message de confirmation:

    | titre | Êtes-vous sûr ? |
    |---|---|
    | message | Cette action est définitive |
    | warning | Le contenu sera supprimé définitivement |
    | actions | Confirmer / Annuler |

## 20. Suppression définitive et non réversible

Étant donné que j'ai supprimé un contenu

Quand j'essaie de le récupérer

Alors la récupération est impossible Et le contenu est définitivement perdu

## 21. Suppression BDD + CDN sous 5 minutes

Quand je supprime un contenu

Alors l'entrée en base de données est marquée "deleted" Et les fichiers CDN sont marqués pour suppression Et la suppression effective a lieu sous 5 minutes

## 22. Historique auditeurs conservé anonymisé

Étant donné que 1000 personnes ont écouté mon contenu

Quand je supprime le contenu

Alors leur historique est conservé Mais marqué "Contenu supprimé par créateur" Et la durée d'écoute est conservée pour leurs stats

## 23. Analytics plateforme anonymisées conservées

Étant donné que mon contenu a généré 10K écoutes

Quand je supprime le contenu

Alors les métriques globales sont conservées anonymement:

    | métrique | conservée |
    |---|---|
    | Total écoutes | ✅ (anonyme) |
    | Durée totale | ✅ (anonyme) |
    | Catégorie | ✅ (anonyme) |
    | Auteur | ❌ (anonymisé) |

Et c'est conforme RGPD

## 24. Fichiers CDN supprimés sous 24h

Étant donné que mon contenu est supprimé

Quand 24 heures s'écoulent

Alors tous les fichiers audio sont purgés du CDN Bunny Et l'espace de stockage est libéré

## 25. Pas de notification aux auditeurs

Étant donné que 500 utilisateurs ont écouté mon contenu

Quand je supprime le contenu

Alors aucune notification n'est envoyée aux auditeurs Et il n'y a pas d'effet Streisand

## 26. Auditeur tente de réécouter contenu supprimé

Étant donné qu'un auditeur a écouté mon contenu Et que j'ai supprimé ce contenu

Quand l'auditeur tente de le réécouter depuis son historique

Alors il voit le message "Ce contenu n'est plus disponible" Et la lecture est impossible

## 27. Historique auditeur conserve trace

Étant donné qu'un auditeur a écouté mon contenu le 15 janvier Et que je supprime le contenu le 20 janvier

Quand l'auditeur consulte son historique

Alors il voit "Vous avez écouté ce contenu le 15 janvier 2026" Et le titre est remplacé par "Contenu supprimé" Et la date d'écoute est conservée

## 28. Statistiques créateur après suppression

Étant donné que j'ai publié 10 contenus Et que je supprime 2 contenus

Quand je consulte mes statistiques globales

Alors je vois:

    | métrique | valeur |
    |---|---|
    | Contenus publiés | 8 (actifs) |
    | Total historique | 10 |
    | Suppressions | 2 |

Et l'historique des suppressions est visible

## 29. Limite de modifications par contenu

Étant donné que j'ai modifié un titre 10 fois

Quand j'essaie de modifier une 11ème fois

Alors la modification est acceptée

## 30. Historique des modifications visible

Étant donné que j'ai modifié un contenu plusieurs fois

Quand je consulte l'historique

Alors je vois:

    | date | modification |
    |---|---|
    | 21/01/2026 | Titre changé |
    | 20/01/2026 | Tags modifiés |
    | 19/01/2026 | Description ajoutée |

Et je peux tracer toutes les modifications

# Upload et encodage de contenu audio

> En tant que créateur Je veux uploader mon contenu audio Afin qu'il soit encodé et disponible pour les auditeurs

29 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et que je suis un créateur connecté

## 1. Upload fichier MP3 valide

Quand j'uploade un fichier MP3 de 50 MB et 30 minutes

Alors l'upload réussit Et le fichier est envoyé vers Bunny Storage temporaire Et un job d'encodage asynchrone est lancé

## 2. Upload fichier AAC valide (.aac)

Quand j'uploade un fichier AAC de 80 MB et 1 heure

Alors l'upload réussit Et le fichier est accepté Et l'encodage démarre

## 3. Upload fichier M4A valide

Quand j'uploade un fichier M4A de 100 MB et 2 heures

Alors l'upload réussit Et le fichier est traité comme AAC Et l'encodage démarre

## 4. Rejet fichier WAV (non supporté)

Quand j'essaie d'uploader un fichier WAV

Alors l'upload échoue Et je vois le message "Format non supporté. Utilisez MP3 ou AAC (.mp3, .aac, .m4a)"

## 5. Rejet fichier FLAC (non supporté)

Quand j'essaie d'uploader un fichier FLAC

Alors l'upload échoue Et je vois le message "Format non supporté. Utilisez MP3 ou AAC (.mp3, .aac, .m4a)"

## 6. Validation taille maximale 200 MB

Quand j'essaie d'uploader un fichier MP3 de 201 MB

Alors l'upload échoue Et je vois le message "Fichier trop volumineux (max 200 MB)"

## 7. Upload à la limite de 200 MB accepté

Quand j'uploade un fichier MP3 de exactement 200 MB

Alors l'upload réussit Et le fichier est accepté

## 8. Validation durée maximale 4 heures

Quand j'essaie d'uploader un fichier de 4h 10min

Alors l'upload échoue Et je vois le message "Durée trop longue (max 4 heures)"

## 9. Upload à la limite de 4h accepté

Quand j'uploade un fichier de exactement 4 heures

Alors l'upload réussit Et le fichier est accepté

## 10. Validation format côté client

Quand je sélectionne un fichier dans l'interface

Alors la validation du format est faite immédiatement côté client Et je suis informé avant même de lancer l'upload si le format est invalide

## 11. Double validation côté backend

Étant donné qu'un fichier a passé la validation client

Quand le backend reçoit le fichier

Alors une validation supplémentaire est effectuée Et le format et l'intégrité sont vérifiés

## 12. Pipeline d'encodage - étape 1 upload

Quand j'uploade un fichier MP3 valide

Alors le fichier est stocké temporairement dans Bunny Storage Et un job d'encodage est mis en file d'attente

## 13. Pipeline d'encodage - validation format

Étant donné qu'un job d'encodage est lancé

Quand le worker Go traite le fichier

Alors le format est validé avec FFmpeg Et l'intégrité du fichier est vérifiée

## 14. Pipeline d'encodage - génération 3 profils Opus

Étant donné qu'un fichier audio est validé

Quand l'encodage démarre

Alors 3 profils Opus sont générés:

    | qualité | bitrate | usage |
    |---|---|---|
    | Basse | 24 kbps | 2G/Edge |
    | Standard | 48 kbps | 3G |
    | Haute | 64 kbps | 4G/5G |

## 15. Pipeline d'encodage - génération segments HLS

Étant donné que les profils Opus sont générés

Quand l'encodage continue

Alors un fichier manifest .m3u8 est créé Et des segments .ts sont générés Et le contenu est prêt pour streaming HLS

## 16. Pipeline d'encodage - génération image par défaut

Étant donné que l'encodage est en cours

Quand les métadonnées sont traitées

Alors une image de couverture par défaut est générée Et l'image fait 800×800px au format PNG

## 17. Pipeline d'encodage - suppression fichier original

Étant donné que l'encodage est terminé avec succès

Quand tous les fichiers de sortie sont générés

Alors le fichier original MP3/AAC est supprimé Et seuls les profils Opus et HLS sont conservés Et l'espace de stockage est économisé

## 18. Temps d'encodage contenu 5 minutes

Étant donné qu'un fichier de 5 minutes est uploadé

Quand l'encodage démarre

Alors l'encodage prend environ 30 secondes Et je reçois une notification "Contenu prêt à publier"

## 19. Temps d'encodage podcast 1 heure

Étant donné qu'un fichier de 1 heure est uploadé

Quand l'encodage démarre

Alors l'encodage prend environ 5 minutes Et une barre de progression est affichée

## 20. Temps d'encodage podcast 4 heures

Étant donné qu'un fichier de 4 heures est uploadé

Quand l'encodage démarre

Alors l'encodage prend environ 20 minutes Et je peux fermer l'app (traitement asynchrone)

## 21. Notification "Contenu prêt à publier"

Étant donné que mon contenu est en cours d'encodage

Quand l'encodage se termine avec succès

Alors je reçois une notification push "✅ Votre contenu est prêt à publier" Et je peux accéder à l'interface de publication

## 22. Échec d'encodage - fichier corrompu

Étant donné qu'un fichier MP3 corrompu est uploadé

Quand l'encodage démarre

Alors l'encodage échoue Et je reçois une notification "❌ Erreur d'encodage: fichier corrompu" Et le fichier temporaire est supprimé

## 23. Écoute accélérée - vitesses disponibles

Étant donné qu'un contenu est publié

Quand un auditeur écoute le contenu

Alors il peut choisir parmi les vitesses:

    | vitesse | usage |
    |---|---|
    | 0.75x | Compréhension difficile |
    | 1.0x | Normal (défaut) |
    | 1.25x | Gain léger |
    | 1.5x | Podcasts longs |
    | 2.0x | Survol rapide |

## 24. Écoute accélérée pour modérateurs

Étant donné que je suis un modérateur Et qu'un contenu de 30 secondes est à valider

Quand je l'écoute à 2.0x

Alors je termine l'écoute en 15 secondes Et ma productivité est doublée

## 25. Écoute accélérée pour auditeurs

Étant donné que je suis un auditeur Et qu'un podcast de 1 heure est disponible

Quand je configure la vitesse à 1.5x

Alors j'écoute le podcast en 40 minutes Et je gagne 20 minutes

## 26. Sauvegarde préférence vitesse d'écoute

Étant donné que je configure la vitesse à 1.5x

Quand j'écoute plusieurs contenus

Alors tous les contenus sont lus à 1.5x par défaut Et ma préférence est sauvegardée

## 27. Scalabilité horizontale des workers

Étant donné que 100 contenus sont uploadés simultanément

Quand les jobs d'encodage sont distribués

Alors plusieurs workers Go traitent les jobs en parallèle Et Kubernetes scale automatiquement les pods Et tous les contenus sont encodés sans délai excessif

## 28. Statut d'encodage visible

Étant donné que mon contenu est en cours d'encodage

Quand je consulte mes contenus

Alors je vois le statut:

    | état | affichage |
    |---|---|
    | En attente | ⏳ File d'attente |
    | En cours | ⚙️ Encodage en cours (45%) |
    | Terminé | ✅ Prêt à publier |
    | Échec | ❌ Erreur - Réessayer |

## 29. Réessayer après échec d'encodage

Étant donné que l'encodage de mon contenu a échoué

Quand je clique sur "Réessayer"

Alors un nouveau job d'encodage est lancé Et je peux tenter à nouveau

# Validation des 3 premiers contenus

> En tant que nouveau créateur Je veux que mes 3 premiers contenus soient validés Afin de devenir créateur vérifié

30 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et que je suis un nouveau créateur

## 1. Premier contenu passe en file de validation

Quand je publie mon premier contenu

Alors le contenu passe en file d'attente modération Et je vois le message "Votre contenu est en cours de validation (24-48h)" Et le contenu n'est pas encore visible publiquement

## 2. Deuxième contenu passe également en validation

Étant donné que mon premier contenu a été validé

Quand je publie mon deuxième contenu

Alors le contenu passe en file d'attente modération Et le délai estimé est 24-48h

## 3. Troisième contenu - dernière validation

Étant donné que mes 2 premiers contenus ont été validés

Quand je publie mon troisième contenu

Alors le contenu passe en file d'attente modération Et je vois "Dernière validation avant statut vérifié ✓"

## 4. Modérateur écoute 30 secondes du contenu

Étant donné qu'un contenu est en file de validation

Quand le modérateur junior l'examine

Alors il écoute les 30 premières secondes Et il vérifie les métadonnées

## 5. Validation - Qualité audio acceptable

Étant donné qu'un contenu a une qualité audio claire

Quand le modérateur l'écoute

Alors il vérifie que l'audio est compréhensible Et qu'il n'y a pas de grésillement excessif

## 6. Rejet - Qualité audio insuffisante

Étant donné qu'un contenu a un audio très grésillant

Quand le modérateur l'écoute

Alors le contenu est rejeté Et la raison est "Qualité audio insuffisante"

## 7. Validation - Respect des règles

Étant donné qu'un contenu respecte les règles

Quand le modérateur l'examine

Alors il vérifie qu'il n'y a pas de contenu prohibé:

    | type prohibé |
    |---|
    | Haine |
    | Violence |
    | Spam |
    | Illégalité |

## 8. Rejet - Contenu haineux détecté

Étant donné qu'un contenu contient des propos haineux

Quand le modérateur l'écoute

Alors le contenu est rejeté immédiatement Et la raison est "Contenu haineux (violation des règles)" Et le créateur peut recevoir un strike

## 9. Validation - Classification âge cohérente

Étant donné qu'un contenu familial est classé "Tout public"

Quand le modérateur l'écoute

Alors il vérifie que la classification correspond au contenu Et le contenu est accepté

## 10. Rejet - Classification incorrecte

Étant donné qu'un contenu adulte est classé "Tout public"

Quand le modérateur détecte l'incohérence

Alors le contenu est rejeté Et la raison est "Classification d'âge incorrecte"

## 11. Validation - Tags pertinents

Étant donné qu'un contenu sur l'automobile est tagué "Automobile", "Technologie"

Quand le modérateur vérifie les tags

Alors il confirme que les tags correspondent au contenu Et le contenu est accepté

## 12. Rejet - Tags non pertinents

Étant donné qu'un contenu musical est tagué "Automobile", "Sport"

Quand le modérateur détecte l'incohérence

Alors le contenu est rejeté Et la raison est "Tags non pertinents avec le contenu"

## 13. Validation - Zone diffusion cohérente

Étant donné qu'un audio-guide de la Tour Eiffel est en "Point GPS" Paris

Quand le modérateur vérifie la cohérence

Alors la zone est appropriée Et le contenu est accepté

## 14. Rejet - Zone incohérente

Étant donné qu'un audio-guide de la Tour Eiffel est en zone "National"

Quand le modérateur détecte l'incohérence

Alors le contenu est rejeté Et la raison est "Zone de diffusion incohérente (devrait être Point GPS)"

## 15. Délai de validation 24-48h jours ouvrés

Étant donné que je publie un contenu un lundi

Quand le contenu entre en file de validation

Alors le délai estimé est 24-48h (mercredi maximum)

## 16. Délai étendu le weekend

Étant donné que je publie un contenu un vendredi soir

Quand le contenu entre en file de validation

Alors le délai peut atteindre 72h (lundi) Et je vois "Validation en cours, délai 24-72h (weekend)"

## 17. Priorité FIFO (First In First Out)

Étant donné que 10 contenus sont en file de validation

Quand les modérateurs traitent la file

Alors les contenus sont traités dans l'ordre d'arrivée Et pas de traitement prioritaire

## 18. Notification acceptation

Étant donné que mon contenu est validé et accepté

Alors je reçois un email "✅ Votre contenu '[Titre]' est en ligne !" Et je reçois une notification push Et je vois un lien direct vers le contenu

## 19. Compteur de validation

Étant donné que mon premier contenu est accepté

Alors je vois "1/3 contenus validés pour devenir créateur vérifié"

Quand mon deuxième contenu est accepté

Alors je vois "2/3 contenus validés pour devenir créateur vérifié"

## 20. Notification refus avec raison détaillée

Étant donné que mon contenu est rejeté

Alors je reçois un email "❌ Contenu '[Titre]' refusé" Et je reçois une notification push Et je vois la raison exacte: "Qualité audio insuffisante" Et je vois un lien vers les règles de publication

## 21. Possibilité de correction et resoumission

Étant donné que mon contenu a été rejeté pour "Tags non pertinents"

Quand je corrige les tags Et que je resoumets le contenu

Alors le contenu repasse en file de validation Et une nouvelle validation est effectuée

## 22. Après 3 validations - Statut vérifié obtenu

Étant donné que mes 3 premiers contenus ont été validés

Alors j'obtiens le statut "Créateur Vérifié" Et je reçois une notification "🎉 Vous êtes maintenant créateur vérifié !" Et un badge ✓ apparaît sur mon profil

## 23. Badge vérifié visible publiquement

Étant donné que j'ai le statut vérifié

Quand un utilisateur consulte mon profil

Alors il voit le badge ✓ à côté de mon pseudo Et une mention "Créateur vérifié"

## 24. Contenus futurs publiés immédiatement

Étant donné que je suis créateur vérifié

Quand je publie un 4ème contenu

Alors le contenu est publié immédiatement Et il n'y a pas de validation préalable Et je vois "✅ Contenu publié"

## 25. Modération a posteriori uniquement

Étant donné que je suis créateur vérifié Et que je publie un contenu

Quand le contenu est en ligne

Alors il peut être signalé par les utilisateurs Et sera modéré uniquement si signalé

## 26. Interface modérateur - Queue de contenus

Étant donné que je suis un modérateur junior

Quand j'accède à l'interface de modération

Alors je vois la file des contenus à valider Et je vois le nombre total en attente Et les contenus sont triés par ordre FIFO

## 27. Interface modérateur - Écoute accélérée

Étant donné que je suis un modérateur

Quand j'écoute un contenu de 30 secondes

Alors je peux choisir la vitesse 1.5x ou 2.0x Et je termine l'écoute en 15 secondes à 2x Et ma productivité est doublée

## 28. Interface modérateur - Raccourcis clavier

Étant donné que je modère un contenu

Quand j'utilise les raccourcis clavier

Alors je peux:

    | touche | action |
    |---|---|
    | A | Accepter |
    | R | Rejeter |
    | Espace | Play/Pause |

Et la modération est accélérée

## 29. Historique créateur visible

Étant donné qu'un créateur soumet son 2ème contenu

Quand le modérateur examine le contenu

Alors il voit l'historique:

    | contenu | statut |
    |---|---|
    | Contenu 1 | Validé |
    | Contenu 2 | En cours |

Et il peut juger la cohérence du créateur

## 30. Temps de modération estimé 1.5 min/créateur

Étant donné qu'un créateur soumet 3 contenus

Quand les modérateurs traitent ces contenus

Alors le temps total est environ:

    | action | temps |
    |---|---|
    | Écoute 30s × 3 | 90s |
    | Vérification metadata | 15s |
    | Décision | 5s |
    | Total | 110s |

# Élargissement automatique de zone quand aucun contenu n'est disponible

9 scénarios

> Contexte commun à tous les scénarios

> Étant donné que je suis un utilisateur connecté Et que la géolocalisation est activée Et que je suis en mode écoute

## 1. Aucun contenu dans rayon 50km - élargissement à 100km

Étant donné que je suis situé à la position GPS 48.8566, 2.3522 Et qu'aucun contenu n'existe dans un rayon de 50 km autour de ma position Mais qu'au moins 1 contenu existe dans un rayon de 100 km

Quand le système recherche du contenu à me proposer

Alors le système élargit automatiquement la zone de recherche à 100 km Et je reçois un message "Aucun contenu dans votre zone immédiate. Voici du contenu à proximité (100 km)" Et un contenu dans le rayon de 100 km m'est proposé

## 2. Aucun contenu dans rayon 100km - élargissement au département

Étant donné que je suis situé dans le département "75" (Paris) Et qu'aucun contenu n'existe dans un rayon de 100 km autour de ma position Mais qu'au moins 1 contenu existe avec la zone "département" pour "75"

Quand le système recherche du contenu à me proposer

Alors le système élargit automatiquement la zone de recherche au département Et je reçois un message "Aucun contenu local disponible. Voici du contenu dans votre département" Et un contenu départemental m'est proposé

## 3. Aucun contenu départemental - élargissement à la région

Étant donné que je suis situé dans la région "Île-de-France" Et qu'aucun contenu n'existe dans un rayon de 100 km autour de ma position Et qu'aucun contenu départemental n'existe pour mon département Mais qu'au moins 1 contenu existe avec la zone "région" pour "Île-de-France"

Quand le système recherche du contenu à me proposer

Alors le système élargit automatiquement la zone de recherche à la région Et je reçois un message "Aucun contenu local disponible. Voici du contenu dans votre région" Et un contenu régional m'est proposé

## 4. Aucun contenu régional - basculement sur contenu national

Étant donné que je suis situé en France Et qu'aucun contenu n'existe dans un rayon de 100 km autour de ma position Et qu'aucun contenu départemental n'existe pour mon département Et qu'aucun contenu régional n'existe pour ma région

Quand le système recherche du contenu à me proposer

Alors le système bascule automatiquement sur du contenu national Et je reçois un message "Aucun contenu local disponible. Voici du contenu national qui pourrait vous intéresser" Et un contenu national m'est proposé Et je ne reste jamais sans contenu disponible

## 5. Élargissement progressif avec plusieurs étapes

Étant donné que je suis situé dans une zone rurale isolée Et qu'aucun contenu n'existe dans un rayon de 50 km Et qu'aucun contenu n'existe dans un rayon de 100 km Et qu'aucun contenu départemental n'existe Et qu'aucun contenu régional n'existe

Quand le système recherche du contenu à me proposer

Alors le système essaie d'abord 50 km Et tout ce processus se fait de manière transparente et automatique Et je reçois le message correspondant au dernier niveau trouvé

## 6. Message personnalisé selon la distance trouvée

Étant donné que je suis situé à la position GPS 43.6047, 1.4442 Et que contenu(s) est/sont trouvé(s)

Quand le système me propose du contenu

Alors je reçois le message ""

## 7. Le contenu national sert de filet de sécurité

Étant donné que le système a épuisé toutes les zones géographiques locales

Quand le système bascule sur du contenu national

Alors je dois toujours avoir au moins 1 contenu disponible Et ce contenu peut être:

    | type_contenu |
    |---|
    | Actualités Le Monde |
    | Podcasts génériques |
    | Contenu éducatif national |
    | Contenu culturel national |

## 8. Pas d'écran d'erreur "Aucun contenu"

Étant donné que je lance l'application Et qu'aucun contenu local n'est disponible dans ma zone

Quand le système recherche du contenu

Alors je ne dois jamais voir un message d'erreur "Aucun contenu disponible" Et je ne dois jamais voir un écran vide Et un contenu doit toujours m'être proposé, même si c'est du contenu national

## 9. Élargissement avec prise en compte des centres d'intérêt

Étant donné que je suis situé dans une zone rurale Et qu'aucun contenu n'existe dans un rayon de 50 km Et que mes centres d'intérêt incluent "Automobile" à 80% et "Voyage" à 70% Et qu'un contenu national existe avec le tag "Automobile" Et qu'un contenu national existe avec le tag "Politique"

Quand le système bascule sur du contenu national

Alors le contenu national proposé prend en compte mes centres d'intérêt Et le contenu "Automobile" a un score supérieur au contenu "Politique"

# Gestion d'un contenu supprimé pendant l'écoute

11 scénarios

> Contexte commun à tous les scénarios

> Étant donné que je suis un utilisateur connecté Et que je suis en mode écoute Et qu'un contenu "C123" est en cours de lecture

## 1. Contenu supprimé pendant lecture - fin de lecture sans interruption

Étant donné que j'écoute le contenu "C123" depuis 30 secondes Et que la durée totale du contenu est de 120 secondes

Quand le contenu est supprimé par la modération côté backend

Alors la lecture du contenu continue sans interruption Et je peux écouter le contenu jusqu'à la fin Et aucune interruption brutale ne se produit

## 2. Passage automatique après fin du contenu supprimé

Étant donné que le contenu "C123" a été supprimé pendant ma lecture Et que j'ai écouté le contenu jusqu'à la fin

Quand le contenu se termine

Alors le système attend 2 secondes Et passe automatiquement au contenu suivant Et je reçois une notification toast discrète "Contenu précédent retiré (violation règles)"

## 3. Bouton Précédent désactivé après suppression

Étant donné que le contenu "C123" a été supprimé pendant ma lecture Et que je suis passé au contenu suivant "C456"

Quand j'essaie d'appuyer sur le bouton "Précédent"

Alors le bouton "Précédent" ne me ramène pas au contenu supprimé Et je reçois un message "Ce contenu n'est plus disponible" Et la lecture du contenu actuel "C456" continue

## 4. Tentative de retour manuel au contenu supprimé

Étant donné que je suis sur le contenu "C456" Et que le contenu précédent "C123" a été supprimé

Quand j'appuie sur le bouton "Précédent" pour revenir au contenu supprimé

Alors je reçois un message "Ce contenu n'est plus disponible" Et la lecture reste sur le contenu actuel "C456" Et aucune action n'est effectuée

## 5. Notification discrète pendant la conduite

Étant donné que je conduis à une vitesse de 60 km/h Et que le contenu "C123" est supprimé pendant ma lecture

Quand le contenu se termine

Alors la notification "Contenu précédent retiré (violation règles)" s'affiche en toast discret Et la notification disparaît automatiquement après 5 secondes Et aucune popup modale n'interrompt ma conduite Et le contenu suivant démarre automatiquement après 2 secondes

## 6. Message informatif mais non alarmiste

Étant donné que le contenu "C123" a été supprimé Et que je passe au contenu suivant

Quand la notification s'affiche

Alors le message doit être informatif: "Contenu précédent retiré (violation règles)" Et le ton ne doit pas être alarmiste Et le message doit être bref et compréhensible Et aucun détail technique n'est affiché pendant la conduite

## 7. Contenu supprimé retiré de l'historique

Étant donné que le contenu "C123" a été supprimé

Quand je consulte mon historique d'écoute

Alors le contenu "C123" n'apparaît plus dans mon historique Et je ne peux pas relancer la lecture de ce contenu Et l'historique affiche "[Contenu retiré]" à la place du titre

## 8. Contenu supprimé non accessible via lien direct

Étant donné que le contenu "C123" a été supprimé Et que j'ai un lien de partage "roadwave.fr/share/c/C123"

Quand je clique sur le lien de partage

Alors je reçois un message "Ce contenu a été retiré pour violation des règles de la communauté" Et je suis redirigé vers l'accueil de l'application Et aucune lecture n'est possible

## 9. Plusieurs contenus supprimés dans l'historique récent

Étant donné que j'ai écouté les contenus suivants:

    | id | statut |
    |---|---|
    | C123 | supprimé |
    | C456 | actif |
    | C789 | supprimé |

Et que je suis actuellement sur le contenu "C456"

Quand j'appuie plusieurs fois sur "Précédent"

Alors je ne peux pas revenir aux contenus "C123" ou "C789" Et le système saute automatiquement les contenus supprimés Et je reviens au dernier contenu actif disponible avant "C456"

## 10. Consultation détaillée du contenu supprimé à l'arrêt

Étant donné que je suis à l'arrêt Et que le contenu "C123" a été supprimé pendant ma session

Quand j'ouvre les détails de la notification de suppression

Alors je peux voir les informations suivantes:

    | information |
    |---|
    | Titre du contenu |
    | Créateur |
    | Raison de suppression |
    | Date de suppression |

Et je peux signaler une erreur de modération si je pense qu'elle est injustifiée

## 11. Pas d'impact sur les jauges d'intérêt lors de la suppression

Étant donné que j'ai écouté le contenu "C123" pendant 80 secondes (66%) Et que mes jauges d'intérêt ont été mises à jour pendant l'écoute

Quand le contenu est supprimé après mon écoute

Alors les modifications de mes jauges d'intérêt sont conservées Et l'écoute déjà effectuée reste comptabilisée Et seules les futures écoutes de ce contenu sont bloquées

# Mode dégradé sans géolocalisation

19 scénarios

> Contexte commun à tous les scénarios

> Étant donné que je suis un utilisateur connecté Et que j'ai refusé ou désactivé l'accès à la géolocalisation

## 1. Types de contenu disponibles sans géolocalisation

Étant donné que la géolocalisation est désactivée

Quand j'ouvre l'application

Alors les types de contenu suivants sont disponibles:

    | type_contenu | disponible |
    |---|---|
    | Contenu national | oui |
    | Contenu téléchargé (offline) | oui |
    | Contenus "Neutre" géographiquement | oui |
    | Contenu géolocalisé Ancré | non |
    | Contenu géolocalisé Contextuel | non |
    | Audio-guides | non |
    | Notifications push géo-déclenchées | non |

## 2. Popup d'information au premier lancement sans GPS

Étant donné que c'est mon premier lancement de l'application Et que j'ai refusé l'accès à la géolocalisation

Quand l'application détecte que le GPS est désactivé

Alors une popup s'affiche avec le message: Et la popup contient les boutons suivants:

    | bouton | action |
    |---|---|
    | Activer | Redirection vers paramètres OS |
    | Continuer sans | Ferme popup et lance en mode dégradé |

Et une checkbox "Ne plus me demander" est disponible

## 3. Popup non affichée si case "Ne plus me demander" cochée

Étant donné que j'ai déjà vu la popup de géolocalisation Et que j'ai coché "Ne plus me demander"

Quand je lance l'application avec le GPS désactivé

Alors la popup de géolocalisation ne s'affiche pas Et l'application démarre directement en mode dégradé Et le banner permanent de rappel s'affiche

## 4. Redirection vers paramètres OS lors du clic sur "Activer"

Étant donné que la popup de géolocalisation est affichée

Quand je clique sur "Activer"

Alors je suis redirigé vers les paramètres de géolocalisation de mon OS Et sur iOS, j'arrive dans "Réglages > RoadWave > Localisation" Et sur Android, j'arrive dans "Paramètres > Applications > RoadWave > Autorisations > Position"

## 5. Banner de rappel permanent sans GPS

Étant donné que j'ai cliqué sur "Continuer sans" géolocalisation

Quand l'application s'affiche

Alors un bandeau s'affiche en haut de l'écran Et le bandeau contient le texte: "Mode limité : géolocalisation désactivée. [Activer]" Et le bandeau a un fond de couleur avertissement (jaune/orange) Et le bandeau n'est pas intrusif mais reste visible Et le bandeau reste affiché sur toutes les pages de l'application

## 6. Clic sur le bouton "Activer" du banner

Étant donné que le banner "Mode limité" est affiché

Quand je clique sur le lien "[Activer]" dans le banner

Alors je suis redirigé vers les paramètres de géolocalisation de mon OS

## 7. Disparition du banner après activation GPS

Étant donné que le banner "Mode limité" est affiché Et que je reviens dans l'application après avoir activé le GPS dans les paramètres

Quand l'application détecte que la géolocalisation est maintenant active

Alors le banner disparaît automatiquement Et l'application bascule en mode normal avec contenu géolocalisé Et un toast de confirmation s'affiche: "Géolocalisation activée"

## 8. Lecture de contenu national sans GPS

Étant donné que la géolocalisation est désactivée Et que du contenu national existe (actualités Le Monde, podcasts génériques)

Quand je lance la lecture

Alors je peux écouter le contenu national sans restriction Et l'algorithme de recommandation se base uniquement sur:

    | critère |
    |---|
    | Mes centres d'intérêt |
    | Mon historique d'écoute |
    | Popularité générale |

Et la proximité géographique n'est pas prise en compte

## 9. Lecture de contenu téléchargé sans GPS

Étant donné que la géolocalisation est désactivée Et que j'ai téléchargé 30 contenus quand j'avais le GPS activé

Quand j'accède à mes contenus téléchargés

Alors je peux lire tous mes contenus téléchargés normalement Et les contenus géolocalisés téléchargés restent accessibles Et le filtre géographique n'est pas appliqué pour les contenus offline

## 10. Contenu "Neutre" géographiquement disponible

Étant donné que la géolocalisation est désactivée Et qu'un créateur a publié du contenu avec la classification géographique "Neutre"

Quand je recherche du contenu

Alors les contenus "Neutre" sont inclus dans les résultats Et ils sont mélangés avec le contenu national Et l'algorithme les priorise selon mes centres d'intérêt

## 11. Audio-guides inaccessibles sans GPS

Étant donné que la géolocalisation est désactivée

Quand je recherche un audio-guide spécifique

Alors les audio-guides apparaissent dans les résultats de recherche Mais un badge "GPS requis" est affiché sur chaque audio-guide Et quand je clique sur un audio-guide, un message s'affiche: Et je peux choisir "Activer" ou "Annuler"

## 12. Notifications push géo-déclenchées désactivées

Étant donné que la géolocalisation est désactivée Et que je suis abonné à un créateur qui diffuse du contenu géolocalisé

Quand le créateur publie un nouveau contenu géolocalisé

Alors je ne reçois pas de notification push géo-déclenchée Mais je reçois une notification push standard (non géo-déclenchée) si le créateur publie du contenu national Et la notification précise: "Nouveau contenu national de [Créateur]"

## 13. Contenu géolocalisé non proposé dans le feed

Étant donné que la géolocalisation est désactivée

Quand le système génère mon feed de contenu

Alors aucun contenu "Ancré" ou "Contextuel" n'est inclus Et seuls les contenus "Neutre" et "National" sont proposés Et mon feed contient au minimum 20 contenus disponibles

## 14. Application fonctionnelle sans GPS (pas de blocage)

Étant donné que la géolocalisation est désactivée

Quand j'utilise l'application

Alors je ne suis jamais bloqué par un écran "GPS requis" Et toutes les fonctionnalités non-géolocalisées restent accessibles:

    | fonctionnalité |
    |---|
    | Écoute contenu national |
    | Gestion profil |
    | Abonnements créateurs |
    | Recherche textuelle |
    | Historique d'écoute |
    | Paramètres |
    | Mode offline |

Et je peux créer et publier du contenu national

## 15. Respect du choix utilisateur de ne pas activer GPS

Étant donné que j'ai coché "Ne plus me demander" pour la géolocalisation

Quand j'utilise l'application pendant plusieurs semaines

Alors la popup de demande GPS ne s'affiche plus jamais automatiquement Et seul le banner permanent reste affiché Et l'application ne force jamais l'activation du GPS

## 16. Bascule automatique en mode normal après activation GPS

Étant donné que j'utilise l'application en mode dégradé depuis 1 semaine Et que je décide d'activer la géolocalisation

Quand l'application détecte que le GPS est maintenant actif

Alors le mode dégradé est désactivé automatiquement Et le banner "Mode limité" disparaît Et le contenu géolocalisé devient disponible immédiatement Et mon feed se rafraîchit avec du contenu local pertinent Et un toast de confirmation s'affiche: "Géolocalisation activée - Contenu local disponible"

## 17. Demande de permission GPS lors de l'utilisation d'une fonctionnalité géo

Étant donné que la géolocalisation est désactivée

Quand j'essaie d'accéder à une fonctionnalité nécessitant le GPS (ex: audio-guide)

Alors une popup contextuelle s'affiche: Et je peux accepter ou refuser Et si j'accepte, je suis redirigé vers les paramètres OS Et si je refuse, je reste en mode dégradé sans message d'erreur répétitif

## 18. Statistiques de contenu local disponible non affiché

Étant donné que la géolocalisation est désactivée

Quand je navigue dans l'application

Alors le banner peut afficher occasionnellement: Et ce message incitatif change tous les 3 jours Et il reste non intrusif (pas de popup, juste le banner)

## 19. Onboarding différent pour utilisateurs sans GPS

Étant donné que c'est ma première utilisation de RoadWave Et que j'ai refusé la géolocalisation

Quand l'onboarding se termine

Alors un écran explicatif s'affiche: Et je peux continuer avec un bouton "Compris"

# Gestion de la perte de réseau et buffering adaptatif

17 scénarios (16 standards, 1 plan)

> Contexte commun à tous les scénarios

> Étant donné que je suis un utilisateur connecté Et que je suis en mode écoute Et qu'un contenu est en cours de lecture

## 1. 📋 Plan: Paramètres de buffer selon le type de réseau

Étant donné que je suis connecté en ""

Quand le système initialise le buffer audio

Alors le buffer minimum est de secondes Et le buffer cible est de secondes Et le buffer maximum est de secondes

📊 Exemples de données:

type_reseau | buffer_min | buffer_cible | buffer_max
--- | --- | --- | ---
WiFi | 5 | 30 | 120
4G | 10 | 45 | 120
5G | 10 | 45 | 120
3G | 30 | 90 | 300

## 2. Connexion instable avec latence élevée - aucun message immédiat

Étant donné que je suis connecté en 4G Et que le buffer contient 45 secondes de contenu

Quand la latence réseau dépasse 500ms

Alors aucun message n'est affiché immédiatement Et la lecture continue normalement sur le buffer Et le système tente de continuer le téléchargement en arrière-plan

## 3. Connexion instable pendant plus de 10 secondes - toast discret

Étant donné que je suis connecté en 4G Et que la latence réseau dépasse 500ms depuis 10 secondes

Quand le système détecte la latence prolongée

Alors un toast discret s'affiche: "Connexion instable" Et le toast disparaît automatiquement après 3 secondes Et la lecture continue normalement

## 4. Perte totale de réseau - lecture sur buffer

Étant donné que je suis connecté en WiFi Et que le buffer contient 30 secondes de contenu

Quand je perds totalement la connexion réseau

Alors la lecture continue sur le buffer disponible Et un toast s'affiche: "Hors ligne, lecture sur buffer (30s restantes)" Et un compte à rebours du temps de buffer restant est visible

## 5. Buffer qui s'épuise pendant la perte réseau

Étant donné que je suis hors ligne Et que le buffer contient 30 secondes de contenu

Quand le contenu continue de jouer

Alors le compte à rebours diminue en temps réel Et le toast affiche "Hors ligne, lecture sur buffer (15s restantes)" après 15 secondes Et le toast affiche "Hors ligne, lecture sur buffer (5s restantes)" après 25 secondes

## 6. Pause automatique après épuisement du buffer

Étant donné que je suis hors ligne depuis 30 secondes Et que le buffer est complètement épuisé

Quand il n'y a plus de contenu audio à lire

Alors la lecture se met en pause automatiquement Et un overlay s'affiche: "Connexion perdue. Reconnexion en cours..." Et le système tente de se reconnecter automatiquement

## 7. Tentatives de reconnexion automatique

Étant donné que la lecture est en pause suite à l'épuisement du buffer

Quand le système tente de se reconnecter

Alors une tentative de reconnexion est effectuée toutes les 5 secondes Et un maximum de 6 tentatives sont effectuées (30 secondes au total) Et l'overlay affiche "Tentative de reconnexion... (X/6)"

## 8. Proposition du mode offline après 30 secondes d'échec

Étant donné que 6 tentatives de reconnexion ont échoué Et que cela fait 30 secondes que je suis déconnecté

Quand la 6ème tentative échoue

Alors une popup s'affiche: "Voulez-vous continuer avec vos contenus téléchargés ?" Et la popup contient deux boutons:

    | bouton | action |
    |---|---|
    | Réessayer | Nouvelle série de 6 tentatives |
    | Mode offline | Bascule sur contenus téléchargés |

## 9. Basculement réussi vers le mode offline

Étant donné que la popup de mode offline est affichée Et que j'ai téléchargé 20 contenus dans ma zone géographique

Quand je clique sur "Mode offline"

Alors le système bascule sur les contenus téléchargés Et un nouveau contenu téléchargé démarre automatiquement Et un bandeau permanent indique "Mode hors ligne - Contenus téléchargés"

## 10. Aucun contenu téléchargé disponible

Étant donné que la popup de mode offline est affichée Et que je n'ai aucun contenu téléchargé

Quand je clique sur "Mode offline"

Alors un message s'affiche: "Aucun contenu téléchargé disponible" Et je suis invité à me connecter en WiFi pour télécharger du contenu Et le bouton "Réessayer" reste la seule option

## 11. Reprise automatique après reconnexion

Étant donné que la lecture est en pause depuis 15 secondes Et que j'étais à 02:35 du contenu en cours

Quand la connexion réseau est rétablie

Alors la lecture reprend automatiquement au point d'arrêt exact (02:35) Et un toast s'affiche: "Connexion rétablie" Et le toast disparaît après 3 secondes Et le buffer se remplit progressivement selon le type de réseau

## 12. Reconnexion avec changement de type de réseau

Étant donné que j'étais connecté en WiFi Et que j'ai perdu la connexion

Quand je me reconnecte en 4G

Alors le système ajuste automatiquement les paramètres de buffer Et le buffer minimum passe de 5s à 10s Et le buffer cible passe de 30s à 45s Et la lecture reprend normalement

## 13. Passage dans un tunnel avec perte de signal

Étant donné que je conduis à 90 km/h sur autoroute Et que je suis connecté en 4G avec un buffer de 45 secondes

Quand j'entre dans un tunnel et perds le signal

Alors la lecture continue sur le buffer pendant 45 secondes maximum Et aucune notification n'est affichée pendant les 10 premières secondes Et un toast discret s'affiche après 10 secondes: "Connexion instable"

## 14. Sortie du tunnel avant épuisement du buffer

Étant donné que je suis dans un tunnel depuis 30 secondes Et qu'il reste 15 secondes de buffer

Quand je sors du tunnel et récupère le signal 4G

Alors la lecture continue sans interruption Et le buffer se remplit à nouveau Et un toast s'affiche: "Connexion rétablie"

## 15. Changement de cellule 4G pendant la lecture

Étant donné que je conduis et change de cellule mobile toutes les 5-10 minutes Et que le buffer contient 45 secondes de contenu

Quand un handoff de cellule se produit

Alors la lecture continue sans interruption grâce au buffer Et la connexion à la nouvelle cellule se fait de manière transparente Et aucune notification n'est affichée si le handoff réussit en moins de 5 secondes

## 16. Téléchargement préventif en WiFi avant trajet

Étant donné que je suis connecté en WiFi Et que j'ai activé le téléchargement automatique

Quand le système détecte que je suis à l'arrêt en WiFi

Alors le système me propose de télécharger du contenu pour mon trajet Et je peux sélectionner une zone géographique à télécharger Et le téléchargement se fait en arrière-plan

## 17. Tracking des événements de perte réseau pour amélioration

Étant donné que je perds la connexion réseau

Quand l'événement de perte est détecté

Alors le système enregistre les métriques suivantes:

    | métrique |
    |---|
    | Type de réseau avant perte |
    | Durée de la coupure |
    | Buffer disponible |
    | Position GPS approximative |
    | Heure de la journée |

Et ces métriques sont anonymisées et envoyées en batch lors de la prochaine connexion WiFi Et les données servent à améliorer les paramètres de buffer

# Tests BDD - Documentation des fonctionnalités

Cette documentation est générée automatiquement à partir des fichiers Gherkin (`.feature`).

## Vue d'ensemble

Métrique | Valeur
--- | ---
Fonctionnalités | 83
Scénarios | 2112
Domaines métier | 18

## 🔔 Abonnements

Fonctionnalité | Scénarios
--- | ---
Audio-guides multi-séquences pour piétons | 29
Impact des abonnements sur l'algorithme | 16
Limites d'abonnements et désabonnement | 27
Notifications contextuelles selon le mode de déplacement | 28

4 fonctionnalités • 100 scénarios

## 🎧 Audio Guides

Fonctionnalité | Scénarios
--- | ---
Audio-guide mode piéton (navigation manuelle) | 29
Audio-guide mode voiture (GPS automatique) | 45
Audio-guides Premium et monétisation | 31
Audio-guides modes vélo et transport | 27
Création d'audio-guide multi-séquences | 35
Intégration audio-guides avec autres fonctionnalités | 39
Sauvegarde et reprise de progression audio-guide | 32

7 fonctionnalités • 238 scénarios

## 🔐 Authentication

Fonctionnalité | Scénarios
--- | ---
Authentification à deux facteurs (2FA) | 16
Classification des contenus par âge | 13
Connexion utilisateur | 11
Gestion des sessions et tokens | 13
Inscription utilisateur | 15
Récupération de compte | 14
Vérification d'email | 10

7 fonctionnalités • 92 scénarios

## 🎨 Content Creation

Fonctionnalité | Scénarios
--- | ---
Modification et suppression de contenu | 30
Métadonnées et publication de contenu | 34
Upload et encodage de contenu audio | 29
Validation des 3 premiers contenus | 30

4 fonctionnalités • 123 scénarios

## ⚠️ Error Handling

Fonctionnalité | Scénarios
--- | ---
Gestion d'un contenu supprimé pendant l'écoute | 11
Gestion de la perte de réseau et buffering adaptatif | 17
Mode dégradé sans géolocalisation | 19
Élargissement automatique de zone quand aucun contenu n'est disponible | 9

4 fonctionnalités • 56 scénarios

## 📊 Interest Gauges

Fonctionnalité | Scénarios
--- | ---
Jauge initiale et cold start | 15
Pas de dégradation temporelle des jauges | 16
Évolution des jauges d'intérêt | 21

3 fonctionnalités • 52 scénarios

## 📴 Mode Offline

Fonctionnalité | Scénarios
--- | ---
Synchronisation actions offline | 45
Téléchargement de contenus offline | 49
Validité et renouvellement contenus offline | 38

3 fonctionnalités • 132 scénarios

## 🛡️ Moderation

Fonctionnalité | Scénarios
--- | ---
Modération préventive | 22
Sanctions et notifications de modération | 27
Signalement de contenu inapproprié | 23
Traitement des signalements par l'IA et les modérateurs | 25

4 fonctionnalités • 97 scénarios

## 💰 Monetisation

Fonctionnalité | Scénarios
--- | ---
Conditions d'activation de la monétisation | 28
Contenus Premium exclusifs | 34
Désactivation et suspension monétisation | 35
KYC et inscription à la monétisation | 37
Obligations fiscales | 30
Paiement des créateurs | 35
Sources de revenus créateurs | 34

7 fonctionnalités • 233 scénarios

## 🧭 Navigation

Fonctionnalité | Scénarios
--- | ---
Actions complémentaires à l'arrêt | 23
Commande "Précédent" | 19
Commandes au volant et interactions simplifiées | 21
Commandes vocales CarPlay et Android Auto | 25
File d'attente et commande "Suivant" | 20
Lecture en boucle et enchaînement automatique | 27

6 fonctionnalités • 135 scénarios

## 🔗 Partage

Fonctionnalité | Scénarios
--- | ---
Partage de contenu | 22

1 fonctionnalités • 22 scénarios

## ⭐ Premium

Fonctionnalité | Scénarios
--- | ---
Avantages Premium | 37
Gestion abonnement Premium | 41
Multi-devices et détection simultanée | 30
Offre et tarification Premium | 31

4 fonctionnalités • 139 scénarios

## 👤 Profil

Fonctionnalité | Scénarios
--- | ---
Profil créateur | 31

1 fonctionnalités • 31 scénarios

## 📢 Publicites

Fonctionnalité | Scénarios
--- | ---
Caractéristiques et facturation des publicités | 32
Création de campagnes publicitaires | 30
Gestion du budget et alertes publicitaires | 30
Insertion et fréquence des publicités | 31
Métriques d'engagement et dashboard publicitaire | 27
Validation et modération des publicités | 29

6 fonctionnalités • 179 scénarios

## 📻 Radio Live

Fonctionnalité | Scénarios
--- | ---
Architecture technique radio live | 24
Arrêt du live | 19
Comportement auditeur pendant un live | 27
Démarrage d'un live | 20

4 fonctionnalités • 90 scénarios

## 🔍 Recherche

Fonctionnalité | Scénarios
--- | ---
Recherche de contenu | 55

1 fonctionnalités • 55 scénarios

## 🎯 Recommendation

Fonctionnalité | Scénarios
--- | ---
Classification de géo-pertinence des contenus | 10
Contenus géolocalisés en mode voiture | 36
Formule de scoring et recommandation | 21
Gestion de l'historique et reproposition | 19
Gestion du contenu politique (MVP simplifié) | 13
Mode Kids pour utilisateurs 13-15 ans | 15
Médias traditionnels sur RoadWave | 21
Paramétrabilité admin et A/B testing | 20
Paramétrabilité utilisateur et profils | 25

9 fonctionnalités • 180 scénarios

## 🔒 Rgpd Compliance

Fonctionnalité | Scénarios
--- | ---
Anonymisation des données GPS après 24h | 18
Conformité administrative RGPD (Registre, Breach, DPO) | 22
Cookies et analytics avec Matomo self-hosted | 20
Durée de conservation des données et purge automatique | 19
Gestion du consentement RGPD | 16
Mode dégradé avec GeoIP (sans GPS précis) | 20
Portabilité des données (Article 20 RGPD) | 22
Suppression du compte utilisateur (Article 17 RGPD - Droit à l'effacement) | 21

8 fonctionnalités • 158 scénarios

# Pas de dégradation temporelle des jauges

> En tant que système de recommandation Je veux que les jauges n'évoluent que par les actions utilisateur Afin d'avoir un comportement prévisible et fiable

16 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et qu'un utilisateur est connecté

## 1. Aucune dégradation automatique avec le temps

Étant donné que ma jauge "Économie" est à 80% Et que je n'écoute aucun contenu pendant 30 jours

Quand je me reconnecte après 30 jours

Alors ma jauge "Économie" est toujours à 80% Et aucune dégradation temporelle n'a été appliquée

## 2. Jauges conservées après 6 mois d'inactivité

Étant donné que mes jauges sont:

    | catégorie | niveau |
    |---|---|
    | Automobile | 75% |
    | Voyage | 60% |
    | Musique | 45% |

Et que je pars en vacances pendant 6 mois sans utiliser l'app

Quand je me reconnecte après 6 mois

Alors mes jauges sont exactement les mêmes:

    | catégorie | niveau |
    |---|---|
    | Automobile | 75% |
    | Voyage | 60% |
    | Musique | 45% |

## 3. Évolution naturelle par les actions

Étant donné que j'aimais "Économie" il y a 1 an (jauge 80%) Et que depuis, je skip tous les contenus "Économie" Et que j'ai skippé 50 contenus "Économie" en 1 an

Alors ma jauge "Économie" descend naturellement via les skips Et atteint environ 55% (80% - 50 × 0.5% = 55%) Et la dégradation vient des actions, pas du temps

## 4. Pas de cron job de dégradation

Étant donné que le système vérifie les jauges quotidiennement

Quand un utilisateur n'a pas d'activité depuis 90 jours

Alors aucun job de dégradation n'est exécuté Et les jauges restent inchangées Et aucune ressource CPU n'est consommée pour la dégradation

## 5. Comportement prévisible après absence

Étant donné que ma jauge "Sport" était à 70% Et que je n'utilise pas l'app pendant 1 an

Quand je reviens et demande des recommandations

Alors mes recommandations reflètent toujours mes goûts d'avant Et je reçois du contenu "Sport" prioritaire Et le comportement est cohérent et prévisible

## 6. Réinitialiser manuellement mes centres d'intérêt

Étant donné que je veux repartir de zéro

Quand je vais dans les paramètres Et que je clique sur "Réinitialiser mes centres d'intérêt" Et que je confirme l'action

Alors toutes mes jauges reviennent à 50% Et je vois le message "Vos centres d'intérêt ont été réinitialisés"

## 7. Confirmation avant réinitialisation

Étant donné que je suis dans les paramètres

Quand je clique sur "Réinitialiser mes centres d'intérêt"

Alors je vois un message de confirmation:

    | titre | Êtes-vous sûr ? |
    |---|---|
    | message | Cette action remettra toutes vos jauges à 50% |
    | actions | Confirmer / Annuler |

## 8. Annuler la réinitialisation

Étant donné que j'ai cliqué sur "Réinitialiser mes centres d'intérêt" Et que la confirmation est affichée

Quand je clique sur "Annuler"

Alors mes jauges ne sont pas modifiées Et je reviens aux paramètres

## 9. Raison de réinitialisation - changement de vie

Étant donné que j'utilisais RoadWave pour mes trajets professionnels Et que mes jauges reflétaient "Économie" (85%) et "Technologie" (75%) Et que je change de vie et deviens musicien

Quand je réinitialise mes centres d'intérêt

Alors je peux repartir avec toutes les jauges à 50% Et découvrir du contenu "Musique" et "Culture" sans biais

## 10. Pas de suggestion automatique de réinitialisation

Étant donné que je n'ai pas utilisé l'app depuis 1 an

Quand je me reconnecte

Alors aucune suggestion de réinitialisation n'est affichée Et mes jauges sont conservées telles quelles Et je garde le contrôle total

## 11. Historique conservé après réinitialisation

Étant donné que j'ai écouté 500 contenus

Quand je réinitialise mes centres d'intérêt

Alors mes jauges reviennent à 50% Mais mon historique d'écoute est conservé Et je peux toujours consulter mes anciens contenus écoutés

## 12. Évolution future basée sur nouvelles actions

Étant donné que j'ai réinitialisé mes jauges à 50%

Quand j'écoute 5 contenus "Voyage" à >80%

Alors ma jauge "Voyage" monte à 60% (50% + 5 × 2%) Et l'algorithme recommence à apprendre mes nouvelles préférences

## 13. Respect de l'historique utilisateur

Étant donné qu'un utilisateur aime "Cryptomonnaie" depuis 2 ans Et que sa jauge est à 90%

Quand 2 ans s'écoulent sans dégradation temporelle

Alors sa jauge reste à 90% Et le système ne fait pas d'"oubli" artificiel

## 14. Coût infrastructure zéro

Étant donné qu'aucune dégradation temporelle n'existe

Quand le système calcule les jauges

Alors aucun calcul de date n'est nécessaire Et aucun batch nocturne ne tourne Et aucun bug de fuseau horaire ne peut survenir Et le coût CPU est minimal

## 15. UX prévisible - jauge = actions

Étant donné qu'un utilisateur consulte sa jauge "Sport" à 65%

Quand il se demande pourquoi elle est à 65%

Alors il peut retracer ses actions:

    | action | impact |
    |---|---|
    | 10 likes automatiques | +10% |
    | 3 abonnements Sport | +15% |
    | 5 skips de contenu non-Sport | 0% |

Et il comprend que c'est le reflet exact de ses actions Et il n'y a pas de mystère ou automatisme caché

## 16. Statistiques affichées sans date

Étant donné que je consulte mes centres d'intérêt

Quand je vois mes jauges

Alors je vois:

    | information | affiché |
    |---|---|
    | Niveau actuel | ✅ 75% |
    | Évolution depuis début | ✅ +25% |
    | Dernière mise à jour | ❌ |

Et aucune date n'est affichée car non pertinente Et seules les actions comptent

# Évolution des jauges d'intérêt

> En tant que système de recommandation Je veux faire évoluer les jauges d'intérêt selon les actions utilisateur Afin d'affiner les recommandations personnalisées

21 scénarios (20 standards, 1 plan)

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et qu'un utilisateur est connecté

## 1. Like automatique renforcé après écoute ≥80%

Étant donné qu'un contenu de 5 minutes est tagué "Automobile" Et que ma jauge "Automobile" est à 45%

Quand j'écoute le contenu pendant 4 minutes 30 secondes (90%)

Alors je reçois un like automatique renforcé Et ma jauge "Automobile" augmente de 2% Et ma jauge "Automobile" est maintenant à 47%

## 2. Like automatique renforcé exactement à 80%

Étant donné qu'un contenu de 10 minutes est tagué "Voyage" Et que ma jauge "Voyage" est à 60%

Quand j'écoute le contenu pendant exactement 8 minutes (80%)

Alors je reçois un like automatique renforcé Et ma jauge "Voyage" augmente de 2% Et ma jauge "Voyage" est maintenant à 62%

## 3. Like automatique standard après écoute 30-79%

Étant donné qu'un contenu de 5 minutes est tagué "Automobile" Et que ma jauge "Automobile" est à 45%

Quand j'écoute le contenu pendant 2 minutes 30 secondes (50%)

Alors je reçois un like automatique standard Et ma jauge "Automobile" augmente de 1% Et ma jauge "Automobile" est maintenant à 46%

## 4. Like automatique standard à 30% exactement

Étant donné qu'un contenu de 10 minutes est tagué "Musique" Et que ma jauge "Musique" est à 40%

Quand j'écoute le contenu pendant exactement 3 minutes (30%)

Alors je reçois un like automatique standard Et ma jauge "Musique" augmente de 1%

## 5. Like automatique standard à 79%

Étant donné qu'un contenu de 10 minutes est tagué "Sport" Et que ma jauge "Sport" est à 55%

Quand j'écoute le contenu pendant 7 minutes 54 secondes (79%)

Alors je reçois un like automatique standard Et ma jauge "Sport" augmente de 1% Et ma jauge "Sport" est maintenant à 56%

## 6. Like explicite (manuel) +2%

Étant donné qu'un contenu est tagué "Économie" Et que ma jauge "Économie" est à 70%

Quand j'écoute le contenu partiellement Et que je clique manuellement sur le bouton "Like"

Alors ma jauge "Économie" augmente de 2% Et ma jauge "Économie" est maintenant à 72%

## 7. Like manuel cumulable avec like automatique

Étant donné qu'un contenu de 5 minutes est tagué "Automobile" Et que ma jauge "Automobile" est à 45%

Quand j'écoute le contenu pendant 2 minutes 30 secondes (50%)

Alors je reçois un like automatique standard (+1%)

Quand je clique ensuite sur le bouton "Like"

Alors ma jauge augmente encore de 2% (like manuel) Et ma jauge "Automobile" a augmenté de 3% au total Et ma jauge "Automobile" est maintenant à 48%

## 8. Abonnement créateur impacte tous ses tags

Étant donné qu'un créateur publie des contenus tagués "Automobile" et "Technologie" Et que mes jauges sont:

    | catégorie | niveau |
    |---|---|
    | Automobile | 50% |
    | Technologie | 45% |

Quand je m'abonne à ce créateur

Alors ma jauge "Automobile" augmente de 5% Et ma jauge "Technologie" augmente de 5% Et mes nouvelles jauges sont:

    | catégorie | niveau |
    |---|---|
    | Automobile | 55% |
    | Technologie | 50% |

## 9. Skip rapide (<10s) diminue la jauge

Étant donné qu'un contenu est tagué "Économie" Et que ma jauge "Économie" est à 45%

Quand je skip le contenu après 5 secondes

Alors ma jauge "Économie" diminue de 0.5% Et ma jauge "Économie" est maintenant à 44.5%

## 10. Skip à exactement 10s ne diminue pas la jauge

Étant donné qu'un contenu est tagué "Politique" Et que ma jauge "Politique" est à 50%

Quand je skip le contenu après exactement 10 secondes

Alors ma jauge "Politique" ne change pas Et reste à 50%

## 11. Skip tardif (≥30%) est neutre

Étant donné qu'un contenu de 10 minutes est tagué "Musique" Et que ma jauge "Musique" est à 60%

Quand j'écoute pendant 3 minutes (30%) Et que je skip ensuite

Alors ma jauge "Musique" ne diminue pas (signal neutre) Et ma jauge reste à 60% (plus le +1% de like auto si applicable)

## 12. Contenu avec plusieurs tags impacte toutes les jauges

Étant donné qu'un contenu est tagué "Automobile" et "Voyage" Et que mes jauges sont:

    | catégorie | niveau |
    |---|---|
    | Automobile | 45% |
    | Voyage | 60% |

Quand j'écoute le contenu à 90%

Alors les deux jauges augmentent de 2% Et mes nouvelles jauges sont:

    | catégorie | niveau |
    |---|---|
    | Automobile | 47% |
    | Voyage | 62% |

## 13. Contenu avec 3 tags impacte les 3 jauges

Étant donné qu'un contenu est tagué "Sport", "Santé" et "Technologie" Et que mes jauges sont à 50% pour chaque catégorie

Quand je skip rapidement après 5 secondes

Alors les 3 jauges diminuent de 0.5% Et toutes passent à 49.5%

## 14. Jauges bornées - ne peut pas dépasser 100%

Étant donné que ma jauge "Cryptomonnaie" est à 99% Et qu'un contenu tagué "Cryptomonnaie" est disponible

Quand j'écoute le contenu à 95% (like auto renforcé +2%)

Alors ma jauge "Cryptomonnaie" passe à 100% (maximum) Et ne dépasse pas 100%

## 15. Jauges bornées - ne peut pas descendre sous 0%

Étant donné que ma jauge "Politique" est à 0.3% Et qu'un contenu tagué "Politique" est disponible

Quand je skip rapidement après 3 secondes (-0.5%)

Alors ma jauge "Politique" passe à 0% (minimum) Et ne devient pas négative

## 16. Calcul immédiat à chaque action

Étant donné que ma jauge "Voyage" est à 50%

Quand j'écoute un contenu "Voyage" à 85%

Alors la jauge est mise à jour immédiatement (pas de batch) Et passe à 52%

Quand je demande mes recommandations dans la seconde suivante

Alors l'algorithme utilise déjà la valeur 52%

## 17. Like manuel après écoute <30% (pas de like auto)

Étant donné qu'un contenu de 10 minutes est tagué "Culture" Et que ma jauge "Culture" est à 60%

Quand j'écoute pendant 2 minutes (20%)

Alors je ne reçois pas de like automatique

Quand je clique sur le bouton "Like"

Alors ma jauge "Culture" augmente de 2% uniquement Et ma jauge "Culture" est maintenant à 62%

## 18. Unlike retire le like manuel

Étant donné que j'ai liké manuellement un contenu "Sport" Et que ma jauge "Sport" est passée de 55% à 57% (+2%)

Quand je clique sur "Unlike"

Alors ma jauge "Sport" diminue de 2% Et ma jauge "Sport" revient à 55%

## 19. Unlike ne peut pas retirer un like automatique

Étant donné que j'ai écouté un contenu "Musique" à 90% Et que j'ai reçu un like automatique renforcé (+2%) Et que ma jauge "Musique" est à 52%

Quand j'essaie de faire "Unlike"

Alors l'action n'est pas disponible Et ma jauge reste à 52%

## 20. Tags définis par créateur à la publication

Étant donné que je suis un créateur

Quand je publie un contenu

Alors je dois sélectionner 1 à 3 tags Et ces tags sont fixés après publication Et impacteront les jauges de tous les auditeurs

## 21. 📋 Plan: Calculs avec différentes durées d'écoute

Étant donné qu'un contenu de 10 minutes est tagué "Voyage" Et que ma jauge "Voyage" est à 50%

Quand j'écoute pendant ()

Alors ma jauge évolue de Et ma nouvelle jauge est à

📊 Exemples de données:

duree | pourcentage | impact | nouveau_niveau
--- | --- | --- | ---
1 min | 10% | 0% | 50%
3 min | 30% | +1% | 51%
5 min | 50% | +1% | 51%
7.9 min | 79% | +1% | 51%
8 min | 80% | +2% | 52%
9.5 min | 95% | +2% | 52%
5 sec | <1% | -0.5% | 49.5%

# Jauge initiale et cold start

> En tant que nouvel utilisateur Je veux que mes jauges d'intérêt démarrent de manière neutre Afin de découvrir du contenu sans biais initial

15 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible

## 1. Inscription - toutes les jauges à 50%

Quand je m'inscris sur RoadWave

Alors toutes mes jauges d'intérêt sont initialisées à 50% Et je ne dois pas remplir de questionnaire Et l'inscription est ultra-rapide

## 2. Liste des catégories disponibles

Étant donné que je suis un nouvel utilisateur

Quand je consulte mes centres d'intérêt

Alors je vois les catégories suivantes à 50%:

    | catégorie |
    |---|
    | Automobile |
    | Voyage |
    | Famille |
    | Amour |
    | Musique |
    | Économie |
    | Cryptomonnaie |
    | Politique |
    | Culture générale |
    | Sport |
    | Technologie |
    | Santé |

## 3. Cold start - premier contenu écouté

Étant donné que je viens de m'inscrire Et que toutes mes jauges sont à 50%

Quand j'écoute mon premier podcast "Automobile" à 90%

Alors ma jauge "Automobile" monte à 52% (+2%) Et toutes les autres jauges restent à 50%

## 4. Cold start - premier skip

Étant donné que je viens de m'inscrire Et que toutes mes jauges sont à 50%

Quand je skip rapidement un contenu "Économie"

Alors ma jauge "Économie" descend à 49.5% (-0.5%) Et toutes les autres jauges restent à 50%

## 5. Après 10 écoutes, profil commence à se dessiner

Étant donné que je suis un nouvel utilisateur Et que j'ai écouté:

    | contenu | tags | completion |
    |---|---|---|
    | Contenu 1 | Automobile | 90% |
    | Contenu 2 | Automobile, Sport | 85% |
    | Contenu 3 | Voyage | 75% |
    | Contenu 4 | Économie | skip 5s |
    | Contenu 5 | Automobile | 95% |
    | Contenu 6 | Sport | 80% |
    | Contenu 7 | Politique | skip 8s |
    | Contenu 8 | Voyage | 88% |
    | Contenu 9 | Automobile | 92% |
    | Contenu 10 | Technologie | 40% |

Alors mes jauges reflètent mes préférences:

    | catégorie | tendance |
    |---|---|
    | Automobile | Forte hausse (>55%) |
    | Voyage | Hausse modérée (~53%) |
    | Sport | Hausse modérée (~53%) |
    | Économie | Baisse légère (~49.5%) |
    | Politique | Baisse légère (~49.5%) |
    | Technologie | Neutre (~51%) |

## 6. Pas de questionnaire onboarding par défaut

Quand je termine l'inscription

Alors aucun questionnaire de centres d'intérêt n'est affiché Et je peux commencer à écouter immédiatement Et l'algorithme apprend naturellement

## 7. Algorithme avec jauges à 50% - chances égales

Étant donné que toutes mes jauges sont à 50%

Quand l'algorithme calcule les recommandations

Alors tous les types de contenus ont une chance égale Et aucun biais initial n'est appliqué Et la géolocalisation prime sur les intérêts

## 8. Questionnaire optionnel après 3 écoutes (post-MVP)

Étant donné que j'ai écouté 3 contenus

Quand je termine ma 3ème écoute

Alors je vois une notification in-app optionnelle:

    | titre | Améliorez vos recommandations |
    |---|---|
    | message | Sélectionnez vos centres d'intérêt |
    | actions | Configurer maintenant / Plus tard |

## 9. Remplir le questionnaire optionnel (post-MVP)

Étant donné que le questionnaire optionnel est affiché

Quand je sélectionne les centres d'intérêt suivants:

    | catégorie |
    |---|
    | Automobile |
    | Voyage |
    | Sport |

Alors les jauges sélectionnées passent à 70% Et les jauges non sélectionnées passent à 30% Et je vois le message "Vos préférences ont été enregistrées"

## 10. Skipper le questionnaire optionnel (post-MVP)

Étant donné que le questionnaire optionnel est affiché

Quand je clique sur "Plus tard"

Alors toutes mes jauges conservent 50% Et l'algorithme continue d'apprendre naturellement Et je ne suis plus sollicité

## 11. Comportement déterministe et testable

Étant donné deux nouveaux utilisateurs A et B

Quand les deux s'inscrivent au même moment

Alors leurs jauges sont identiques (toutes à 50%) Et leurs recommandations initiales sont identiques (basées sur géo uniquement)

## 12. Équité entre créateurs au cold start

Étant donné qu'un nouvel utilisateur s'inscrit Et qu'il existe 1000 contenus de catégories variées dans sa zone

Quand l'algorithme génère les premières recommandations

Alors tous les contenus ont une pondération intérêts identique (50%) Et seuls la géolocalisation et l'engagement différencient les contenus Et aucun créateur n'a d'avantage initial

## 13. Catégories extensibles

Étant donné que RoadWave ajoute une nouvelle catégorie "Gastronomie"

Quand je consulte mes centres d'intérêt

Alors je vois la nouvelle catégorie "Gastronomie" à 50% Et je peux commencer à l'explorer normalement

## 14. Voir l'évolution de mes jauges

Étant donné que je suis un utilisateur avec historique

Quand je consulte mes centres d'intérêt dans les paramètres

Alors je vois mes jauges actuelles:

    | catégorie | niveau | evolution |
    |---|---|---|
    | Automobile | 67% | +17% |
    | Voyage | 82% | +32% |
    | Économie | 34% | -16% |
    | Sport | 50% | 0% |

Et je comprends mes préférences actuelles

## 15. Friction zéro à l'inscription

Étant donné que je veux m'inscrire rapidement

Quand je remplis les 4 champs obligatoires Et que je clique sur "S'inscrire"

Alors mon compte est créé immédiatement Et je peux commencer à écouter dans les 30 secondes Et aucune configuration supplémentaire n'est requise

# Synchronisation actions offline

> En tant qu'utilisateur Je veux que mes actions offline soient synchronisées quand je me reconnecte Afin de ne perdre aucune interaction même sans connexion

45 scénarios

> Contexte commun à tous les scénarios

> Étant donné que j'utilise l'application RoadWave

## 1. Like d'un contenu en mode offline

Étant donné que je n'ai aucune connexion Internet

Quand je like un contenu téléchargé

Alors l'action est enregistrée localement dans SQLite: Et l'UI affiche immédiatement le like (optimistic update)

## 2. Unlike d'un contenu en mode offline

Étant donné que je n'ai aucune connexion Internet Et que j'avais liké un contenu

Quand je retire mon like

Alors l'action est enregistrée localement: Et l'UI retire immédiatement le like

## 3. Abonnement à un créateur en mode offline

Étant donné que je n'ai aucune connexion Internet

Quand je m'abonne à un créateur

Alors l'action est enregistrée localement: Et l'UI affiche immédiatement "Abonné ✓"

## 4. Désabonnement d'un créateur en mode offline

Étant donné que je n'ai aucune connexion Internet Et que j'étais abonné à un créateur

Quand je me désabonne

Alors l'action est enregistrée localement: Et l'UI affiche "S'abonner"

## 5. Signalement d'un contenu en mode offline

Étant donné que je n'ai aucune connexion Internet

Quand je signale un contenu pour "Contenu inapproprié"

Alors l'action est enregistrée localement: Et je vois "Signalement enregistré. Sera envoyé à la reconnexion."

## 6. Progression audio-guide en mode offline

Étant donné que je n'ai aucune connexion Internet Et que j'écoute un audio-guide multi-séquences

Quand je termine la séquence 3/10

Alors la progression est enregistrée localement: Et ma progression est sauvegardée

## 7. Multiple actions offline stockées en queue

Étant donné que je n'ai aucune connexion Internet pendant 2 jours

Quand j'effectue plusieurs actions:

    | action | cible |
    |---|---|
    | like | contenu A |
    | like | contenu B |
    | subscribe | créateur X |
    | unlike | contenu C |
    | report | contenu D |

Alors les 5 actions sont stockées dans pending_actions Et elles seront synchronisées dans l'ordre à la reconnexion

## 8. Détection reconnexion Internet

Étant donné que j'étais en mode offline

Quand l'app détecte une reconnexion Internet

Alors le processus de synchronisation démarre automatiquement Et je vois une notification "Synchronisation en cours..."

## 9. Récupération queue locale pendant sync

Étant donné que la synchronisation démarre

Quand l'app récupère les actions en attente

Alors une requête SQL est exécutée: Et toutes les actions sont récupérées dans l'ordre chronologique

## 10. Envoi batch API des actions

Étant donné que 15 actions sont en attente

Quand le batch est envoyé au backend

Alors une requête POST /sync/actions est faite: Et toutes les actions sont groupées en une seule requête

## 11. Backend traite chaque action

Étant donné que le backend reçoit le batch d'actions

Quand il traite chaque action

Alors pour chaque action:

    | étape | détail |
    |---|---|
    | Validation | Vérifier user_id, content_id valides |
    | Vérification existence | Contenu/créateur existe toujours ? |
    | Application action | INSERT/UPDATE/DELETE en base |
    | Mise à jour compteurs | Likes, abonnés, etc. |
    | Impact sur algorithme | Mise à jour jauges si nécessaire |

## 12. Confirmation réception et suppression queue locale

Étant donné que le backend a traité toutes les actions avec succès

Quand la confirmation est reçue par l'app

Alors les actions sont supprimées de la queue locale: Et la table pending_actions est vidée

## 13. Toast confirmation synchronisation

Étant donné que 15 actions ont été synchronisées

Quand la synchronisation se termine

Alors je vois un toast:

## 14. Synchronisation silencieuse si peu d'actions

Étant donné que j'ai seulement 2 actions en attente

Quand la synchronisation se termine

Alors aucun toast n'est affiché (sync silencieuse) Et l'expérience reste fluide Mais je peux voir le détail dans l'historique des syncs

## 15. Échec synchronisation - Retry automatique

Étant donné que la synchronisation échoue (erreur réseau)

Quand l'échec est détecté

Alors un retry automatique est programmé dans 30 secondes Et les actions restent dans pending_actions

## 16. 3 tentatives échouées - Notification utilisateur

Étant donné que 3 tentatives de synchronisation ont échoué

Quand la 3ème tentative échoue

Alors je reçois une notification:

## 17. Actions conservées jusqu'à sync réussie

Étant donné que la synchronisation échoue plusieurs fois

Quand les tentatives continuent d'échouer

Alors les actions restent dans pending_actions Et aucune action n'est perdue Et elles seront envoyées dès que la connexion sera stable

## 18. Rétention max 7 jours - Purge automatique

Étant donné qu'une action est en attente depuis 7 jours

Quand le système détecte cette ancienneté

Alors l'action est automatiquement supprimée de la queue Et je vois "1 action trop ancienne supprimée (>7 jours)" Et cela évite une queue infinie

## 19. Justification rétention 7 jours

Étant donné qu'un utilisateur ne se connecte jamais pendant 2 semaines

Quand ses actions ont >7 jours

Alors elles sont purgées automatiquement Et évite une queue qui grandit indéfiniment

## 20. Retry manuel après échec

Étant donné que la synchronisation a échoué

Quand je clique sur "Réessayer maintenant"

Alors une nouvelle tentative de synchronisation est lancée immédiatement Et si elle réussit, les actions sont synchronisées

## 21. Backend retourne contenus supprimés

Étant donné que j'ai liké un contenu offline Mais que le contenu a été supprimé entre temps

Quand le backend traite la synchronisation

Alors il retourne:

## 22. App supprime fichiers locaux contenus supprimés

Étant donné que le backend retourne deleted_content_ids: [123, 456]

Quand l'app traite la réponse

Alors elle supprime les fichiers locaux des contenus 123 et 456 Et libère l'espace disque Et les actions associées sont retirées de la queue

## 23. Contenu supprimé en cours d'écoute

Étant donné que j'écoute le contenu 123 en offline Et que la sync détecte que le contenu a été supprimé

Quand la lecture actuelle se termine

Alors l'app attend 2 secondes Et passe automatiquement au contenu suivant Et le fichier du contenu 123 est supprimé en arrière-plan

## 24. Toast notification contenu retiré

Étant donné que 2 contenus téléchargés ont été supprimés

Quand la synchronisation se termine

Alors je vois un toast:

## 25. Contenu modéré après téléchargement

Étant donné que j'ai téléchargé un contenu qui est ensuite modéré

Quand la synchronisation détecte la modération

Alors le contenu est immédiatement supprimé du device Et je ne peux plus l'écouter Et cela garantit la conformité même offline

## 26. Justification pas de conflit possible

Étant donné que les actions offline sont unilatérales (likes, abonnements)

Quand elles sont synchronisées

Alors il n'y a pas de conflit de version possible Et pas de merge complexe nécessaire

## 27. Justification UX fluide offline

Étant donné que toutes les actions fonctionnent offline

Quand l'utilisateur interagit sans connexion

Alors l'expérience est identique au mode online Et l'utilisateur n'est pas bloqué Et peut utiliser l'app normalement

## 28. Justification batch = Économie requêtes

Étant donné que 15 actions sont en attente

Quand elles sont synchronisées en batch

Alors 1 seule requête HTTP est envoyée (vs 15 si individuelles) Et cela économise la bande passante et la batterie Et réduit la charge serveur

## 29. Justification conformité modération offline

Étant donné qu'un contenu illégal est modéré pendant qu'un user est offline

Quand le user se reconnecte

Alors le contenu est immédiatement supprimé de son device Et cela garantit que les contenus illégaux disparaissent même offline

## 30. Historique synchronisations

Étant donné que j'accède à "Paramètres > Synchronisation"

Quand je consulte l'historique

Alors je vois:

    | date | actions sync | statut |
    |---|---|---|
    | 15/06/2025 14:30:00 | 15 | Réussi ✅ |
    | 14/06/2025 09:15:00 | 7 | Réussi ✅ |
    | 13/06/2025 18:45:00 | 3 | Échec ❌ |

## 31. Détail d'une synchronisation

Étant donné que je clique sur une ligne de l'historique

Quand le détail s'affiche

Alors je vois:

## 32. Compteur actions en attente visible

Étant donné que j'ai 12 actions en attente de synchronisation

Quand j'accède à l'onglet Profil

Alors je vois un badge "12" sur l'icône de synchronisation Et je sais qu'il y a des actions en attente

## 33. Synchronisation manuelle forcée

Étant donné que je veux forcer une synchronisation immédiate

Quand je vais dans "Paramètres > Synchronisation" Et que je clique sur "Synchroniser maintenant"

Alors la synchronisation démarre immédiatement Et toutes les actions en attente sont envoyées

## 34. Statistiques utilisateur - Syncs effectuées

Étant donné que j'accède à mes statistiques

Quand je consulte la section Synchronisation

Alors je vois:

    | métrique | valeur |
    |---|---|
    | Synchronisations depuis début | 87 |
    | Actions synchronisées total | 1,234 |
    | Taux de succès | 94% |
    | Dernière sync | Il y a 2h |

## 35. Statistiques admin - Volume synchronisations

Étant donné qu'un admin consulte les métriques de synchronisation

Quand il accède au dashboard

Alors il voit:

    | métrique | valeur |
    |---|---|
    | Synchronisations/jour | 45,678 |
    | Actions synchronisées/jour | 234,567 |
    | Taux succès sync | 96.5% |
    | Temps moyen traitement batch | 0.8s |
    | Actions en attente (global) | 12,345 |

## 36. Alerte admin si taux échec sync >10%

Étant donné que le taux d'échec sync dépasse 10%

Quand le système détecte cette anomalie

Alors une alerte est envoyée:

## 37. Synchronisation rapide <2s

Étant donné que j'ai 20 actions en attente

Quand la synchronisation démarre

Alors le traitement prend <2 secondes Et je ne remarque aucun ralentissement de l'app

## 38. Synchronisation de gros batch (100 actions)

Étant donné que je n'ai pas synchronisé pendant 1 semaine Et que j'ai 100 actions en attente

Quand la synchronisation démarre

Alors le batch de 100 actions est traité en <5 secondes Et toutes les actions sont synchronisées avec succès

## 39. Gestion charge serveur - 10 000 syncs simultanées

Étant donné que 10 000 utilisateurs se reconnectent simultanément

Quand chacun envoie un batch de 20 actions

Alors le serveur traite 200 000 actions Et grâce au traitement asynchrone (queue Redis), le temps de réponse reste <3s Et aucun timeout n'est constaté

## 40. Stockage SQLite optimisé

Étant donné que la table pending_actions stocke des centaines d'actions

Quand des requêtes sont exécutées

Alors la table est indexée sur created_at Et les requêtes SELECT et DELETE sont instantanées (<10ms) Et l'expérience utilisateur reste fluide

## 41. Nettoyage automatique table pending_actions

Étant donné que la table pending_actions grossit avec le temps

Quand les actions sont synchronisées et supprimées

Alors la table est automatiquement optimisée (VACUUM sur SQLite) Et l'espace disque est libéré Et les performances restent optimales

## 42. Action dupliquée - Idempotence

Étant donné que j'ai liké un contenu offline Et que la sync échoue et retry

Quand le backend reçoit 2 fois le même like

Alors il applique l'idempotence (1 seul like enregistré) Et le compteur de likes n'est pas faussé

## 43. Séquence like/unlike offline

Étant donné que j'ai liké puis unliké un contenu offline

Quand les 2 actions sont synchronisées

Alors le backend applique les 2 actions dans l'ordre Et le résultat final est "pas de like" (état correct)

## 44. Abonnement puis désabonnement offline

Étant donné que je me suis abonné puis désabonné d'un créateur offline

Quand les 2 actions sont synchronisées

Alors le backend applique les 2 actions dans l'ordre Et le résultat final est "pas abonné" Et les jauges évoluent correctement (+5% puis -5% = 0% net)

## 45. Créateur supprimé pendant offline

Étant donné que je me suis abonné à un créateur offline Mais que le créateur a supprimé son compte entre temps

Quand la sync traite l'abonnement

Alors le backend retourne "creator_deleted" Et l'action est ignorée silencieusement Et aucune erreur n'est affichée à l'utilisateur

# Téléchargement de contenus offline

> En tant qu'utilisateur Je veux télécharger des contenus pour les écouter sans connexion Afin de profiter de RoadWave même dans les zones sans réseau

49 scénarios

> Contexte commun à tous les scénarios

> Étant donné que je suis connecté à l'application RoadWave

## 1. Option "Autour de moi" - Rayon 50 km

Étant donné que je suis à Paris (position GPS détectée)

Quand je sélectionne "Télécharger > Autour de moi"

Alors l'app recherche tous les contenus géolocalisés dans un rayon de 50 km Et je vois une liste de contenus de Paris et banlieue proche Et l'estimation affiche "~150 contenus disponibles"

## 2. Option "Ma ville" - Limite administrative détectée

Étant donné que je suis à Lyon (position GPS détectée)

Quand je sélectionne "Télécharger > Ma ville"

Alors l'app détecte automatiquement "Lyon" comme ville Et recherche tous les contenus géolocalisés "Lyon" Et je vois uniquement les contenus de la ville de Lyon (pas banlieue)

## 3. Option "Mon département" - Sélection dans liste

Étant donné que je veux télécharger des contenus pour un département

Quand je sélectionne "Télécharger > Mon département"

Alors je vois une liste de tous les départements français:

    | département |
    |---|
    | 01 - Ain |
    | 02 - Aisne |
    | 75 - Paris |
    | 69 - Rhône |
    | ... |

Et je peux choisir un département

## 4. Sélection département et téléchargement contenus

Étant donné que je sélectionne "75 - Paris" dans la liste des départements

Quand la sélection est confirmée

Alors l'app recherche tous les contenus géolocalisés "Paris" Et je vois "~234 contenus disponibles pour Paris"

## 5. Option "Ma région" - Sélection dans liste

Étant donné que je veux télécharger des contenus pour une région

Quand je sélectionne "Télécharger > Ma région"

Alors je vois une liste de toutes les régions françaises:

    | région |
    |---|
    | Auvergne-Rhône-Alpes |
    | Bretagne |
    | Île-de-France |
    | Nouvelle-Aquitaine |
    | Occitanie |
    | ... |

Et je peux choisir une région

## 6. Sélection région et téléchargement contenus

Étant donné que je sélectionne "Bretagne" dans la liste des régions

Quand la sélection est confirmée

Alors l'app recherche tous les contenus géolocalisés des départements bretons:

    | département |
    |---|
    | Côtes-d'Armor (22) |
    | Finistère (29) |
    | Ille-et-Vilaine (35) |
    | Morbihan (56) |

Et je vois "~487 contenus disponibles pour Bretagne"

## 7. Recherche manuelle ville

Étant donné que je veux télécharger des contenus pour une ville spécifique

Quand je tape "Marseille" dans la barre de recherche

Alors l'app propose des suggestions:

    | suggestion |
    |---|
    | Marseille (13) |
    | Marseille-en-Beauvaisis |

Et je peux sélectionner "Marseille (13)"

## 8. Recherche manuelle avec autocomplétion

Étant donné que je tape "Ly" dans la barre de recherche

Quand l'autocomplétion s'active

Alors je vois des suggestions:

    | suggestion |
    |---|
    | Lyon (69) |
    | Lys-lez-Lannoy |

Et je peux affiner ma recherche

## 9. Utilisateur gratuit - Limite 50 contenus max

Étant donné que je suis un utilisateur gratuit Et que j'ai déjà téléchargé 45 contenus

Quand j'accède à la page Téléchargements

Alors je vois "45 / 50 contenus téléchargés" Et je peux télécharger 5 contenus supplémentaires maximum

## 10. Utilisateur gratuit - Tentative dépasser limite 50

Étant donné que je suis gratuit et j'ai déjà 50 contenus téléchargés

Quand j'essaie de télécharger un 51ème contenu

Alors le téléchargement est refusé Et je vois le message:

## 11. Utilisateur Premium - Téléchargements illimités

Étant donné que je suis un utilisateur Premium Et que j'ai déjà téléchargé 245 contenus

Quand j'accède à la page Téléchargements

Alors je vois "245 contenus (3.2 GB)" Et aucune limite n'est affichée Et je peux télécharger autant de contenus que je veux

## 12. Limite Premium = Espace disque disponible

Étant donné que je suis Premium Et que mon device a 500 MB d'espace disque disponible

Quand j'essaie de télécharger 100 contenus (2 GB)

Alors le téléchargement échoue après ~50 contenus (500 MB) Et je vois "Espace disque insuffisant. Libérez de l'espace pour continuer."

## 13. Calcul temps écoute disponible gratuit

Étant donné que je suis gratuit avec 50 contenus téléchargés Et que la durée moyenne d'un contenu est 5 minutes

Quand je calcule le temps d'écoute disponible

Alors 50 contenus × 5 min = 250 minutes = 4h10 d'écoute Et cela suffit pour un trajet quotidien ou road trip court

## 14. Calcul temps écoute disponible Premium illimité

Étant donné que je suis Premium avec 300 contenus téléchargés Et que la durée moyenne est 5 minutes

Quand je calcule le temps d'écoute disponible

Alors 300 contenus × 5 min = 1500 minutes = 25h d'écoute Et cela suffit pour un road trip de plusieurs jours

## 15. Téléchargement par défaut en WiFi uniquement

Étant donné que je suis connecté en WiFi

Quand je clique sur "Télécharger 20 contenus"

Alors le téléchargement démarre immédiatement Et aucune popup de confirmation n'apparaît

## 16. Tentative téléchargement en données mobiles - Popup confirmation

Étant donné que je suis connecté en 4G (pas de WiFi)

Quand je clique sur "Télécharger 20 contenus"

Alors une popup apparaît:

## 17. Calcul estimation consommation data mobile

Étant donné que je veux télécharger 20 contenus Et que la durée moyenne est 5 minutes Et que la qualité Standard est 48 kbps Opus

Quand l'estimation est calculée

Alors consommation = 20 contenus × 5 min × 48 kbps / 8 = 72 MB Et ce montant est affiché dans la popup

## 18. Confirmation téléchargement en données mobiles

Étant donné que je vois la popup de confirmation données mobiles

Quand je clique sur "Continuer quand même"

Alors le téléchargement démarre immédiatement via 4G Et la consommation data est comptabilisée sur mon forfait mobile

## 19. Refus téléchargement données mobiles - Attendre WiFi

Étant donné que je vois la popup de confirmation données mobiles

Quand je clique sur "Attendre WiFi"

Alors les téléchargements sont mis en file d'attente Et ils démarreront automatiquement quand le WiFi sera détecté

## 20. Détection automatique WiFi et reprise téléchargements

Étant donné que j'ai mis 20 contenus en file d'attente (attente WiFi)

Quand l'app détecte une connexion WiFi

Alors les téléchargements démarrent automatiquement Et je reçois une notification "Téléchargements en cours via WiFi"

## 21. Qualité Standard (48 kbps) par défaut

Étant donné que je configure mes téléchargements

Quand j'accède aux paramètres de qualité

Alors la qualité "Standard (48 kbps - ~20 MB/h)" est sélectionnée par défaut Et elle est disponible pour tous (gratuit + Premium)

## 22. Qualité Basse (24 kbps) disponible pour tous

Étant donné que j'ai peu d'espace disque disponible

Quand je sélectionne qualité "Basse (24 kbps - ~10 MB/h)"

Alors mes prochains téléchargements seront en 24 kbps Et l'espace utilisé sera divisé par 2 par rapport à Standard Et cette option est disponible pour gratuit + Premium

## 23. Qualité Haute (64 kbps) réservée Premium

Étant donné que je suis un utilisateur gratuit

Quand je consulte les options de qualité

Alors l'option "Haute (64 kbps - ~30 MB/h)" est grisée Et je vois "👑 Premium uniquement" Et je ne peux pas la sélectionner

## 24. Utilisateur Premium peut choisir qualité Haute

Étant donné que je suis un utilisateur Premium

Quand je consulte les options de qualité

Alors l'option "Haute (64 kbps - ~30 MB/h)" est disponible Et je peux la sélectionner pour mes téléchargements Et la qualité audio sera excellente (meilleure restitution voix et ambiances)

## 25. Comparaison taille fichiers selon qualité

Étant donné que je veux télécharger 50 contenus de 5 min chacun

Quand je compare les qualités

Alors les tailles totales sont:

    | qualité | bitrate | taille totale |
    |---|---|---|
    | Basse | 24 kbps | ~250 MB |
    | Standard | 48 kbps | ~500 MB |
    | Haute | 64 kbps | ~650 MB |

## 26. Justification Standard = Bon compromis

Étant donné que le contenu RoadWave est principalement de la voix

Quand la qualité Standard (48 kbps Opus) est utilisée

Alors la qualité est très correcte pour la voix Et équivalente à la radio FM Et le compromis qualité/taille est optimal

## 27. Justification Haute réservée Premium = Incitation upgrade

Étant donné qu'un utilisateur gratuit veut la meilleure qualité

Quand il voit que Haute est réservée Premium

Alors cela l'incite à passer Premium pour 4.99€/mois Et c'est un avantage tangible supplémentaire de Premium

## 28. Changement qualité après téléchargements existants

Étant donné que j'ai déjà téléchargé 30 contenus en qualité Standard

Quand je change la qualité vers Haute (si Premium)

Alors les 30 contenus existants restent en Standard Et seuls les nouveaux téléchargements seront en Haute Et je peux manuellement re-télécharger les 30 contenus pour les avoir en Haute

## 29. Téléchargement individuel d'un contenu

Étant donné que je consulte la page d'un contenu

Quand je clique sur l'icône de téléchargement 📥

Alors le téléchargement démarre Et une barre de progression apparaît Et l'icône devient ✅ quand terminé

## 30. Téléchargement batch de contenus sélectionnés

Étant donné que je consulte une liste de contenus pour "Paris"

Quand je sélectionne 15 contenus manuellement Et que je clique sur "Télécharger la sélection"

Alors les 15 contenus sont téléchargés en parallèle (max 3 simultanés) Et une notification affiche "15 contenus téléchargés"

## 31. Téléchargement automatique recommandations zone

Étant donné que je sélectionne "Autour de moi" (Paris)

Quand je clique sur "Télécharger les 50 meilleurs contenus"

Alors l'algorithme sélectionne automatiquement les 50 contenus les mieux notés/récents Et les télécharge tous Et je n'ai pas besoin de choisir manuellement

## 32. Barre de progression téléchargement global

Étant donné que je télécharge 20 contenus

Quand les téléchargements sont en cours

Alors je vois une barre de progression globale:

## 33. Téléchargements en tâche de fond

Étant donné que je lance le téléchargement de 30 contenus

Quand je ferme l'app ou passe à une autre activité

Alors les téléchargements continuent en arrière-plan Et je reçois une notification quand tous sont terminés

## 34. Pause et reprise téléchargements

Étant donné que je télécharge 20 contenus

Quand je clique sur "Pause"

Alors les téléchargements en cours se terminent Et les téléchargements en attente sont mis en pause Et je peux cliquer sur "Reprendre" plus tard

## 35. Annulation téléchargements

Étant donné que je télécharge 20 contenus

Quand je clique sur "Annuler"

Alors tous les téléchargements sont arrêtés Et les fichiers partiels sont supprimés Et l'espace disque est libéré

## 36. Gestion erreurs téléchargement

Étant donné que je télécharge un contenu Mais que la connexion Internet coupe au milieu

Quand la connexion revient

Alors le téléchargement reprend automatiquement où il s'était arrêté Et aucune perte de progression n'a lieu

## 37. Retry automatique après échec

Étant donné qu'un téléchargement échoue 3 fois consécutives

Quand l'échec est détecté

Alors le contenu est marqué "Échec" Et je vois une notification "3 contenus n'ont pas pu être téléchargés" Et je peux retry manuellement en cliquant sur "Réessayer"

## 38. Liste contenus téléchargés

Étant donné que j'ai téléchargé 45 contenus

Quand j'accède à "Téléchargements"

Alors je vois la liste complète de mes 45 contenus Et pour chaque contenu: titre, créateur, durée, taille, date téléchargement

## 39. Tri contenus téléchargés

Étant donné que je consulte ma liste de téléchargements

Quand je clique sur "Trier par"

Alors je peux trier par:

    | critère | ordre |
    |---|---|
    | Date téléchargement | Plus récent / Plus ancien |
    | Titre | A-Z / Z-A |
    | Créateur | A-Z / Z-A |
    | Durée | Plus long / Plus court |
    | Taille | Plus gros / Plus petit |

## 40. Recherche dans contenus téléchargés

Étant donné que j'ai 200 contenus téléchargés

Quand je tape "Tesla" dans la barre de recherche

Alors seuls les contenus contenant "Tesla" s'affichent Et je peux rapidement trouver un contenu spécifique

## 41. Suppression individuelle contenu téléchargé

Étant donné que je veux supprimer un contenu téléchargé

Quand je swipe left (iOS) ou long press (Android) sur le contenu Et que je clique sur "Supprimer"

Alors le fichier est supprimé du device Et l'espace disque est libéré Et le compteur est décrémenté (ex: 45/50 → 44/50)

## 42. Suppression batch contenus téléchargés

Étant donné que je veux supprimer plusieurs contenus

Quand je sélectionne 10 contenus Et que je clique sur "Supprimer la sélection"

Alors les 10 fichiers sont supprimés Et ~100 MB d'espace disque sont libérés Et une notification confirme "10 contenus supprimés"

## 43. Suppression tous les contenus téléchargés

Étant donné que j'ai 45 contenus téléchargés

Quand je clique sur "Supprimer tout" Et que je confirme l'action

Alors tous les 45 contenus sont supprimés Et l'espace disque total est libéré (~450 MB) Et le compteur repasse à 0/50

## 44. Espace disque utilisé visible

Étant donné que j'ai téléchargé 45 contenus

Quand j'accède à la page Téléchargements

Alors je vois l'espace disque utilisé:

## 45. Statistiques téléchargements

Étant donné que j'accède à mes statistiques

Quand je consulte la section Téléchargements

Alors je vois:

    | métrique | valeur |
    |---|---|
    | Contenus actuellement téléchargés | 45 |
    | Espace disque utilisé | 478 MB |
    | Contenus téléchargés depuis début | 287 |
    | Total data téléchargée | 3.2 GB |
    | Téléchargements via WiFi | 92% |
    | Téléchargements via mobile | 8% |

## 46. Lecture contenu téléchargé sans connexion

Étant donné que je n'ai aucune connexion Internet (mode avion) Et que j'ai des contenus téléchargés

Quand je lance un contenu téléchargé

Alors la lecture démarre normalement depuis le fichier local Et aucune erreur de connexion n'apparaît

## 47. Badge "Téléchargé" sur contenus offline

Étant donné que j'ai téléchargé certains contenus

Quand je consulte une liste de contenus

Alors les contenus téléchargés ont un badge ✅ "Offline" Et je sais immédiatement lesquels sont disponibles sans connexion

## 48. Filtre "Téléchargés uniquement"

Étant donné que je veux voir uniquement mes contenus offline

Quand j'active le filtre "Téléchargés uniquement"

Alors seuls les contenus téléchargés s'affichent Et je peux facilement naviguer dans mon catalogue offline

## 49. Playlist offline automatique

Étant donné que j'ai téléchargé 45 contenus

Quand j'accède à "Téléchargements"

Alors je peux lancer une playlist aléatoire de mes 45 contenus Et profiter d'une écoute continue offline

# Validité et renouvellement contenus offline

> En tant qu'utilisateur Je veux que mes contenus téléchargés restent valides un certain temps Afin de garantir la légalité et la fraîcheur du contenu

38 scénarios

> Contexte commun à tous les scénarios

> Étant donné que je suis connecté à l'application RoadWave Et que j'ai des contenus téléchargés

## 1. Validité de 30 jours après téléchargement

Étant donné que je télécharge un contenu le 1er juin 2025

Quand le téléchargement est terminé

Alors le contenu est valide jusqu'au 1er juillet 2025 (30 jours) Et la date d'expiration est stockée en local

## 2. Affichage date expiration sur contenu téléchargé

Étant donné que j'ai téléchargé un contenu il y a 20 jours

Quand je consulte les détails du contenu

Alors je vois "Expire dans 10 jours" Et je sais combien de temps il reste avant expiration

## 3. Standard industrie aligné (Spotify, YouTube, Deezer)

Étant donné que Spotify, YouTube Music et Deezer utilisent 30 jours

Quand RoadWave fixe également 30 jours

Alors c'est le standard accepté par les utilisateurs Et il n'y a pas de confusion avec les autres plateformes

## 4. Justification 30 jours - Force reconnexion régulière

Étant donné qu'un utilisateur ne se connecte jamais

Quand ses contenus expirent après 30 jours

Alors il est obligé de se reconnecter pour les renouveler Et le système peut vérifier:

    | vérification |
    |---|
    | Abonnement Premium toujours actif |
    | Contenus non modérés/supprimés |
    | Métadonnées à jour |

## 5. Justification 30 jours - Évite stockage obsolète

Étant donné qu'un contenu a été modéré après téléchargement

Quand le contenu expire après 30 jours maximum

Alors le contenu illégal est automatiquement supprimé Et ne reste pas indéfiniment sur le device

## 6. Détection WiFi et contenus >25 jours

Étant donné que j'ai des contenus téléchargés il y a 26 jours

Quand l'app détecte une connexion WiFi

Alors une requête GET /offline/contents/refresh est envoyée Et le backend vérifie chaque contenu

## 7. Vérification abonnement Premium toujours actif

Étant donné qu'un contenu téléchargé en Premium est à renouveler

Quand le backend vérifie le statut Et que l'abonnement Premium est toujours actif

Alors la validité est renouvelée à 30 jours supplémentaires

## 8. Abonnement Premium expiré - Contenu non renouvelé

Étant donné qu'un contenu Premium téléchargé est à renouveler

Quand le backend vérifie le statut Et que l'abonnement Premium a expiré

Alors le contenu n'est pas renouvelé Et il sera supprimé à l'expiration (J-0) Et l'utilisateur voit "Contenu Premium expiré (abonnement inactif)"

## 9. Vérification contenu pas modéré/supprimé

Étant donné qu'un contenu téléchargé est à renouveler

Quand le backend vérifie le statut Et que le contenu a été modéré ou supprimé entre temps

Alors le contenu n'est pas renouvelé Et sera supprimé immédiatement du device Et l'utilisateur voit "1 contenu retiré (violation règles)"

## 10. Mise à jour métadonnées lors du renouvellement

Étant donné qu'un contenu téléchargé est renouvelé

Quand le backend traite le renouvellement

Alors les métadonnées sont mises à jour:

    | métadonnée | mise à jour si changée |
    |---|---|
    | Titre | ✅ |
    | Nom créateur | ✅ |
    | Description | ✅ |
    | Tags | ✅ |
    | Statut Premium | ✅ |

Et l'utilisateur voit les infos à jour

## 11. Pas de re-téléchargement audio si fichier OK

Étant donné qu'un contenu est renouvelé

Quand le fichier audio local est intact

Alors seules les métadonnées sont mises à jour Et le fichier audio n'est pas re-téléchargé Et cela économise la bande passante

## 12. Re-téléchargement audio si fichier corrompu

Étant donné qu'un contenu est renouvelé

Quand le fichier audio local est corrompu (checksum invalide)

Alors le fichier audio est re-téléchargé entièrement Et le nouveau fichier remplace le corrompu

## 13. Renouvellement silencieux si WiFi régulier

Étant donné que je me connecte en WiFi tous les jours

Quand mes contenus atteignent 25-30 jours

Alors ils sont automatiquement renouvelés en arrière-plan Et je ne vois aucune notification (processus transparent) Et mes contenus restent valides indéfiniment

## 14. Renouvellement batch de plusieurs contenus

Étant donné que j'ai 30 contenus à renouveler

Quand le renouvellement automatique se déclenche

Alors une requête batch est envoyée: Et le backend traite les 30 contenus en une seule requête Et cela économise les requêtes HTTP

## 15. Temps de traitement renouvellement

Étant donné que 30 contenus sont à renouveler

Quand la requête batch est traitée

Alors le backend répond en <2 secondes Et les métadonnées sont mises à jour localement Et l'utilisateur ne remarque aucun ralentissement

## 16. Notification J-3 avant expiration

Étant donné que j'ai 15 contenus qui expirent dans 3 jours

Quand le système vérifie les expirations

Alors je reçois une notification: Et je peux agir avant l'expiration

## 17. Pas de notification si connexion WiFi régulière

Étant donné que je me connecte en WiFi tous les jours Et que mes contenus sont automatiquement renouvelés

Quand le système vérifie les expirations

Alors aucune notification J-3 n'est envoyée

## 18. Notification uniquement si contenus non renouvelés

Étant donné que j'ai 20 contenus dont 15 renouvelés et 5 non renouvelés

Quand le J-3 arrive pour les 5 non renouvelés

Alors je reçois "5 contenus expirent dans 3 jours" Et seuls les contenus à risque sont mentionnés

## 19. Action utilisateur après notification J-3

Étant donné que je reçois la notification J-3

Quand je clique sur la notification

Alors l'app s'ouvre sur la page Téléchargements Et je vois les contenus qui vont expirer en rouge Et je peux me connecter en WiFi pour les renouveler

## 20. Suppression automatique J-0 (expiration)

Étant donné qu'un contenu n'a pas été renouvelé

Quand le jour d'expiration arrive (J-0)

Alors le fichier est automatiquement supprimé du device Et l'espace disque est libéré Et le compteur est décrémenté (ex: 45/50 → 44/50)

## 21. Toast après suppression automatique J-0

Étant donné que 15 contenus viennent d'expirer

Quand l'utilisateur ouvre l'app

Alors il voit un toast:

## 22. Liste contenus supprimés après expiration

Étant donné que 15 contenus ont expiré

Quand je consulte l'historique des suppressions

Alors je vois la liste des 15 contenus supprimés:

    | titre | créateur | date expiration |
    |---|---|---|
    | Mon épisode préféré | JeanDupont | 15 juin 2025 |
    | Road trip Bretagne | MarieLambert | 15 juin 2025 |
    | ... | ... | ... |

Et je peux les re-télécharger si je veux

## 23. Re-téléchargement après expiration

Étant donné qu'un contenu a expiré et été supprimé

Quand je retrouve ce contenu dans l'app

Alors le badge ✅ "Offline" n'est plus affiché Et je peux le re-télécharger normalement Et la validité repart à 30 jours

## 24. Utilisateur ne se connecte jamais pendant 30 jours

Étant donné que je télécharge 50 contenus le 1er juin Mais que je ne me connecte jamais en WiFi pendant 30 jours

Quand le 1er juillet arrive

Alors tous les 50 contenus expirent Et sont automatiquement supprimés Et je n'ai plus aucun contenu offline

## 25. Utilisateur en zone blanche 30+ jours

Étant donné que je télécharge 50 contenus avant de partir en zone sans réseau Et que je reste 45 jours sans connexion

Quand les contenus expirent après 30 jours

Alors ils sont supprimés même si je ne peux pas me connecter Et je perds l'accès à mes contenus offline

## 26. Recommandation téléchargement avant zone blanche longue

Étant donné que je prépare un road trip de 60 jours

Quand je consulte la FAQ

Alors je vois la recommandation:

## 27. Changement statut Premium en gratuit pendant validité

Étant donné que je suis Premium et j'ai téléchargé 200 contenus

Quand mon abonnement Premium expire Et que je repasse en gratuit

Alors au prochain renouvellement, seulement 50 contenus sont conservés Et les 150 autres sont supprimés (limite gratuit) Et je vois "Limite gratuit (50 contenus) appliquée. 150 contenus supprimés."

## 28. Sélection automatique 50 meilleurs contenus si passage gratuit

Étant donné que je repasse en gratuit avec 200 contenus téléchargés

Quand le système applique la limite de 50

Alors les 50 contenus les plus récemment écoutés sont conservés Et les 150 autres sont supprimés Et cela maximise les chances de garder les contenus que j'aime

## 29. Contenus Premium exclusifs supprimés si abonnement expire

Étant donné que j'ai téléchargé 20 contenus Premium exclusifs

Quand mon abonnement Premium expire

Alors les 20 contenus Premium sont immédiatement supprimés Et je vois "20 contenus Premium supprimés (abonnement expiré)"

## 30. Affichage temps restant avant expiration

Étant donné que j'ai 45 contenus téléchargés

Quand je consulte la page Téléchargements

Alors je vois pour chaque contenu:

    | contenu | temps restant |
    |---|---|
    | Mon épisode (récent) | Expire dans 28 jours |
    | Road trip (ancien) | Expire dans 3 jours |

Et je sais lesquels sont prioritaires pour renouvellement

## 31. Tri par date expiration

Étant donné que j'ai 45 contenus avec différentes dates d'expiration

Quand je trie par "Expiration"

Alors les contenus qui expirent le plus tôt apparaissent en premier Et je peux voir rapidement lesquels nécessitent une reconnexion urgente

## 32. Badge rouge si expiration <3 jours

Étant donné qu'un contenu expire dans 2 jours

Quand je consulte la liste des téléchargements

Alors le contenu a un badge rouge "⚠️ Expire bientôt" Et il est visuellement mis en avant

## 33. Statistiques utilisateur - Taux de renouvellement

Étant donné que j'accède à mes statistiques

Quand je consulte la section Téléchargements

Alors je vois:

    | métrique | valeur |
    |---|---|
    | Contenus actuels | 45 |
    | Contenus expirés depuis début | 87 |
    | Contenus renouvelés (auto) | 234 |
    | Taux renouvellement automatique | 73% |

## 34. Statistiques admin - Taux expiration global

Étant donné qu'un admin consulte les métriques offline

Quand il accède au dashboard

Alors il voit:

    | métrique | valeur |
    |---|---|
    | Contenus téléchargés actifs | 1,234,567 |
    | Expirations ce mois | 45,678 |
    | Taux expiration | 3.7% |
    | Renouvellements automatiques/mois | 234,567 |

## 35. Alerte admin si taux expiration >10%

Étant donné que le taux d'expiration mensuel dépasse 10%

Quand le système détecte cette anomalie

Alors une alerte est envoyée:

## 36. Email rappel si pas de connexion WiFi depuis 20 jours

Étant donné que je n'ai pas connecté l'app en WiFi depuis 20 jours Et que j'ai 45 contenus téléchargés

Quand le système détecte cette inactivité WiFi

Alors je reçois un email:

## 37. Performance renouvellement avec 10 000 utilisateurs simultanés

Étant donné que 10 000 utilisateurs se connectent en WiFi simultanément

Quand chacun demande le renouvellement de 50 contenus

Alors le serveur traite 500 000 vérifications Et grâce au cache Redis et index PostgreSQL, le temps de réponse reste <3s Et les serveurs gèrent la charge sans problème

## 38. Logs audit renouvellements

Étant donné qu'un contenu est renouvelé

Quand l'opération se termine

Alors un log est enregistré:

    | timestamp | user_id | content_id | action | résultat |
    |---|---|---|---|---|
    | 2025-06-15 14:30:00 | abc123 | xyz789 | renew | success (+30d) |
    | 2025-06-15 14:30:01 | abc123 | def456 | renew | failed (deleted) |

Et ces logs aident à débugger les problèmes

# Modération préventive

22 scénarios

> Contexte commun à tous les scénarios

> Étant donné que le système de modération préventive est actif

## 1. Créateur nouvellement inscrit

Étant donné que je viens de créer un compte créateur Et que je n'ai jamais publié de contenu

Quand j'examine mon statut de créateur

Alors mon compte est marqué comme "Nouveau créateur" Et mes 3 premiers contenus devront être validés manuellement Et je suis informé de ce processus lors de l'onboarding

## 2. Publication du premier contenu par un nouveau créateur

Étant donné que je suis un nouveau créateur Et que je n'ai publié aucun contenu auparavant

Quand je publie mon premier contenu

Alors le contenu entre en file d'attente de validation manuelle Et le statut du contenu est "En attente de validation" Et le contenu n'est pas diffusé sur la plateforme Et je reçois une notification:

## 3. Validation manuelle par un modérateur

Étant donné que j'ai publié mon premier contenu Et que le contenu est en attente de validation

Quand un modérateur examine mon contenu

Alors le modérateur utilise la transcription automatique Whisper Et le modérateur vérifie:

    | critère | conforme |
    |---|---|
    | Respect des règles communauté | oui |
    | Pas de contenu inapproprié | oui |
    | Qualité audio acceptable | oui |
    | Métadonnées cohérentes | oui |
    | Tags appropriés | oui |

Et si tout est conforme, le contenu est validé

## 4. Délai de validation de 24-48h jours ouvrés

Étant donné que j'ai publié mon premier contenu lundi à 10:00

Quand le contenu entre en file de validation

Alors le contenu est validé avant mercredi 10:00 (48h jours ouvrés) Et dans la plupart des cas, la validation est effectuée sous 24h Et je reçois une notification dès que le contenu est validé

## 5. Notification de validation réussie

Étant donné que mon premier contenu a été validé par un modérateur

Quand la validation est approuvée

Alors je reçois une notification: Et le statut du contenu passe à "Publié" Et le contenu devient visible pour tous les utilisateurs Et il entre dans l'algorithme de recommandation

## 6. Refus de validation si contenu non conforme

Étant donné que mon premier contenu viole les règles de la communauté

Quand le modérateur examine le contenu

Alors le contenu est refusé Et je reçois une notification détaillée: Et le contenu reste en statut "Refusé" Et je peux modifier et republier

## 7. Les 3 premiers contenus sont validés manuellement

Étant donné que je suis un nouveau créateur

Quand je publie mes contenus

Alors les contenus suivants nécessitent une validation manuelle:

    | contenu | validation manuelle |
    |---|---|
    | 1er | oui |
    | 2ème | oui |
    | 3ème | oui |
    | 4ème | non (auto) |

Et après 3 contenus validés, mes futurs contenus sont publiés automatiquement

## 8. Passage en mode automatique après 3 validations

Étant donné que mes 3 premiers contenus ont été validés avec succès

Quand je publie mon 4ème contenu

Alors le contenu est publié automatiquement Et aucune validation manuelle n'est requise Et le statut passe directement à "Publié" Et je reçois une notification:

## 9. Évolution du score de confiance

Étant donné que je suis un créateur établi

Quand le système évalue mon historique

Alors un score de confiance est calculé basé sur:

    | critère | poids |
    |---|---|
    | Nombre de contenus publiés | 20% |
    | Strikes reçus | 40% |
    | Signalements infondés | 20% |
    | Ancienneté du compte | 10% |
    | Taux d'engagement positif | 10% |

Et le score évolue dynamiquement

## 10. Créateur fiable - Publication automatique

Étant donné que je suis un créateur Et que j'ai 0 strike depuis 6 mois Et que tous mes contenus précédents ont été conformes

Quand mon score de confiance est calculé

Alors je suis classé comme "Créateur fiable" Et tous mes nouveaux contenus sont publiés automatiquement Et aucune validation manuelle n'est nécessaire Et je bénéficie d'une publication instantanée

## 11. Créateur suspect - Validation manuelle systématique

Étant donné que je suis un créateur Et que j'ai reçu 2 strikes récents (< 3 mois)

Quand mon score de confiance est recalculé

Alors je suis classé comme "Créateur suspect" Et tous mes nouveaux contenus nécessitent une validation manuelle Et chaque contenu est examiné avant publication Et je suis notifié de ce changement de statut:

## 12. Réhabilitation après période sans incident

Étant donné que j'étais un "Créateur suspect" Et que je publie 10 contenus conformes sur 6 mois Et que je ne reçois aucun nouveau strike

Quand le système réévalue mon score de confiance

Alors je passe en "Créateur fiable" Et la publication automatique est rétablie Et je reçois une notification de réhabilitation:

## 13. Toute publicité nécessite validation manuelle

Étant donné qu'un annonceur soumet une publicité audio

Quand la publicité est créée

Alors elle entre automatiquement en file de validation manuelle Et aucune publicité n'est diffusée sans validation préalable Et cela est obligatoire pour des raisons de responsabilité juridique

## 14. Validation d'une publicité - Processus complet

Étant donné qu'une publicité est en attente de validation

Quand un modérateur senior examine la publicité

Alors le modérateur vérifie:

    | critère | conforme |
    |---|---|
    | Transcription automatique Whisper | effectuée |
    | Contenu conforme aux règles | oui |
    | Pas de fausse publicité / arnaque | oui |
    | Respect du ciblage géographique | oui |
    | Durée conforme (10-60s) | oui |
    | Volume audio acceptable (pas trop fort) | oui |
    | Métadonnées correctes | oui |

Et si tout est conforme, la publicité est validée

## 15. Délai de validation d'une publicité - 24-48h

Étant donné qu'un annonceur soumet une publicité lundi à 10:00

Quand la publicité entre en file de validation

Alors la publicité est validée avant mercredi 10:00 (48h jours ouvrés) Et l'annonceur est notifié dès la validation Et la campagne publicitaire peut alors démarrer

## 16. Refus de validation d'une publicité

Étant donné qu'une publicité contient des éléments non conformes

Quand le modérateur examine la publicité

Alors la publicité est refusée Et l'annonceur reçoit une notification détaillée: Et l'annonceur peut modifier et resoumettre la publicité Et aucun remboursement n'est effectué pour une publicité refusée

## 17. Économie de modération grâce à la prévention

Étant donné que la modération préventive est active

Quand on analyse l'efficacité du système

Alors 80% des contenus inappropriés sont détectés avant publication Et cela réduit le nombre de signalements de 70% Et les ressources de modération sont optimisées Et la qualité de la plateforme est préservée dès le début

## 18. Qualité de la plateforme maintenue

Étant donné que tous les nouveaux créateurs sont vérifiés

Quand on analyse la qualité globale des contenus

Alors le taux de contenus inappropriés est <1% Et les utilisateurs font confiance à la plateforme Et la réputation de RoadWave est préservée Et l'expérience utilisateur est optimale

## 19. Information claire sur le processus de validation

Étant donné que je suis un nouveau créateur

Quand je consulte la page d'aide "Validation des contenus"

Alors j'apprends que: Et le processus est clair et transparent

## 20. Badge "Créateur vérifié" après validation

Étant donné que mes 3 premiers contenus ont été validés avec succès

Quand je consulte mon profil créateur

Alors un badge discret "✓ Créateur vérifié" s'affiche Et ce badge rassure les auditeurs sur la qualité de mes contenus Et il améliore ma crédibilité sur la plateforme

## 21. Justification de la modération préventive

Étant donné que la modération préventive est en place

Quand on évalue les bénéfices

Alors les avantages suivants sont constatés:

    | bénéfice |
    |---|
    | Prévention meilleure que réaction |
    | Économie de ressources de modération (×3-5) |
    | Qualité de la plateforme préservée dès le début |
    | Confiance des utilisateurs renforcée |
    | Moins de contenus inappropriés signalés |
    | Réputation de la plateforme protégée |

Et l'investissement dans la prévention est rentable

## 22. Coût de la modération préventive

Étant donné que 100 nouveaux créateurs publient 3 contenus chacun Et que 50 publicités sont soumises par mois

Quand on calcule le coût de modération préventive

Alors le coût en temps modérateur est:

    | type | nombre | temps/contenu | total |
    |---|---|---|---|
    | Nouveaux créateurs | 300 | 5 min | 25h |
    | Publicités | 50 | 10 min | 8.3h |

Et le coût total est d'environ 33h de modération/mois Et c'est largement compensé par la réduction des signalements réactifs

# Sanctions et notifications de modération

27 scénarios

> Contexte commun à tous les scénarios

> Étant donné que je suis un créateur de contenu Et que j'ai publié un contenu

## 1. Notification multi-canal après sanction

Étant donné que mon contenu a été modéré

Quand la sanction est appliquée

Alors je reçois une notification sur 3 canaux:

    | canal | timing | contenu |
    |---|---|---|
    | Push | Immédiat | "Votre contenu a été modéré" |
    | In-app | Au prochain lancement | Popup détaillée avec bouton "Voir détails" |
    | Email | Dans l'heure | Notification complète avec lien d'appel |

Et chaque canal contient un lien vers les détails complets

## 2. Notification push immédiate

Étant donné que mon contenu vient d'être modéré

Quand la sanction est appliquée

Alors je reçois une notification push immédiate Et le message est court: "⚠️ Votre contenu a été modéré" Et je peux cliquer pour voir les détails Et la notification utilise Firebase Cloud Messaging (Android) ou APNs (iOS) Et le coût est de 0€

## 3. Popup in-app au prochain lancement

Étant donné que mon contenu a été modéré

Quand j'ouvre l'application

Alors une popup détaillée s'affiche automatiquement Et la popup contient:

    | élément | description |
    |---|---|
    | Titre du contenu | "Mon podcast #42" |
    | Icône d'avertissement | ⚠️ |
    | Catégorie violée | 🚫 Haine & violence |
    | Sanction | Strike 2/4 - Suspension 7 jours |
    | Bouton "Voir détails" | Redirige vers page détaillée |
    | Bouton "Compris" | Ferme la popup |

Et je ne peux pas fermer la popup sans l'avoir vue

## 4. Email de notification complet dans l'heure

Étant donné que mon contenu a été modéré à 14:00

Quand la sanction est appliquée

Alors je reçois un email avant 15:00 (dans l'heure) Et l'objet de l'email est "Modération de votre contenu \"[Titre du contenu]\"" Et l'email contient toutes les informations détaillées Et le coût est d'environ 0.001€ par email (Brevo, Resend)

## 5. Email de notification complet et structuré

Étant donné que mon contenu "Mon podcast #42" a été modéré

Quand je reçois l'email de notification

Alors l'email contient la structure suivante:

## 6. Page détaillée de la sanction in-app

Étant donné que je clique sur "Voir détails" dans la notification

Quand la page détaillée s'affiche

Alors je vois les 6 éléments obligatoires:

    | élément | contenu |
    |---|---|
    | 1. Catégorie violée | 🚫 Haine & violence (Article 3.2 CGU) |
    | 2. Raison détaillée | Explication claire et non juridique |
    | 3. Extrait audio | Timestamp exact: 3:42-4:15 |
    | 4. Transcription | Texte problématique surligné en rouge |
    | 5. Gravité | Strike actuel + conséquences (Strike 2/4, 7j susp) |
    | 6. Recours | Lien formulaire d'appel + délai 7j |

## 7. Affichage du passage problématique avec timestamp

Étant donné que la page détaillée de la sanction est affichée

Quand je consulte l'extrait audio concerné

Alors le timestamp exact est affiché: "3:42-4:15" Et je peux écouter uniquement cette portion de l'audio Et un player audio intégré permet l'écoute du passage Et la transcription correspondante est affichée en dessous Et les mots/phrases problématiques sont surlignés en rouge

## 8. Référence précise aux CGU

Étant donné que la sanction fait référence à l'Article 3.2 des CGU

Quand je clique sur "Article 3.2"

Alors je suis redirigé vers la section correspondante des CGU Et la section "Haine & violence" est mise en évidence Et je peux lire exactement ce qui est interdit Et cela m'aide à comprendre mon erreur

## 9. Gravité de la sanction avec système de strikes

Étant donné que c'est mon 2ème strike

Quand je consulte les détails de la sanction

Alors je vois clairement "Strike 2/4" Et les conséquences sont explicitées: Et je comprends l'escalade des sanctions

## 10. Accès au formulaire d'appel depuis la notification

Étant donné que j'ai reçu une notification de modération

Quand je clique sur "Contester cette décision"

Alors je suis redirigé vers le formulaire d'appel Et le formulaire est pré-rempli avec les informations de la sanction Et je peux commencer à rédiger mon appel

## 11. Accès au formulaire d'appel depuis "Mes sanctions"

Étant donné que j'ai reçu une sanction il y a 2 jours

Quand j'ouvre "Profil créateur > Mes sanctions"

Alors je vois la liste de mes sanctions Et chaque sanction a un bouton "Faire appel" (si délai <7j) Et je peux accéder au formulaire d'appel

## 12. Structure du formulaire d'appel

Étant donné que j'ouvre le formulaire d'appel

Quand le formulaire s'affiche

Alors je vois les champs suivants:

    | champ | type | obligatoire | description |
    |---|---|---|---|
    | Sanction contestée | Pré-rempli (readonly) | oui | "Strike 2 - Podcast #42" |
    | Raison de l'appel | Texte (50-1000 car) | oui | Explication courte de la contestation |
    | Arguments détaillés | Zone texte enrichie | oui | Arguments complets |
    | Preuves | Upload fichiers | non | Max 5 fichiers, 10 MB total |

Et tous les champs obligatoires sont marqués d'un astérisque

## 13. Validation du formulaire d'appel

Étant donné que je remplis le formulaire d'appel

Quand je clique sur "Soumettre l'appel"

Alors le système valide les champs obligatoires Et si un champ obligatoire est vide, une erreur s'affiche Et si la raison fait moins de 50 caractères, une erreur s'affiche Et si tout est valide, l'appel est soumis

## 14. Confirmation après soumission de l'appel

Étant donné que j'ai soumis un appel valide

Quand l'appel est enregistré

Alors un numéro de ticket unique est généré: "#MOD-2026-00142" Et un email de confirmation est envoyé: Et le statut de l'appel est "En cours d'examen" Et je peux suivre le statut dans "Mes sanctions"

## 15. Délai de soumission de 7 jours maximum

Étant donné que j'ai reçu une sanction le 2026-01-15

Quand j'essaie de faire appel le 2026-01-25 (10 jours plus tard)

Alors le formulaire d'appel est désactivé Et un message s'affiche: Et je ne peux plus contester la sanction

## 16. Bouton "Faire appel" visible si délai respecté

Étant donné que j'ai reçu une sanction il y a 3 jours

Quand je consulte "Mes sanctions"

Alors le bouton "Faire appel" est actif Et un compteur indique "4 jours restants pour faire appel" Et je peux cliquer pour soumettre un appel

## 17. SLA de 72h garanti pour appel standard

Étant donné que j'ai soumis un appel standard le lundi à 10:00

Quand l'appel est en cours de traitement

Alors un modérateur senior est assigné Et l'appel doit être traité avant jeudi 10:00 (72h - 3 jours ouvrés) Et je reçois une réponse dans ce délai

## 18. Appel complexe avec notification intermédiaire

Étant donné que j'ai soumis un appel complexe Et que le traitement nécessite plus de 72h

Quand 3 jours se sont écoulés

Alors je reçois un email de notification intermédiaire: Et l'appel est traité sous 5 jours ouvrés au total Et un modérateur senior + admin modération examinent le cas

## 19. Appel CRITIQUE traité en 24h

Étant donné que j'ai reçu une suspension longue ou un ban Et que je soumets un appel

Quand l'appel est classé en priorité CRITIQUE

Alors l'admin modération traite l'appel sous 24h Et je reçois une réponse rapide Et le cas est examiné en priorité absolue

## 20. Réponse finale détaillée - Appel accepté

Étant donné que mon appel est accepté

Quand je reçois la réponse finale

Alors l'email contient:

    | élément | contenu |
    |---|---|
    | Décision | Annulation de la sanction |
    | Justification | Explication de pourquoi l'appel est accepté |
    | Actions | Strike retiré, suspension annulée, contenu rétabli |
    | Définitif | "Cette décision est définitive" |

Et le strike est retiré de mon compte Et le contenu est rétabli sur la plateforme Et je peux continuer normalement

## 21. Réponse finale détaillée - Appel rejeté

Étant donné que mon appel est rejeté

Quand je reçois la réponse finale

Alors l'email contient:

    | élément | contenu |
    |---|---|
    | Décision | Maintien de la sanction |
    | Justification | Explication de pourquoi l'appel est rejeté |
    | Actions | Sanction maintenue, strike conservé |
    | Définitif | "Cette décision est définitive" |

Et la sanction reste active Et je ne peux pas faire de second appel Et je dois respecter la suspension

## 22. Réponse finale - Réduction de sanction

Étant donné que mon appel est partiellement accepté

Quand je reçois la réponse finale

Alors la décision est "Réduction de sanction" Et l'email explique: Et le strike est réduit Et la suspension est raccourcie Et je suis notifié de la nouvelle date de fin

## 23. Suivi du statut de l'appel in-app

Étant donné que j'ai soumis un appel

Quand je consulte "Mes sanctions"

Alors je vois le statut actuel de l'appel:

    | statut | badge | couleur |
    |---|---|---|
    | En cours d'examen | En cours 🔍 | orange |
    | Appel accepté | Accepté ✓ | vert |
    | Appel rejeté | Rejeté ✗ | rouge |
    | Sanction réduite | Partiellement accepté | bleu |

Et une notification badge m'alerte quand le statut change

## 24. Historique complet des sanctions visible

Étant donné que je suis un créateur

Quand j'ouvre "Profil créateur > Mes sanctions"

Alors je vois la liste complète de mes sanctions passées:

    | colonne | description |
    |---|---|
    | Date | 15/01/2026 |
    | Contenu | "Mon podcast #42" |
    | Catégorie | 🚫 Haine & violence |
    | Sanction | Strike 2 - Suspension 7j |
    | Statut | Active / Terminée / Annulée |
    | Appel | Aucun / Accepté / Rejeté |

Et les sanctions sont triées par date décroissante

## 25. Conformité DSA - Transparence obligatoire

Étant donné que le système de sanction est en place

Quand un audit DSA est effectué

Alors chaque sanction contient:

    | élément DSA | présent |
    |---|---|
    | Référence précise à la règle violée | oui |
    | Explication claire et compréhensible | oui |
    | Preuve (extrait + transcription) | oui |
    | Possibilité de recours (appel) | oui |
    | Délai de recours clairement indiqué | oui |
    | Réponse motivée au recours | oui |

Et le système est conforme au Digital Services Act

## 26. Décision définitive après premier appel

Étant donné que mon premier appel a été rejeté

Quand j'essaie de faire un second appel

Alors le bouton "Faire appel" est désactivé Et un message s'affiche: "Cette décision est définitive. Aucun second appel n'est possible." Et je ne peux plus contester la sanction Et je dois respecter la décision finale

## 27. Coût des notifications multi-canal

Étant donné que 100 sanctions sont appliquées en un mois

Quand on calcule le coût des notifications

Alors le coût total est d'environ 0.10€:

    | canal | coût unitaire | coût pour 100 |
    |---|---|---|
    | Email | 0.001€ | 0.10€ |
    | Push | 0€ | 0€ |
    | In-app | 0€ | 0€ |

Et le coût est négligeable même à grande échelle

# Signalement de contenu inapproprié

23 scénarios (22 standards, 1 plan)

> Contexte commun à tous les scénarios

> Étant donné que je suis un utilisateur connecté Et que je suis en train d'écouter un contenu

## 1. Affichage du formulaire de signalement

Étant donné que j'écoute un contenu inapproprié

Quand j'ouvre le menu du contenu Et que je clique sur "Signaler"

Alors un formulaire de signalement s'affiche Et le formulaire contient une liste déroulante "Catégorie du problème" Et le formulaire contient un champ texte "Commentaire (optionnel)" Et le formulaire contient un bouton "Envoyer le signalement"

## 2. Liste des 7 catégories prédéfinies

Étant donné que le formulaire de signalement est affiché

Quand je clique sur la liste déroulante "Catégorie du problème"

Alors je vois les 7 catégories suivantes:

    | icône | catégorie | description |
    |---|---|---|
    | 🚫 | Haine & violence | Incitation à la haine, discrimination, menaces |
    | 🔞 | Contenu sexuel | Pornographie, contenu explicite |
    | ⚖️ | Illégalité | Terrorisme, apologie de crimes |
    | 🎵 | Droits d'auteur | Musique/contenu protégé non autorisé |
    | 📧 | Spam | Publicité non sollicitée, répétition |
    | ❌ | Fausse information | Désinformation sur santé, sécurité routière |
    | 🔧 | Autre | Champ texte obligatoire si sélectionné |

Et chaque catégorie a une description claire

## 3. Sélection de la catégorie "Haine & violence"

Étant donné que le formulaire de signalement est affiché

Quand je sélectionne la catégorie "🚫 Haine & violence"

Alors la catégorie est sélectionnée Et la description "Incitation à la haine, discrimination, menaces" s'affiche Et je peux passer au champ commentaire

## 4. Catégorie "Autre" nécessite un commentaire obligatoire

Étant donné que le formulaire de signalement est affiché

Quand je sélectionne la catégorie "🔧 Autre"

Alors le champ "Commentaire" devient obligatoire Et un message s'affiche: "Veuillez décrire le problème (obligatoire)" Et le placeholder change en "Décrivez le problème rencontré" Et je ne peux pas envoyer le signalement sans commentaire

## 5. Champ commentaire optionnel avec incitation

Étant donné que le formulaire de signalement est affiché Et que j'ai sélectionné une catégorie autre que "Autre"

Quand je consulte le champ "Commentaire"

Alors le champ est optionnel (pas d'astérisque rouge) Et le placeholder indique "Décrivez le problème (optionnel mais recommandé)" Et la limite de caractères est de 500 Et un compteur affiche "0/500"

## 6. Envoi de signalement sans commentaire

Étant donné que j'ai sélectionné la catégorie "📧 Spam" Et que je n'ai pas rempli le champ commentaire

Quand je clique sur "Envoyer le signalement"

Alors le signalement est envoyé avec succès Et aucune erreur de validation ne s'affiche Et le commentaire est enregistré comme vide

## 7. Envoi de signalement avec commentaire

Étant donné que j'ai sélectionné la catégorie "🚫 Haine & violence" Et que j'ai saisi le commentaire "Le créateur tient des propos discriminatoires à 2:30"

Quand je clique sur "Envoyer le signalement"

Alors le signalement est envoyé avec succès Et le commentaire est enregistré avec le signalement Et il sera visible par les modérateurs

## 8. Limite de 500 caractères pour le commentaire

Étant donné que le formulaire de signalement est affiché

Quand je saisis un commentaire de 501 caractères

Alors le champ limite automatiquement à 500 caractères Et le compteur affiche "500/500" Et les caractères supplémentaires ne sont pas acceptés

## 9. Toast de confirmation après signalement

Étant donné que j'ai envoyé un signalement

Quand le signalement est enregistré

Alors un toast notification s'affiche Et le toast contient le message "✓ Signalement envoyé. Nous l'examinerons sous 24-48h." Et le toast s'affiche pendant 5 secondes Et le toast contient un bouton "Voir mes signalements" Et je peux fermer le toast manuellement avec un bouton X

## 10. Accès à l'historique des signalements via le toast

Étant donné que le toast de confirmation est affiché

Quand je clique sur "Voir mes signalements"

Alors je suis redirigé vers la page "Mes signalements" Et je vois la liste de tous mes signalements Et le signalement que je viens d'envoyer apparaît en premier

## 11. Historique personnel des signalements

Étant donné que j'ai envoyé 3 signalements précédemment

Quand j'ouvre "Profil > Mes signalements"

Alors je vois la liste de mes 3 signalements Et chaque signalement affiche:

    | information | description |
    |---|---|
    | Titre du contenu | "Podcast #42" |
    | Créateur | @pseudo_createur |
    | Catégorie | 🚫 Haine & violence |
    | Date | 15/01/2026 |
    | Statut | En cours / Traité / Rejeté |
    | Mon commentaire | Texte que j'ai saisi |

Et les signalements sont triés par date décroissante

## 12. 📋 Plan: Statuts possibles d'un signalement

Étant donné que j'ai envoyé un signalement

Quand le statut du signalement est ""

Alors le badge affiché est "" Et la couleur du badge est ""

📊 Exemples de données:

statut | badge | couleur
--- | --- | ---
En cours | En cours | orange
Traité | Traité ✓ | vert
Rejeté | Rejeté ✗ | rouge

## 13. Notification in-app si action prise

Étant donné que j'ai signalé un contenu il y a 24h

Quand le modérateur traite mon signalement Et que le contenu est effectivement retiré

Alors je reçois une notification in-app Et la notification indique "Votre signalement a été traité. Le contenu a été retiré." Et le statut de mon signalement passe à "Traité ✓" Et je peux voir les détails de l'action prise

## 14. Notification si signalement rejeté

Étant donné que j'ai signalé un contenu

Quand le modérateur rejette mon signalement

Alors je reçois une notification in-app Et la notification indique "Votre signalement a été examiné. Le contenu ne viole pas les règles de la communauté." Et le statut de mon signalement passe à "Rejeté ✗" Et je peux voir la raison du rejet

## 15. Un contenu peut être signalé plusieurs fois

Étant donné qu'un contenu a déjà été signalé par 5 autres utilisateurs

Quand je signale le même contenu

Alors mon signalement est enregistré indépendamment Et le compteur de signalements du contenu passe à 6 Et mon signalement rejoint la file d'attente de modération Et les signalements cumulés augmentent la priorité de traitement

## 16. Limite de signalements par utilisateur

Étant donné que j'ai déjà signalé le même contenu il y a 2 jours

Quand j'essaie de signaler à nouveau le même contenu

Alors un message m'informe "Vous avez déjà signalé ce contenu" Et le formulaire de signalement n'est pas affiché Et je peux consulter le statut de mon signalement précédent

## 17. Détection de signalements abusifs répétés

Étant donné que j'ai envoyé 10 signalements ce mois-ci Et que 8 d'entre eux ont été rejetés comme infondés

Quand j'essaie d'envoyer un nouveau signalement

Alors mon compte est marqué comme "signaleur suspect" Et un avertissement s'affiche: Et je peux toujours envoyer le signalement Mais mes futurs signalements auront une priorité réduite

## 18. Sanction pour signalements abusifs graves

Étant donné que j'ai envoyé 20 signalements abusifs en 1 mois Et que tous ont été rejetés comme volontairement faux

Quand le modérateur détecte le pattern abusif

Alors mon compte reçoit un avertissement formel Et je perds la possibilité de signaler pendant 30 jours Et je reçois un email m'expliquant la sanction

## 19. Signalement depuis le player audio

Étant donné que j'écoute un contenu

Quand j'ouvre le menu "⋮" du player

Alors je vois l'option "Signaler" Et je peux ouvrir le formulaire de signalement

## 20. Signalement depuis la page de détails du contenu

Étant donné que je consulte la page de détails d'un contenu

Quand je clique sur le bouton "⋮" en haut à droite

Alors je vois l'option "Signaler" Et je peux ouvrir le formulaire de signalement

## 21. Signalement depuis l'historique d'écoute

Étant donné que je consulte mon historique d'écoute

Quand je clique sur "⋮" à côté d'un contenu passé

Alors je vois l'option "Signaler" Et je peux signaler ce contenu même si je ne l'écoute plus actuellement

## 22. Identité du signaleur anonyme pour le créateur

Étant donné que j'ai signalé un contenu

Quand le créateur est notifié de la modération

Alors mon identité reste anonyme Et le créateur ne peut pas savoir qui a signalé Et seuls les modérateurs ont accès à l'identité du signaleur

## 23. Coût du système de signalement

Étant donné que le système de signalement est en place

Quand on calcule le coût

Alors le coût est de 0€ Et le formulaire est développé en interne Et aucun service tiers n'est utilisé Et les notifications in-app sont gratuites

# Traitement des signalements par l'IA et les modérateurs

25 scénarios (21 standards, 4 plans)

> Contexte commun à tous les scénarios

> Étant donné que le système de modération est actif

## 1. Signalement ajouté à la file d'attente asynchrone

Étant donné qu'un utilisateur envoie un signalement pour un contenu audio

Quand le signalement est reçu

Alors le signalement est ajouté à la file d'attente asynchrone Et un worker de traitement est déclenché Et le traitement se fait en arrière-plan sans bloquer l'utilisateur

## 2. Transcription automatique avec Whisper large-v3

Étant donné qu'un contenu audio signalé dure 5 minutes

Quand le worker de traitement démarre

Alors le système utilise Whisper large-v3 pour transcrire l'audio Et la transcription est en self-hosted (pas de service cloud) Et le texte transcrit est enregistré en base de données Et le délai de transcription est de 1-3 minutes

## 3. 📋 Plan: Délai de transcription selon durée audio

Étant donné qu'un contenu audio signalé dure minutes

Quand le système transcrit l'audio

Alors la transcription prend environ

📊 Exemples de données:

duree | delai
--- | ---
2 | 1-3 minutes
10 | 3-10 minutes
45 | 10-20 minutes

## 4. Analyse automatique du contenu transcrit

Étant donné que la transcription audio est terminée

Quand le système analyse le texte transcrit

Alors les analyses suivantes sont effectuées:

    | analyse | technologie |
    |---|---|
    | Analyse de sentiment | distilbert-base-uncased |
    | Détection de haine | facebook/roberta-hate-speech |
    | Mots-clés interdits | Liste noire FR/EN + regex |

Et chaque analyse génère un score de confiance (0-100%)

## 5. Génération du score de confiance IA

Étant donné que toutes les analyses sont terminées

Quand le système calcule le score final

Alors un score de confiance IA entre 0-100% est généré Et le score indique la probabilité que le contenu viole les règles Et la catégorie la plus probable est identifiée Et les timestamps des passages problématiques sont extraits

## 6. Détection automatique de contenu clairement inapproprié

Étant donné qu'un contenu contient des insultes graves et répétées

Quand l'IA analyse la transcription

Alors le score de confiance IA est >95% Et la catégorie détectée est "Haine & violence" Et les passages problématiques sont identifiés avec timestamps:

    | timestamp | texte problématique |
    |---|---|
    | 02:15 | [insulte discriminatoire] |
    | 03:42 | [propos haineux] |

Et le signalement est classé en priorité CRITIQUE

## 7. 📋 Plan: SLA selon priorité du signalement

Étant donné qu'un signalement a une priorité ""

Quand le signalement entre en file d'attente

Alors le délai de traitement cible est "" Et le responsable du traitement est ""

📊 Exemples de données:

priorite | delai | responsable
--- | --- | ---
CRITIQUE | <2h (24/7) | Modérateur senior (astreinte)
HAUTE | <24h (jours ouvrés) | Modérateur junior/senior
MOYENNE | <24h (jours ouvrés) | Modérateur junior
BASSE | <72h (jours ouvrés) | Modérateur junior

## 8. Traitement automatique pour score IA >95%

Étant donné qu'un signalement a un score IA de 97% Et que la catégorie détectée est "Spam" (évidente)

Quand le système évalue le signalement

Alors une action automatique immédiate est déclenchée Et le contenu est retiré automatiquement Et le créateur est notifié de la modération Et le créateur peut faire appel de la décision Et un modérateur senior vérifie l'action a posteriori

## 9. Signalement CRITIQUE traité en moins de 2h

Étant donné qu'un signalement de priorité CRITIQUE est reçu à 14:00 Et que le contenu concerne une menace de violence

Quand le signalement est assigné à un modérateur senior d'astreinte

Alors le modérateur est alerté immédiatement (push + SMS) Et le signalement est traité avant 16:00 (2h) Et une décision est prise et appliquée Et les autorités peuvent être contactées si nécessaire

## 10. Astreinte modérateur 24/7 pour signalements CRITIQUES

Étant donné qu'un signalement CRITIQUE est reçu un dimanche à 03:00

Quand le signalement est classé en priorité CRITIQUE

Alors le modérateur senior d'astreinte est alerté Et le signalement est traité dans les 2h (avant 05:00) Et le service d'astreinte garantit une disponibilité 24/7

## 11. Signalement HAUTE priorité traité en moins de 24h

Étant donné qu'un signalement de priorité HAUTE est reçu lundi à 10:00 Et que le contenu concerne du harcèlement

Quand le signalement entre en file d'attente

Alors le signalement est assigné à un modérateur (junior ou senior) Et le signalement est traité avant mardi 10:00 (24h jours ouvrés) Et une décision est prise et appliquée

## 12. Signalement BASSE priorité traité en moins de 72h

Étant donné qu'un signalement de priorité BASSE est reçu lundi à 10:00 Et que le contenu concerne des tags incorrects

Quand le signalement entre en file d'attente

Alors le signalement est traité avant jeudi 10:00 (72h jours ouvrés) Et un modérateur junior peut traiter ce type de signalement

## 13. Calcul du score de priorité

Étant donné qu'un signalement a les caractéristiques suivantes:

    | caractéristique | valeur |
    |---|---|
    | Score IA | 85% |
    | Signalements cumulés | 3 |
    | Fiabilité du signaleur | 75% |

Quand le système calcule la priorité

Alors la formule appliquée est: Et le score de priorité est: (85 × 0.7) + (3 × 0.2) + (75 × 0.1) = 67.5 Et le signalement est classé en priorité MOYENNE

## 14. 📋 Plan: Classification selon score de priorité

Étant donné qu'un signalement a un score de priorité de

Quand le système classe le signalement

Alors la priorité assignée est "" Et le signalement entre dans la file ""

📊 Exemples de données:

score | priorite | file
--- | --- | ---
95 | CRITIQUE | Immédiate
82 | HAUTE | Prioritaire
55 | MOYENNE | Normale
25 | BASSE | Différée

## 15. Boost de priorité avec signalements cumulés

Étant donné qu'un contenu a été signalé par 1 utilisateur avec un score IA de 60% Et que le signalement est classé en priorité MOYENNE (score 42)

Quand 5 autres utilisateurs signalent le même contenu

Alors le nombre de signalements cumulés passe à 6 Et le score de priorité augmente significativement Et le signalement peut passer en priorité HAUTE Et le traitement est accéléré

## 16. Impact de la fiabilité du signaleur

Étant donné qu'un utilisateur de confiance (90% fiabilité) envoie un signalement Et qu'un utilisateur suspect (20% fiabilité) envoie un signalement similaire

Quand le système calcule les priorités

Alors le signalement de l'utilisateur de confiance a un score plus élevé Et son signalement est traité en priorité Et le signalement de l'utilisateur suspect est traité plus tard

## 17. Évolution du score de fiabilité du signaleur

Étant donné qu'un utilisateur a envoyé 10 signalements Et que 8 d'entre eux ont été acceptés par les modérateurs

Quand le système calcule son score de fiabilité

Alors le score est de 80% (8 acceptés / 10 total) Et ses futurs signalements auront plus de poids Et il peut devenir "utilisateur de confiance"

## 18. Files d'attente séparées par priorité

Étant donné que 50 signalements sont en attente

Quand le système organise la file d'attente

Alors les signalements sont répartis dans les files suivantes:

    | file | nombre | priorité |
    |---|---|---|
    | Immédiate (24/7) | 5 | CRITIQUE |
    | Prioritaire | 15 | HAUTE |
    | Normale | 20 | MOYENNE |
    | Différée | 10 | BASSE |

Et les modérateurs traitent en priorité la file Immédiate

## 19. Modérateurs assignés selon compétences

Étant donné qu'un signalement complexe de harcèlement est reçu

Quand le système assigne un modérateur

Alors un modérateur senior est prioritairement assigné Et les modérateurs juniors peuvent traiter les cas simples (spam, tags) Et les modérateurs seniors traitent les cas complexes (haine, violence, appels)

## 20. Stack technique 100% opensource

Étant donné que le système de modération IA est déployé

Quand on analyse les technologies utilisées

Alors toutes les technologies sont opensource:

    | composant | technologie | hébergement |
    |---|---|---|
    | Transcription | Whisper large-v3 | Self-hosted |
    | Analyse sentiment | distilbert-base-uncased | Self-hosted |
    | Détection haine | facebook/roberta-hate-speech | Self-hosted |
    | Mots-clés interdits | Liste noire FR/EN + regex | PostgreSQL |

Et aucune dépendance à Google, AWS, Azure

## 21. 📋 Plan: Coût selon phase du projet

Étant donné que RoadWave est en phase ""

Quand on calcule le coût de l'infrastructure IA

Alors le coût mensuel est ""

📊 Exemples de données:

phase | cout
--- | ---
MVP | 0-50€ (CPU)
Scale | 50-200€ (GPU VPS)

## 22. Processing asynchrone en MVP avec CPU

Étant donné que RoadWave est en phase MVP Et que le volume est <1000 signalements/mois

Quand le système traite les signalements

Alors un serveur CPU standard est suffisant Et le coût est de 0€ (serveur existant) Et le processing asynchrone absorbe les pics de charge Et les délais restent acceptables (1-20 minutes)

## 23. Scaling avec GPU pour gros volumes

Étant donné que RoadWave reçoit >1000 signalements/jour

Quand le système nécessite un scaling

Alors un VPS avec GPU est requis Et le coût passe à 50-200€/mois Et les délais de transcription sont divisés par 5-10 Et le système peut gérer 10 000+ signalements/mois

## 24. Logs d'audit pour chaque traitement

Étant donné qu'un signalement est traité

Quand une action est prise (rejet, acceptation, sanction)

Alors un log d'audit complet est créé:

    | champ | description |
    |---|---|
    | signalement_id | ID unique du signalement |
    | content_id | ID du contenu signalé |
    | ia_score | Score de confiance IA |
    | ia_category | Catégorie détectée par IA |
    | priority | CRITIQUE / HAUTE / MOYENNE / BASSE |
    | moderator_id | ID du modérateur assigné |
    | action_taken | Retiré / Rejeté / Strike |
    | processing_time | Durée du traitement |
    | timestamp | Date et heure de la décision |

Et le log est conservé pour conformité DSA Et les logs sont anonymisés après 3 ans (RGPD)

## 25. Traçabilité complète pour conformité DSA

Étant donné que le système de modération est actif

Quand un audit DSA est effectué

Alors toutes les actions de modération sont tracées Et les délais de traitement sont mesurés et respectés Et les décisions sont justifiées et documentées Et la transparence vis-à-vis des utilisateurs est garantie Et le système est conforme au Digital Services Act

# Conditions d'activation de la monétisation

> En tant que créateur Je veux pouvoir activer la monétisation quand je remplis les critères Afin de générer des revenus avec mes contenus

28 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et que je suis connecté en tant que créateur

## 1. Critère 1 - Ancienneté de 3 mois validée

Étant donné que mon compte a été créé il y a 91 jours

Quand je consulte les critères de monétisation

Alors le critère "Ancienneté ≥ 3 mois" est validé ✅

## 2. Critère 1 - Ancienneté insuffisante

Étant donné que mon compte a été créé il y a 60 jours

Quand je consulte les critères de monétisation

Alors le critère "Ancienneté ≥ 3 mois" n'est pas validé ❌ Et je vois "Encore 30 jours avant d'être éligible"

## 3. Critère 2 - 500 abonnés atteints

Étant donné que j'ai exactement 500 abonnés

Quand je consulte les critères de monétisation

Alors le critère "≥ 500 abonnés" est validé ✅

## 4. Critère 2 - Pas assez d'abonnés

Étant donné que j'ai 347 abonnés

Quand je consulte les critères de monétisation

Alors le critère "≥ 500 abonnés" n'est pas validé ❌ Et je vois "Encore 153 abonnés nécessaires"

## 5. Critère 3 - 10 000 écoutes complètes atteintes

Étant donné que mes contenus ont cumulé 10 487 écoutes complètes

Quand je consulte les critères de monétisation

Alors le critère "≥ 10 000 écoutes complètes" est validé ✅

## 6. Critère 3 - Écoutes incomplètes non comptabilisées

Étant donné que mes contenus ont:

    | type écoute | nombre |
    |---|---|
    | Écoutes complètes | 8 500 |
    | Écoutes <80% | 3 000 |

Quand je consulte les critères de monétisation

Alors seules les 8 500 écoutes complètes comptent Et je vois "Encore 1 500 écoutes complètes nécessaires"

## 7. Critère 4 - Aucun strike actif

Étant donné que je n'ai aucun strike actif Et que je n'ai eu aucun contenu modéré dans les 6 derniers mois

Quand je consulte les critères de monétisation

Alors le critère "Fiabilité" est validé ✅

## 8. Critère 4 - Strike actif bloque l'éligibilité

Étant donné que j'ai 1 strike actif pour contenu inapproprié

Quand je consulte les critères de monétisation

Alors le critère "Fiabilité" n'est pas validé ❌ Et je vois "Vous devez résoudre votre strike avant d'être éligible"

## 9. Critère 4 - Contenu modéré dans les 6 derniers mois

Étant donné que je n'ai pas de strike actif Mais qu'un de mes contenus a été modéré il y a 4 mois

Quand je consulte les critères de monétisation

Alors le critère "Fiabilité" n'est pas validé ❌ Et je vois "Attendre 2 mois après le dernier contenu modéré"

## 10. Critère 5 - 5 contenus publiés dans les 90 derniers jours

Étant donné que j'ai publié:

    | date de publication | titre |
    |---|---|
    | Il y a 15 jours | Contenu 1 |
    | Il y a 30 jours | Contenu 2 |
    | Il y a 45 jours | Contenu 3 |
    | Il y a 60 jours | Contenu 4 |
    | Il y a 75 jours | Contenu 5 |

Quand je consulte les critères de monétisation

Alors le critère "≥ 5 contenus publiés dans les 90 derniers jours" est validé ✅

## 11. Critère 5 - Contenus trop anciens ne comptent pas

Étant donné que j'ai publié:

    | date de publication | titre |
    |---|---|
    | Il y a 15 jours | Contenu 1 |
    | Il y a 30 jours | Contenu 2 |
    | Il y a 95 jours | Contenu 3 |
    | Il y a 120 jours | Contenu 4 |

Quand je consulte les critères de monétisation

Alors seuls 2 contenus comptent (dans les 90 jours) Et je vois "Encore 3 contenus à publier dans les 90 prochains jours"

## 12. Tous les critères validés - Bouton disponible

Étant donné que tous mes critères sont validés:

    | critère | statut |
    |---|---|
    | Ancienneté ≥ 3 mois | ✅ |
    | ≥ 500 abonnés | ✅ |
    | ≥ 10 000 écoutes | ✅ |
    | Fiabilité | ✅ |
    | Régularité (5 contenus) | ✅ |

Quand j'accède à mon profil créateur

Alors le bouton "Demander la monétisation" est actif Et je peux cliquer pour démarrer le KYC

## 13. Critères incomplets - Bouton grisé avec progression

Étant donné que mes critères sont:

    | critère | statut | progression |
    |---|---|---|
    | Ancienneté ≥ 3 mois | ✅ | 100% |
    | ≥ 500 abonnés | ❌ | 347/500 (69%) |
    | ≥ 10 000 écoutes | ❌ | 8500/10000 (85%) |
    | Fiabilité | ✅ | 100% |
    | Régularité (5 contenus) | ✅ | 100% |

Quand j'accède à mon profil créateur

Alors le bouton "Demander la monétisation" est grisé Et je vois la progression détaillée de chaque critère

## 14. Vérification automatique SQL lors de la demande

Étant donné que je clique sur "Demander la monétisation"

Quand le système vérifie mes critères

Alors une requête SQL est exécutée: Et si tous les critères sont TRUE, je suis redirigé vers le KYC

## 15. Notification par email quand critères atteints

Étant donné que je viens d'atteindre 500 abonnés Et que c'était mon dernier critère manquant

Quand le système détecte l'éligibilité

Alors je reçois un email:

## 16. Badge "Éligible monétisation" dans profil

Étant donné que je remplis tous les critères Mais que je n'ai pas encore activé la monétisation

Quand un utilisateur consulte mon profil

Alors il voit un badge "Éligible monétisation 💰" Et cela renforce ma crédibilité de créateur

## 17. Justification anti-fraude - Délai 3 mois

Étant donné qu'un compte suspect crée du contenu frauduleux

Quand le compte est détecté dans les 2 premiers mois

Alors le compte est banni avant d'atteindre les 3 mois Et le créateur n'a jamais été éligible à la monétisation Et aucun paiement n'a été effectué

## 18. Justification qualité - 10 000 écoutes

Étant donné qu'un créateur produit du contenu de mauvaise qualité

Quand ses contenus ne génèrent que 2 000 écoutes complètes

Alors il ne peut pas activer la monétisation Et seuls les créateurs avec contenu apprécié sont monétisés

## 19. Réduction coût administratif plateforme

Étant donné que RoadWave a 10 000 créateurs inscrits Et que seuls 500 remplissent tous les critères

Quand le système calcule le coût administratif

Alors seulement 500 KYC sont à gérer (vs 10 000) Et seulement 500 virements mensuels (vs 10 000) Et la charge comptable est réduite de 95%

## 20. Statistiques publiques pour transparence

Quand un utilisateur consulte la page "Devenir créateur"

Alors il voit les statistiques:

    | métrique | valeur exemple |
    |---|---|
    | Nombre créateurs monétisés | 1 247 |
    | Revenus moyens par créateur | 127€/mois |
    | Top créateur (anonymisé) | 2 450€/mois |
    | Critères d'éligibilité à remplir | 5 critères |

Et cela permet de fixer des attentes réalistes

## 21. Cache Redis pour calcul rapide critères

Étant donné que je consulte mes critères de monétisation

Quand le système charge la page

Alors les compteurs sont récupérés depuis Redis:

    | clé Redis | exemple valeur |
    |---|---|
    | creator:[id]:subscribers_count | 347 |
    | creator:[id]:complete_listens_total | 8500 |
    | creator:[id]:recent_contents_count | 7 |

Et le temps de réponse est <50ms

## 22. Mise à jour temps réel des compteurs

Étant donné que je viens de publier un nouveau contenu

Quand un utilisateur écoute ce contenu en entier

Alors le compteur "complete_listens_total" est incrémenté immédiatement Et si je rafraîchis la page critères, je vois la nouvelle valeur Et cela encourage les créateurs à continuer de produire

## 23. Historique des tentatives d'activation

Étant donné que j'ai tenté d'activer la monétisation il y a 2 mois Mais que les critères n'étaient pas remplis

Quand j'accède à mes logs d'activité

Alors je vois:

    | date | action | résultat | raison |
    |---|---|---|---|
    | 2025-11-15 | Demande monétisation | Refusée | Seulement 300 abonnés |

Et cela m'aide à suivre ma progression

## 24. Performance avec 100 000 créateurs

Étant donné que RoadWave a 100 000 créateurs Et que chacun consulte ses critères 1 fois par jour

Quand le système traite ces requêtes

Alors la table users est indexée sur created_at Et la table subscriptions est indexée sur creator_id Et la table contents est indexée sur creator_id et published_at Et chaque requête reste <50ms grâce aux index

## 25. Export des critères pour support client

Étant donné que je contacte le support car je pense être éligible

Quand l'agent support consulte mon compte

Alors il voit un export JSON complet: Et l'agent peut expliquer précisément pourquoi je ne suis pas éligible

## 26. Notification 30 jours avant éligibilité probable

Étant donné que mes critères sont:

    | critère | statut | progression |
    |---|---|---|
    | Ancienneté ≥ 3 mois | ❌ | 60/90 jours |
    | Tous les autres critères | ✅ | 100% |

Quand il reste exactement 30 jours avant les 90 jours

Alors je reçois une notification:

## 27. Pas de bypass possible pour amis/influenceurs

Étant donné qu'un créateur influent me contacte directement Et qu'il demande un bypass des critères

Quand je consulte la politique RoadWave

Alors la réponse est "Aucune exception possible, critères automatiques uniquement" Et cela garantit l'équité pour tous les créateurs

## 28. A/B test futur sur seuils (post-MVP)

Étant donné que RoadWave veut tester des seuils différents

Quand un A/B test est lancé en 2027

Alors groupe A voit: 500 abonnés, 10 000 écoutes Et groupe B voit: 300 abonnés, 5 000 écoutes Et les métriques (taux activation, fraude, qualité) sont comparées Et le meilleur seuil est déployé définitivement

# Contenus Premium exclusifs

> En tant que créateur monétisé Je veux pouvoir rendre certains contenus exclusifs aux abonnés Premium Afin d'inciter les utilisateurs à s'abonner

34 scénarios

> Contexte commun à tous les scénarios

> Étant donné que je suis un créateur avec la monétisation activée

## 1. Toggle "Réservé Premium" lors de la création

Étant donné que je crée un nouveau contenu

Quand j'accède aux options de publication

Alors je vois un toggle "Réservé aux abonnés Premium 👑" Et je peux l'activer ou le désactiver

## 2. Contenu marqué Premium lors de la création

Étant donné que je crée un nouveau contenu

Quand j'active le toggle "Réservé Premium" Et que je publie le contenu

Alors le champ `is_premium` en base est mis à `true` Et le contenu est visible uniquement pour les utilisateurs Premium

## 3. Contenu gratuit par défaut

Étant donné que je crée un nouveau contenu

Quand je ne touche pas au toggle "Réservé Premium" Et que je publie le contenu

Alors le champ `is_premium` en base est mis à `false` (défaut) Et le contenu est accessible à tous les utilisateurs

## 4. Modification d'un contenu existant en Premium

Étant donné que j'ai publié un contenu gratuit il y a 2 jours

Quand je modifie le contenu et active le toggle "Réservé Premium" Et que j'enregistre les modifications

Alors le contenu devient immédiatement Premium Et les utilisateurs gratuits ne peuvent plus y accéder

## 5. Passage d'un contenu Premium en gratuit

Étant donné que j'ai publié un contenu Premium il y a 1 mois

Quand je modifie le contenu et désactive le toggle "Réservé Premium" Et que j'enregistre les modifications

Alors le contenu devient immédiatement gratuit Et tous les utilisateurs peuvent maintenant y accéder

## 6. Aucune limite sur pourcentage de contenus Premium

Étant donné que je publie 10 nouveaux contenus

Quand je décide de rendre les 10 contenus Premium (100%)

Alors le système accepte sans limitation Et je peux avoir 100% de mon catalogue en Premium

## 7. Stratégie freemium - Mix gratuit/premium

Étant donné que je publie 10 nouveaux contenus

Quand je décide de rendre 5 contenus Premium et 5 gratuits (50/50)

Alors le système accepte cette stratégie Et je peux tester différents mix pour optimiser mes revenus

## 8. Stratégie tout gratuit possible

Étant donné que je suis monétisé via publicités

Quand je décide de ne mettre aucun contenu en Premium (0%)

Alors le système accepte cette stratégie Et je génère des revenus uniquement via les publicités

## 9. Badge 👑 visible sur l'interface utilisateur

Étant donné qu'un utilisateur consulte ma liste de contenus

Quand il voit un contenu Premium

Alors un badge 👑 "Premium" est affiché Et le contenu est clairement identifiable comme réservé

## 10. Utilisateur gratuit voit les contenus Premium dans la liste

Étant donné que je suis un utilisateur gratuit

Quand je consulte les contenus d'un créateur

Alors je vois aussi les contenus Premium dans la liste Et ils sont affichés avec un badge 👑 Mais je ne peux pas les lire

## 11. Tentative de lecture Premium par utilisateur gratuit - Overlay bloquant

Étant donné que je suis un utilisateur gratuit

Quand je clique sur un contenu Premium pour le lire

Alors un overlay bloquant apparaît Et je vois le message: Et un bouton "Passer Premium" est affiché

## 12. CTA "Passer Premium" redirige vers abonnement

Étant donné que je vois l'overlay de contenu Premium bloqué

Quand je clique sur "Passer Premium"

Alors je suis redirigé vers la page d'abonnement Premium Et je peux m'abonner pour 4.99€/mois

## 13. Utilisateur Premium peut lire tous les contenus Premium

Étant donné que je suis un utilisateur Premium actif

Quand je clique sur un contenu Premium

Alors le contenu se lance immédiatement Et je n'ai aucun overlay bloquant Et je peux profiter pleinement du contenu exclusif

## 14. Contenus Premium inclus dans les recommandations

Étant donné que l'algorithme génère ma file de 5 contenus

Quand je suis un utilisateur gratuit

Alors les contenus Premium peuvent apparaître dans les recommandations Et cela me fait découvrir qu'il existe du contenu exclusif

## 15. Contenu Premium skippé automatiquement pour utilisateur gratuit

Étant donné que je suis un utilisateur gratuit Et qu'un contenu Premium apparaît dans ma file de recommandation

Quand j'écoute le contenu précédent jusqu'à la fin

Alors le contenu Premium est automatiquement skippé Et le contenu suivant (gratuit) est lancé Et le slot Premium ne compte pas dans ma file de 5 contenus

## 16. Contenu Premium diffusé normalement pour utilisateur Premium

Étant donné que je suis un utilisateur Premium Et qu'un contenu Premium apparaît dans ma file de recommandation

Quand j'écoute le contenu précédent jusqu'à la fin

Alors le contenu Premium est lancé normalement Et je profite du contenu exclusif sans interruption

## 17. Champ `is_premium` boolean en base PostgreSQL

Étant donné qu'un contenu est créé

Quand il est stocké en base de données

Alors la table `contents` contient un champ `is_premium BOOLEAN DEFAULT FALSE` Et ce champ est indexé pour requêtes rapides

## 18. Index PostgreSQL sur `is_premium`

Étant donné que l'algorithme doit filtrer les contenus selon le statut Premium

Quand une requête SQL est exécutée:

Alors l'index sur `is_premium` accélère la requête Et le temps de réponse reste <20ms

## 19. Cache Redis pour statut Premium

Étant donné qu'un contenu Premium est consulté fréquemment

Quand l'API vérifie le statut Premium

Alors la valeur est récupérée depuis Redis: Et le cache a un TTL de 1 heure Et cela évite des requêtes SQL inutiles

## 20. Invalidation cache lors de modification statut Premium

Étant donné qu'un contenu est passé de gratuit à Premium

Quand le créateur enregistre la modification

Alors le cache Redis `content:[id]:premium` est invalidé immédiatement Et la nouvelle valeur est mise à jour Et les utilisateurs voient le changement en temps réel

## 21. Justification liberté créateur - Stratégie personnalisée

Étant donné que chaque créateur a une audience différente

Quand un créateur décide de sa stratégie Premium

Alors il peut tester différentes approches:

    | stratégie | % Premium | objectif |
    |---|---|---|
    | Tout gratuit | 0% | Maximiser audience + revenus pub |
    | Mix 50/50 | 50% | Équilibrer audience et exclusivité |
    | Premium majoritaire | 80% | Cibler abonnés fidèles |
    | 100% Premium | 100% | Contenu ultra-exclusif |

## 22. Justification incitation Premium - Argument fort pour s'abonner

Étant donné qu'un utilisateur gratuit voit beaucoup de contenus Premium

Quand il consulte les profils de ses créateurs préférés

Alors il voit que 60% de leur contenu est réservé Premium Et cela l'incite à s'abonner pour 4.99€/mois Et RoadWave augmente son taux de conversion vers Premium

## 23. Justification équité - Petit créateur peut tout mettre en Premium

Étant donné que je suis un petit créateur avec 600 abonnés Et que 50 sont abonnés Premium

Quand je mets 100% de mon contenu en Premium

Alors je génère des revenus uniquement via mes 50 abonnés Premium Et cela me permet de vivre de mon contenu malgré une petite audience

## 24. Justification équité - Gros créateur peut tout offrir gratuitement

Étant donné que je suis un gros créateur avec 50 000 abonnés Et que je génère déjà beaucoup de revenus publicitaires

Quand je laisse 100% de mon contenu gratuit

Alors je maximise mon audience et mes revenus pub Et je n'ai pas besoin de mettre du contenu en Premium

## 25. Statistiques créateur - Ratio Premium/Gratuit

Étant donné que j'accède à mon tableau de bord créateur

Quand je consulte mes statistiques de contenus

Alors je vois:

    | métrique | valeur |
    |---|---|
    | Contenus totaux | 47 |
    | Contenus gratuits | 32 (68%) |
    | Contenus Premium | 15 (32%) |
    | Écoutes Premium ce mois | 12,345 |
    | Écoutes gratuites ce mois | 28,901 |

## 26. Statistiques créateur - Revenus par type

Étant donné que j'ai des contenus gratuits et Premium

Quand je consulte mes revenus détaillés

Alors je vois:

    | source | montant |
    |---|---|
    | Revenus pub (gratuit) | 86.70€ |
    | Revenus Premium (exclusifs) | 34.20€ |
    | Revenus Premium (tout contenu) | 78.90€ |

Et je peux comparer l'efficacité de chaque stratégie

## 27. Notification créateur - Contenu Premium très écouté

Étant donné que j'ai publié un contenu Premium il y a 3 jours Et qu'il a généré 5 000 écoutes Premium (très élevé)

Quand le système détecte cette performance

Alors je reçois une notification:

## 28. A/B test utilisateur - Impact badge Premium sur conversion

Étant donné que RoadWave veut optimiser le taux de conversion Premium

Quand un A/B test est lancé

Alors groupe A voit le badge 👑 "Premium" Et groupe B voit le badge 💎 "Exclusif" Et les taux de clic et conversion sont mesurés Et le badge le plus performant est déployé définitivement

## 29. Analytics plateforme - Adoption fonctionnalité Premium

Étant donné que RoadWave suit l'adoption de la fonctionnalité

Quand un admin consulte les métriques

Alors il voit:

    | métrique | valeur |
    |---|---|
    | Créateurs utilisant Premium | 847 (68%) |
    | % moyen contenus Premium | 23% |
    | Taux conversion vers Premium (users) | 8.5% |
    | Revenus Premium/mois | 47,890€ |

## 30. Impact sur churn - Contenus Premium réduisent le churn Premium

Étant donné qu'un utilisateur Premium envisage de résilier Mais qu'il a accès à 150 contenus Premium de ses créateurs préférés

Quand il voit la valeur exclusive qu'il perdrait

Alors il est moins susceptible de résilier (churn réduit de ~30%) Et les contenus Premium augmentent la rétention

## 31. Transparence - Créateur voit combien de contenus Premium il a

Étant donné que j'accède à mon profil créateur

Quand je consulte mes contenus

Alors je peux filtrer par statut:

    | filtre | résultats |
    |---|---|
    | Tous | 47 |
    | Gratuits | 32 |
    | Premium 👑 | 15 |

Et je peux facilement gérer mon catalogue

## 32. Export liste contenus avec statut Premium (RGPD)

Étant donné que je demande l'export de mes données

Quand l'export est généré

Alors la liste de mes contenus inclut le statut Premium:

## 33. Suppression compte créateur et contenus Premium

Étant donné que je supprime définitivement mon compte créateur

Quand la suppression est confirmée

Alors tous mes contenus (gratuits et Premium) sont supprimés Et les utilisateurs Premium ne peuvent plus y accéder Et les fichiers audio sont supprimés du CDN sous 7 jours

## 34. Performance avec 1 million de contenus Premium

Étant donné que RoadWave a 1 million de contenus dont 300 000 Premium

Quand l'algorithme génère une recommandation

Alors la requête SQL filtre efficacement avec l'index `is_premium` Et le temps de réponse reste <50ms Et la scalabilité est garantie

# Désactivation et suspension monétisation

> En tant que créateur ou plateforme Je veux pouvoir désactiver ou suspendre la monétisation selon certaines conditions Afin de gérer les pauses, problèmes techniques ou violations des règles

35 scénarios

> Contexte commun à tous les scénarios

> Étant donné que je suis un créateur avec la monétisation activée

## 1. Désactivation temporaire par le créateur

Étant donné que je veux faire une pause dans ma création de contenu

Quand j'accède à "Paramètres > Monétisation" Et que je clique sur "Désactiver temporairement la monétisation"

Alors ma monétisation est désactivée immédiatement Et je ne génère plus de revenus à partir de maintenant

## 2. Confirmation avant désactivation

Étant donné que je clique sur "Désactiver temporairement"

Quand une popup de confirmation apparaît

Alors je vois le message: Et je dois confirmer pour continuer

## 3. Solde conservé pendant désactivation

Étant donné que mon solde actuel est 87.45€

Quand je désactive ma monétisation le 15 du mois

Alors mon solde de 87.45€ est conservé Et il sera reporté au mois suivant Et si le total dépasse 50€, il sera versé normalement le 15 du mois prochain

## 4. Contenus restent accessibles pendant désactivation

Étant donné que j'ai désactivé ma monétisation

Quand des utilisateurs écoutent mes contenus

Alors mes contenus restent accessibles normalement Mais je ne génère aucun revenu (ni pub ni Premium)

## 5. Réactivation sans refaire le KYC si <2 ans

Étant donné que j'ai désactivé ma monétisation il y a 8 mois Et que mes documents KYC sont toujours valides

Quand je clique sur "Réactiver la monétisation"

Alors la réactivation est immédiate Et je n'ai pas besoin de refaire le KYC Et je recommence à générer des revenus dès maintenant

## 6. Nouveau KYC requis si inactivité >2 ans

Étant donné que j'ai désactivé ma monétisation il y a 25 mois

Quand j'essaie de réactiver

Alors le système demande un nouveau KYC Et je vois: Et je dois soumettre à nouveau mes documents

## 7. Historique des désactivations/réactivations

Étant donné que j'ai désactivé et réactivé ma monétisation plusieurs fois

Quand j'accède à "Paramètres > Monétisation > Historique"

Alors je vois la liste complète:

    | date | action | raison |
    |---|---|---|
    | 15/06/2025 | Réactivation | Reprise création contenu |
    | 01/03/2025 | Désactivation | Pause vacances |
    | 20/01/2025 | Activation | KYC validé |

## 8. Suspension si 3+ strikes actifs

Étant donné que je reçois un 3ème strike pour violation des règles

Quand le strike devient actif

Alors ma monétisation est suspendue automatiquement Et je vois:

## 9. Réactivation après résolution des strikes

Étant donné que ma monétisation est suspendue pour 3 strikes

Quand je résous tous mes strikes (après expiration ou contestation) Et que mon compteur de strikes passe à 0

Alors ma monétisation est réactivée automatiquement Et je reçois un email de confirmation

## 10. Suspension si RIB invalide après 3 échecs de virement

Étant donné que 3 tentatives de virement ont échoué (15, 18, 22 du mois)

Quand le 3ème échec est confirmé

Alors ma monétisation est suspendue automatiquement Et je vois:

## 11. Réactivation après mise à jour RIB valide

Étant donné que ma monétisation est suspendue pour RIB invalide

Quand je mets à jour mon RIB avec un compte bancaire valide Et que Mangopay valide le nouveau RIB

Alors ma monétisation est réactivée automatiquement Et un virement est tenté immédiatement pour le solde en attente

## 12. Suspension si documents KYC expirés

Étant donné que ma carte d'identité expire dans 30 jours

Quand je reçois un email de rappel de mise à jour Mais que je ne mets pas à jour mes documents Et que ma CNI expire

Alors ma monétisation est suspendue automatiquement après 30 jours de grâce

## 13. Préavis 30 jours avant suspension pour docs expirés

Étant donné que ma CNI expire le 15 juin 2025

Quand le 15 mai 2025 arrive (30 jours avant)

Alors je reçois un email d'alerte:

## 14. Réactivation après renouvellement documents KYC

Étant donné que ma monétisation est suspendue pour CNI expirée

Quand je soumets une nouvelle CNI valide Et que Mangopay valide le document sous 24-72h

Alors ma monétisation est réactivée automatiquement Et je recommence à générer des revenus

## 15. Suspension si fraude détectée

Étant donné que le système détecte une activité frauduleuse (bots, écoutes artificielles)

Quand l'équipe modération confirme la fraude

Alors ma monétisation est suspendue immédiatement Et mon compte est mis sous enquête Et je reçois un email m'informant de la suspension

## 16. Enquête fraude - Vérification manuelle

Étant donné que ma monétisation est suspendue pour suspicion de fraude

Quand l'équipe modération enquête

Alors elle analyse:

    | élément à vérifier | outil |
    |---|---|
    | Patterns d'écoute suspects | Analytics + logs |
    | Origine géographique | Logs IP |
    | Vitesse de croissance anormale | Graphiques statistiques |
    | Plaintes utilisateurs | Système de signalement |

## 17. Levée suspension si fraude non confirmée

Étant donné que mon compte était suspendu pour suspicion de fraude

Quand l'enquête conclut qu'il n'y a pas eu de fraude

Alors ma monétisation est réactivée Et les revenus suspendus pendant l'enquête sont versés normalement Et je reçois un email d'excuses avec explication

## 18. Suspension définitive si fraude confirmée

Étant donné que l'enquête confirme une fraude avérée

Quand l'équipe modération prend la décision

Alors ma monétisation est définitivement désactivée Et mon solde en attente est gelé (non versé) Et je peux recevoir un strike 4 (ban définitif du compte)

## 19. Suppression définitive sur demande créateur

Étant donné que je veux arrêter définitivement la monétisation

Quand j'accède à "Paramètres > Monétisation > Supprimer définitivement"

Alors une confirmation stricte est demandée Et je dois taper "SUPPRIMER" pour confirmer

## 20. Solde versé sous 30 jours après suppression

Étant donné que je supprime définitivement ma monétisation Et que mon solde en attente est 127.45€

Quand la suppression est confirmée

Alors mon solde sera versé sous 30 jours Et je reçois un dernier virement de clôture Et mon e-wallet Mangopay est clôturé

## 21. Suppression auto si inactivité 24 mois + solde <50€

Étant donné que je n'ai plus publié de contenu depuis 24 mois Et que mon solde en attente est 12.30€ (<50€)

Quand le processus de purge RGPD s'exécute

Alors ma monétisation est automatiquement supprimée Et mon solde de 12.30€ est perdu (trop faible pour virement) Et mes données KYC sont archivées puis supprimées selon la législation

## 22. Email de préavis 60 jours avant purge RGPD

Étant donné que je suis inactif depuis 22 mois

Quand le système détecte l'inactivité

Alors je reçois un email:

## 23. Ban définitif compte - Strike 4

Étant donné que je reçois un 4ème strike (violation grave ou répétée)

Quand l'équipe modération applique le strike 4

Alors mon compte est banni définitivement Et ma monétisation est supprimée définitivement Et mon solde en attente est gelé (non versé) Et je ne peux plus créer de nouveau compte (blacklist email/SIRET)

## 24. Email pour toute suspension

Étant donné que ma monétisation est suspendue (quelle qu'en soit la raison)

Quand la suspension devient effective

Alors je reçois immédiatement un email:

## 25. Notification in-app avec raison explicite

Étant donné que ma monétisation est suspendue

Quand je me connecte à l'application

Alors je vois une bannière en haut de mon dashboard:

## 26. Email de confirmation lors de réactivation

Étant donné que ma monétisation était suspendue

Quand elle est réactivée (automatiquement ou manuellement)

Alors je reçois un email:

## 27. Dashboard admin - Suspensions actives

Étant donné qu'un admin RoadWave consulte les suspensions

Quand il accède au dashboard admin "Monétisation > Suspensions"

Alors il voit:

    | raison suspension | nombre actif | taux |
    |---|---|---|
    | Strikes (3+) | 23 | 1.8% |
    | RIB invalide | 12 | 0.9% |
    | Documents KYC expirés | 8 | 0.6% |
    | Fraude sous enquête | 3 | 0.2% |
    | TOTAL | 46 | 3.7% |

## 28. Alertes si taux de suspension >5%

Étant donné que le taux de suspension dépasse 5%

Quand le système détecte cette anomalie

Alors une alerte est envoyée à l'équipe:

## 29. Statistiques personnelles - Temps actif monétisation

Étant donné que j'accède à mon dashboard créateur

Quand je consulte "Statistiques > Monétisation"

Alors je vois:

    | métrique | valeur |
    |---|---|
    | Date activation monétisation | 20 janvier 2025 |
    | Temps actif total | 8 mois |
    | Périodes de désactivation | 2 (3 mois total) |
    | Suspensions subies | 0 |
    | Statut actuel | ✅ Actif |

## 30. Export données suspension (RGPD)

Étant donné que je demande l'export de mes données

Quand l'export est généré

Alors l'historique des suspensions est inclus:

## 31. Suppression compte et données monétisation

Étant donné que je supprime définitivement mon compte RoadWave

Quand la suppression est confirmée

Alors toutes mes données de monétisation sont supprimées:

    | donnée | action |
    |---|---|
    | Solde en attente | Versé sous 30 jours puis supprimé |
    | Historique revenus | Archivé 10 ans (obligation légale) |
    | Documents KYC | Archivés 10 ans chez Mangopay puis supprimés |
    | E-wallet Mangopay | Clôturé après versement final |

## 32. Conservation archives 10 ans obligation légale

Étant donné que je supprime mon compte

Quand mes données sont archivées

Alors RoadWave conserve 10 ans:

    | donnée archivée | raison |
    |---|---|
    | Relevés mensuels PDF | Obligation comptable France |
    | Déclarations DAS2 | Obligation fiscale France |
    | Justificatifs virements | Preuve paiement en cas d'audit |

Et après 10 ans, tout est supprimé définitivement

## 33. Suspension temporaire pour maintenance technique

Étant donné que Mangopay effectue une maintenance planifiée

Quand la maintenance est programmée

Alors tous les créateurs reçoivent un email préventif 7 jours avant:

## 34. Réactivation progressive après incident majeur

Étant donné qu'un incident technique majeur suspend toutes les monétisations

Quand l'incident est résolu

Alors les réactivations se font progressivement:

    | vague | critère | % créateurs |
    |---|---|---|
    | 1 | Top 10% créateurs (revenus) | 10% |
    | 2 | Créateurs vérifiés | 30% |
    | 3 | Tous les autres créateurs | 60% |

Et cela évite une surcharge système lors de la reprise

## 35. Support prioritaire pour créateurs suspendus injustement

Étant donné que ma monétisation est suspendue Et que je pense que c'est une erreur

Quand je contacte le support avec tag "Suspension monétisation"

Alors mon ticket est traité en priorité (SLA 24h) Et un agent expert examine mon cas Et si suspension injustifiée, je suis réactivé immédiatement avec excuses

# KYC et inscription à la monétisation

> En tant que créateur éligible Je veux compléter le KYC pour activer la monétisation Afin de recevoir des paiements légalement

37 scénarios

> Contexte commun à tous les scénarios

> Étant donné que je remplis tous les critères de monétisation Et que j'ai cliqué sur "Demander la monétisation"

## 1. Redirection vers formulaire KYC Mangopay

Quand je démarre le processus d'activation

Alors je suis redirigé vers un formulaire KYC Et le formulaire est fourni par Mangopay (iframe sécurisée) Et toutes les données sont chiffrées et hébergées en EU

## 2. Statut auto-entrepreneur accepté

Étant donné que je suis auto-entrepreneur

Quand je renseigne mon statut juridique

Alors l'option "Auto-entrepreneur (micro-BNC)" est disponible Et je peux continuer le processus

## 3. Statut société SARL/SAS/SASU accepté

Étant donné que j'ai créé une société

Quand je renseigne mon statut juridique

Alors les options suivantes sont disponibles:

    | statut juridique |
    |---|
    | SARL |
    | SAS |
    | SASU |

Et je peux continuer le processus

## 4. Statut particulier refusé

Étant donné que je n'ai pas de statut professionnel

Quand j'essaie de m'inscrire en tant que "Particulier"

Alors le formulaire affiche: Et je ne peux pas continuer sans statut professionnel

## 5. Document SIRET obligatoire et validé

Étant donné que je renseigne mon SIRET

Quand je saisis "12345678901234" (14 chiffres)

Alors le format est validé Et Mangopay vérifie l'existence du SIRET auprès du répertoire SIRENE Et si valide, le document est accepté

## 6. SIRET invalide ou inexistant

Étant donné que je renseigne un SIRET inexistant

Quand je saisis "99999999999999"

Alors Mangopay rejette le SIRET Et je vois "SIRET non trouvé dans le répertoire SIRENE. Vérifiez le numéro." Et je dois corriger avant de continuer

## 7. RIB professionnel obligatoire

Étant donné que j'upload mon RIB

Quand le RIB est scanné par Mangopay

Alors le système vérifie que le titulaire correspond à mon SIRET Et que l'IBAN commence par "FR" (compte français) Et si valide, le document est accepté

## 8. RIB particulier refusé

Étant donné que j'upload un RIB de compte particulier

Quand Mangopay détecte que le compte n'est pas professionnel

Alors le RIB est rejeté Et je vois:

## 9. Pièce d'identité CNI en cours de validité

Étant donné que j'upload ma carte nationale d'identité

Quand Mangopay analyse le document

Alors la date d'expiration est vérifiée Et si la CNI est valide, le document est accepté Et mon identité est vérifiée par OCR + vérification manuelle

## 10. Pièce d'identité expirée refusée

Étant donné que j'upload une CNI expirée depuis 2 ans

Quand Mangopay analyse le document

Alors le document est rejeté Et je vois "Pièce d'identité expirée. Veuillez fournir un document en cours de validité."

## 11. Passeport accepté comme alternative

Étant donné que je n'ai pas de CNI

Quand j'upload mon passeport en cours de validité

Alors Mangopay accepte le passeport Et mon identité est vérifiée de la même manière

## 12. Numéro TVA intracommunautaire si applicable

Étant donné que mon CA dépasse 37 000€/an Et que je suis sorti de la franchise en base

Quand je renseigne mon numéro TVA intracommunautaire

Alors le format "FR + 11 chiffres" est validé Et Mangopay vérifie l'existence auprès de la Commission Européenne (VIES)

## 13. TVA non applicable pour micro-BNC sous franchise

Étant donné que je suis auto-entrepreneur sous franchise en base Et que mon CA est <37 000€/an

Quand je remplis le formulaire KYC

Alors le champ "Numéro TVA" est optionnel Et je peux continuer sans TVA

## 14. Kbis <3 mois pour sociétés

Étant donné que je suis gérant d'une SARL

Quand j'upload mon extrait Kbis

Alors Mangopay vérifie que le Kbis date de moins de 3 mois Et que le SIRET correspond Et si valide, le document est accepté

## 15. Kbis trop ancien refusé

Étant donné que j'upload un Kbis de 5 mois

Quand Mangopay analyse le document

Alors le Kbis est rejeté Et je vois "Le Kbis doit dater de moins de 3 mois. Téléchargez un extrait récent sur infogreffe.fr"

## 16. Vérification identité ne correspond pas au compte

Étant donné que mon compte RoadWave est au nom de "Jean Dupont" Mais que ma CNI est au nom de "Pierre Martin"

Quand Mangopay compare les identités

Alors le KYC est rejeté Et je vois:

## 17. Liste noire anti-blanchiment détectée

Étant donné que mon identité apparaît sur une liste anti-blanchiment

Quand Mangopay effectue la vérification AML (Anti-Money Laundering)

Alors le KYC est automatiquement rejeté Et je vois "Votre demande ne peut être acceptée pour des raisons de conformité légale" Et mon compte créateur peut être suspendu

## 18. Délai de vérification 24-72h si documents conformes

Étant donné que j'ai soumis tous les documents valides

Quand Mangopay traite ma demande

Alors je reçois un email "KYC en cours de vérification (24-72h)" Et mon statut est "En attente de validation" Et je peux continuer à publier des contenus en attendant

## 19. Validation KYC réussie

Étant donné que mes documents sont conformes

Quand Mangopay valide mon KYC après 48h

Alors je reçois un email "Monétisation activée !" Et mon statut passe à "Monétisé" Et je commence à générer des revenus dès maintenant

## 20. Rejet KYC pour documents invalides

Étant donné que j'ai soumis une CNI floue et illisible

Quand Mangopay analyse les documents

Alors le KYC est rejeté après 24h Et je reçois un email détaillant les documents à refournir:

## 21. E-wallet Mangopay créé automatiquement

Étant donné que mon KYC est validé

Quand Mangopay finalise mon inscription

Alors un e-wallet Mangopay est créé automatiquement à mon nom Et tous mes futurs revenus seront transférés vers ce wallet Et les virements SEPA vers mon RIB seront effectués depuis ce wallet

## 22. Conformité RGPD - Données hébergées EU

Étant donné que je fournis mes documents KYC

Quand Mangopay stocke mes données

Alors toutes les données sont hébergées en Union Européenne Et Mangopay est régulé par l'ACPR (Autorité de Contrôle Prudentiel) Et mes données sont protégées selon le RGPD

## 23. KYC gratuit inclus dans Mangopay

Étant donné que je complète le KYC

Quand le processus se termine

Alors aucun frais ne m'est facturé (0€) Et aucun frais n'est facturé à RoadWave (inclus dans l'offre Mangopay)

## 24. Base légale - Conformité fiscale française

Étant donné que RoadWave est une plateforme française

Quand je génère des revenus >1200€/an

Alors RoadWave doit déclarer ces revenus aux impôts (DAS2) Et le KYC permet de garantir l'identité réelle du bénéficiaire Et cela respecte la réglementation fiscale française

## 25. Base légale - Directive anti-blanchiment EU 2018/843

Étant donné que RoadWave verse de l'argent aux créateurs

Quand le KYC est effectué

Alors RoadWave respecte la 5ème directive anti-blanchiment EU Et Mangopay effectue les vérifications requises (identité, liste noire, origine fonds)

## 26. Notification de mise à jour documents expirés

Étant donné que ma CNI va expirer dans 30 jours

Quand le système détecte l'expiration proche

Alors je reçois un email:

## 27. Suspension monétisation si documents expirés

Étant donné que ma CNI est expirée depuis 10 jours Et que je n'ai pas mis à jour mes documents

Quand le système vérifie mon statut KYC

Alors ma monétisation est suspendue automatiquement Et je ne génère plus de revenus jusqu'à mise à jour

## 28. Réactivation sans nouveau KYC si données à jour

Étant donné que j'ai désactivé temporairement ma monétisation il y a 6 mois Et que mes documents KYC sont toujours valides

Quand je réactive la monétisation

Alors je n'ai pas besoin de refaire le KYC Et la réactivation est immédiate

## 29. Nouveau KYC requis après 2 ans d'inactivité

Étant donné que j'ai désactivé ma monétisation il y a 25 mois

Quand j'essaie de réactiver

Alors le système demande un nouveau KYC Et je dois soumettre des documents à jour (CNI peut avoir changé)

## 30. Support créateur pour problèmes KYC

Étant donné que mon KYC est rejeté et je ne comprends pas pourquoi

Quand je contacte le support RoadWave

Alors un agent peut consulter les raisons du rejet Mangopay Et m'aider à fournir les bons documents

## 31. Export données KYC pour RGPD

Étant donné que je demande l'export de mes données personnelles

Quand l'export est généré

Alors les informations KYC sont incluses: Et les documents scannés (CNI, RIB) sont exclus pour sécurité

## 32. Suppression compte et données KYC

Étant donné que je supprime définitivement mon compte RoadWave

Quand la suppression est confirmée

Alors mes données KYC chez Mangopay sont archivées 10 ans (obligation légale) Mais supprimées de la base RoadWave immédiatement Et mon e-wallet est clôturé après versement du solde final

## 33. Statistiques KYC pour monitoring plateforme

Étant donné que RoadWave suit la qualité du processus KYC

Quand un admin consulte les métriques

Alors il voit:

    | métrique | valeur exemple |
    |---|---|
    | Demandes KYC ce mois | 247 |
    | Taux de validation | 87% |
    | Délai moyen validation | 36h |
    | Taux de rejet (documents invalides) | 13% |

Et cela permet d'optimiser le processus

## 34. Vérification SIRET via API INSEE

Étant donné que je saisis mon SIRET

Quand le système le valide

Alors une requête est faite à l'API SIRENE de l'INSEE Et le système vérifie que le SIRET existe et est actif Et récupère le nom de l'entreprise pour pré-remplir le formulaire

## 35. Détection fraude - Même SIRET utilisé par plusieurs comptes

Étant donné qu'un SIRET "12345678901234" est déjà utilisé par un autre créateur

Quand j'essaie d'utiliser le même SIRET

Alors le système détecte la duplication Et affiche "Ce SIRET est déjà utilisé par un autre compte RoadWave" Et je dois contacter le support si c'est une erreur

## 36. Protection données sensibles - Logs chiffrés

Étant donné que des données KYC sensibles transitent dans le système

Quand les logs sont enregistrés

Alors les numéros SIRET, IBAN et données CNI sont masqués: Et seule l'équipe sécurité peut accéder aux données complètes

## 37. Backup Mangopay des documents KYC

Étant donné que mes documents KYC sont stockés chez Mangopay

Quand un audit est demandé par les autorités

Alors Mangopay peut fournir les documents originaux Et RoadWave n'a pas besoin de stocker ces documents (réduction risque RGPD)

# Obligations fiscales

> En tant que créateur monétisé Je veux que RoadWave génère automatiquement les documents fiscaux requis Afin de faciliter ma comptabilité et respecter la loi

30 scénarios

> Contexte commun à tous les scénarios

> Étant donné que je suis un créateur avec la monétisation activée Et que je génère des revenus sur RoadWave

## 1. Génération automatique relevé mensuel PDF

Étant donné que le mois de janvier se termine

Quand le système calcule mes revenus du mois

Alors un relevé mensuel PDF est généré automatiquement Et le PDF est disponible dans mon tableau de bord

## 2. Contenu du relevé mensuel PDF

Étant donné que mon relevé de janvier est généré

Quand je télécharge le PDF

Alors le document contient:

## 3. Téléchargement relevé depuis tableau de bord

Étant donné que je suis sur mon tableau de bord créateur

Quand j'accède à l'onglet "Revenus > Historique"

Alors je vois la liste de mes relevés mensuels:

    | mois | montant | actions |
    |---|---|---|
    | Janvier 2025 | 150.00€ | 📄 Télécharger PDF |
    | Décembre 2024 | 123.50€ | 📄 Télécharger PDF |
    | Novembre 2024 | 98.75€ | 📄 Télécharger PDF |

## 4. Conservation relevés accessibles 10 ans

Étant donné que j'ai commencé la monétisation en janvier 2025

Quand je consulte mes relevés en janvier 2035 (10 ans plus tard)

Alors tous les relevés depuis 2025 sont toujours accessibles Et je peux télécharger n'importe quel relevé historique Et cela respecte l'obligation de conservation comptable de 10 ans

## 5. Export CSV à la demande

Étant donné que je clique sur "Exporter pour comptable"

Quand je choisis la période "Année 2025"

Alors un fichier CSV est généré et téléchargé

## 6. Contenu export CSV détaillé

Étant donné que j'exporte mes données comptables 2025

Quand je télécharge le fichier CSV

Alors le fichier contient:

## 7. Transmission à l'expert-comptable

Étant donné que j'ai téléchargé mon export CSV 2025

Quand je l'envoie à mon expert-comptable

Alors il peut importer le fichier dans son logiciel comptable Et il saisit rapidement mes revenus RoadWave Et cela facilite ma déclaration fiscale annuelle

## 8. DAS2 généré automatiquement si revenus >1200€/an

Étant donné que mes revenus 2025 totalisent 2,450€

Quand l'année 2025 se termine

Alors RoadWave génère automatiquement une DAS2 pour les impôts Et la DAS2 est transmise à la DGFIP en janvier 2026

## 9. Contenu de la DAS2

Étant donné que RoadWave génère ma DAS2 pour 2025

Quand la DGFIP reçoit la déclaration

Alors le document contient:

## 10. Créateur reçoit une copie de la DAS2

Étant donné que RoadWave transmet ma DAS2 aux impôts

Quand la transmission est confirmée

Alors je reçois un email avec une copie de la DAS2 en pièce jointe Et je peux consulter le document dans mon tableau de bord

## 11. Pas de DAS2 si revenus <1200€/an

Étant donné que mes revenus 2025 totalisent seulement 890€

Quand l'année 2025 se termine

Alors aucune DAS2 n'est générée car le seuil de 1200€ n'est pas atteint Mais je dois quand même déclarer mes revenus dans ma déclaration personnelle

## 12. Base légale DAS2 - Obligation France

Étant donné que RoadWave verse des honoraires à des prestataires

Quand les revenus dépassent 1200€/an

Alors la déclaration DAS2 est obligatoire selon l'article 87 du Code Général des Impôts Et le non-respect entraîne une amende de 15€ par bénéficiaire non déclaré

## 13. Transmission DAS2 via EDI-TDFC

Étant donné que RoadWave génère 1,247 DAS2 pour l'année 2025

Quand la transmission aux impôts est effectuée

Alors la transmission se fait via le portail EDI-TDFC de la DGFIP Et la transmission est automatisée (pas de saisie manuelle) Et un accusé de réception est reçu sous 48h

## 14. Créateur responsable de déclarer aux impôts

Étant donné que j'ai reçu 2,450€ de revenus RoadWave en 2025

Quand je fais ma déclaration fiscale en mai 2026

Alors je dois déclarer ces 2,450€ dans ma déclaration annuelle Et si je suis auto-entrepreneur, je déclare en BNC (Bénéfices Non Commerciaux)

## 15. Créateur responsable des cotisations URSSAF

Étant donné que je suis auto-entrepreneur Et que j'ai reçu 2,450€ de revenus RoadWave en 2025

Quand je fais ma déclaration URSSAF trimestrielle

Alors je dois déclarer ces revenus à l'URSSAF Et je paie ~22% de cotisations sociales (soit ~539€)

## 16. TVA non applicable en franchise en base

Étant donné que je suis auto-entrepreneur en micro-BNC Et que mon chiffre d'affaires est <37,800€/an

Quand je génère des revenus sur RoadWave

Alors je bénéficie de la franchise en base de TVA Et je ne facture pas de TVA à RoadWave Et je ne récupère pas la TVA sur mes achats

## 17. TVA applicable si CA >37,800€/an

Étant donné que mon chiffre d'affaires total 2025 est 45,000€

Quand je dépasse le seuil de franchise en base (37,800€)

Alors je dois facturer de la TVA (20%) à RoadWave Et je dois obtenir un numéro TVA intracommunautaire Et je dois déclarer ma TVA mensuellement ou trimestriellement

## 18. Conservation justificatifs 10 ans - Obligation légale

Étant donné que je génère des revenus sur RoadWave

Quand je télécharge mes relevés mensuels et exports CSV

Alors je dois les conserver 10 ans (obligation comptable France) Et en cas de contrôle fiscal, je dois pouvoir les fournir

## 19. Mangopay transmet automatiquement via DAC7

Étant donné que je suis créateur monétisé sur RoadWave

Quand l'année se termine

Alors Mangopay transmet automatiquement mes revenus aux autorités fiscales EU Et cela respecte la directive DAC7 (2021/514) sur la transparence fiscale des plateformes

## 20. Directive DAC7 - Obligations plateforme

Étant donné que RoadWave est une plateforme facilitant des transactions

Quand Mangopay gère les paiements

Alors Mangopay transmet automatiquement:

    | information | destinataire |
    |---|---|
    | Identité créateur (SIRET) | Autorités fiscales pays EU |
    | Revenus annuels | Autorités fiscales pays EU |
    | Nombre transactions | Autorités fiscales pays EU |

Et RoadWave n'a pas besoin de faire cette transmission manuellement

## 21. Justificatif virement = Preuve bancaire comptable

Étant donné que je reçois un virement de 150.00€ de Mangopay

Quand je consulte mon relevé bancaire

Alors je vois le virement avec la référence MANGOPAY-ABC123 Et ce relevé bancaire sert de justificatif comptable Et je peux le fournir à mon expert-comptable ou aux impôts

## 22. Notification annuelle rappel déclaration fiscale

Étant donné que je suis créateur monétisé

Quand le mois d'avril 2026 arrive (période déclaration impôts France)

Alors je reçois un email de rappel:

## 23. Page ressources fiscales pour créateurs

Étant donné que je suis créateur monétisé

Quand j'accède à "Aide > Fiscalité"

Alors je vois une page avec:

    | ressource | description |
    |---|---|
    | Guide auto-entrepreneur RoadWave | PDF expliquant démarches et déclarations |
    | FAQ fiscalité | Questions fréquentes sur TVA, cotisations, etc. |
    | Liens URSSAF et impots.gouv.fr | Portails officiels |
    | Contact expert-comptable partenaire | Recommandations d'experts connaissant RoadWave |

## 24. Dashboard créateur - Récapitulatif annuel

Étant donné que je consulte mon dashboard en décembre 2025

Quand j'accède à "Revenus > Récapitulatif annuel"

Alors je vois:

## 25. Génération automatique minimise erreurs

Étant donné que tous les documents fiscaux sont générés automatiquement

Quand un créateur télécharge ses documents

Alors les montants sont garantis corrects (issus de la base de données) Et il n'y a pas d'erreur de saisie manuelle Et cela réduit les risques de contrôle fiscal

## 26. Conformité RGPD - Données fiscales chiffrées

Étant donné que les documents fiscaux contiennent des données sensibles (SIRET, revenus)

Quand les documents sont stockés

Alors ils sont chiffrés au repos (encryption AES-256) Et seul le créateur et les admins autorisés peuvent y accéder Et les logs d'accès sont conservés pour audit

## 27. Backup documents fiscaux 10 ans

Étant donné qu'un document fiscal est généré

Quand il est stocké dans la base de données

Alors une copie est sauvegardée sur S3 (stockage durable) Et les backups sont répliqués sur 3 zones de disponibilité Et la conservation est garantie 10 ans minimum

## 28. Audit trail génération DAS2

Étant donné que 1,247 DAS2 sont générées en janvier 2026

Quand un audit est demandé

Alors tous les événements sont loggés:

    | événement | timestamp | détails |
    |---|---|---|
    | Calcul revenus annuels | 2025-12-31 23:59:00 | 1,247 créateurs éligibles |
    | Génération fichier EDI | 2026-01-10 08:00:00 | Format EDI-TDFC |
    | Transmission DGFIP | 2026-01-10 10:30:00 | Via portail EDI-TDFC |
    | Accusé réception DGFIP | 2026-01-11 14:20:00 | Transmission confirmée |
    | Email créateurs | 2026-01-11 16:00:00 | 1,247 emails envoyés |

## 29. Statistiques admin - Conformité fiscale

Étant donné qu'un admin RoadWave consulte les métriques fiscales

Quand il accède au dashboard admin

Alors il voit:

    | métrique | valeur 2025 |
    |---|---|
    | Créateurs monétisés | 1,247 |
    | Créateurs éligibles DAS2 (>1200€) | 847 (68%) |
    | Revenus totaux versés | 1,890,345€ |
    | DAS2 transmises à la DGFIP | 847 |
    | Taux conformité | 100% |

## 30. Support créateur pour questions fiscales

Étant donné que j'ai une question sur ma déclaration fiscale

Quand je contacte le support RoadWave

Alors l'agent peut consulter mes documents fiscaux Et m'aider à comprendre ce que je dois déclarer Mais il ne peut pas me conseiller fiscalement (pas expert-comptable) Et il me recommande de consulter un expert-comptable si nécessaire

# Paiement des créateurs

> En tant que créateur monétisé Je veux recevoir mes paiements mensuels de manière fiable Afin d'être rémunéré pour mon travail

35 scénarios

> Contexte commun à tous les scénarios

> Étant donné que je suis un créateur avec la monétisation activée Et que mon KYC est validé

## 1. Seuil minimum de 50€ atteint - Paiement effectué

Étant donné que mes revenus du mois sont 73.45€

Quand le dernier jour du mois arrive

Alors mon solde de 73.45€ est transféré vers "en attente de paiement" Et le paiement sera effectué le 15 du mois prochain

## 2. Seuil minimum de 50€ non atteint - Report mois suivant

Étant donné que mes revenus du mois sont 32.17€

Quand le dernier jour du mois arrive

Alors mon solde de 32.17€ est reporté au mois suivant Et je vois "Solde insuffisant pour paiement (<50€). Report mois prochain."

## 3. Cumul sur plusieurs mois jusqu'à atteindre 50€

Étant donné que mes revenus sont:

    | mois | revenus | solde cumulé |
    |---|---|---|
    | Janvier | 18.50€ | 18.50€ |
    | Février | 22.30€ | 40.80€ |
    | Mars | 15.70€ | 56.50€ |

Quand la fin du mois de mars arrive

Alors le solde cumulé de 56.50€ dépasse les 50€ Et un paiement de 56.50€ est effectué le 15 avril

## 4. Calcul des revenus le dernier jour du mois

Étant donné que nous sommes le 31 janvier à 23h59

Quand le système calcule les revenus du mois

Alors une requête SQL agrège tous les revenus pub et premium Et le solde final du mois est figé dans monthly_revenues Et le compteur du mois en cours repart à 0€ le 1er février

## 5. Période de traitement contestations 1-14 du mois

Étant donné que mes revenus de janvier sont calculés à 150.00€

Quand la période du 1-14 février arrive

Alors RoadWave analyse les éventuelles fraudes ou contestations Et si une fraude est détectée, les revenus concernés sont retirés du solde Et le solde final est validé le 14 février

## 6. Virement SEPA le 15 du mois suivant

Étant donné que mes revenus de janvier validés sont 150.00€

Quand le 15 février arrive

Alors Mangopay initie un virement SEPA depuis mon e-wallet vers mon RIB Et le statut du paiement passe à "En cours"

## 7. Réception virement 16-18 du mois (1-3 jours SEPA)

Étant donné qu'un virement SEPA a été initié le 15 février

Quand 1-3 jours ouvrés s'écoulent

Alors je reçois le virement sur mon compte bancaire entre le 16 et 18 février Et je peux consulter l'historique des paiements dans mon dashboard

## 8. Virement SEPA gratuit pour comptes EU

Étant donné que mon RIB est français (IBAN FR)

Quand Mangopay effectue le virement

Alors aucun frais n'est prélevé (virement SEPA gratuit) Et je reçois 100% du montant annoncé

## 9. Virement international hors EU avec frais variables

Étant donné que je suis créateur expatrié avec RIB hors Union Européenne

Quand Mangopay effectue le virement international

Alors des frais variables s'appliquent selon le pays Et les frais sont déduits du montant final Et je vois le détail des frais dans mon historique

## 10. E-wallet Mangopay automatique

Étant donné que mon KYC est validé

Quand mes revenus sont calculés

Alors les revenus sont automatiquement transférés vers mon e-wallet Mangopay Et l'e-wallet est débité lors du virement SEPA vers mon RIB Et je n'ai aucune action manuelle à faire

## 11. Tableau de bord - Revenus pub temps réel

Étant donné que j'accède à mon tableau de bord créateur

Quand je consulte l'onglet "Revenus"

Alors je vois:

    | métrique | valeur exemple |
    |---|---|
    | Revenus pub ce mois | 123.45€ |
    | Revenus premium ce mois | 67.89€ |
    | Solde disponible ce mois | 191.34€ |
    | Prochain paiement | 15 mars 2025 |

Et ces valeurs sont mises à jour en temps réel (cache Redis, refresh 10 min)

## 12. Tableau de bord - Solde en attente de paiement

Étant donné que mes revenus de janvier sont calculés et validés Et que nous sommes le 10 février

Quand je consulte mon tableau de bord

Alors je vois:

    | métrique | valeur exemple |
    |---|---|
    | Solde en attente | 150.00€ |
    | Date de paiement | 15 février 2025 |
    | Statut | En attente |

## 13. Historique des virements permanents

Étant donné que je suis monétisé depuis 6 mois

Quand je consulte l'historique des paiements

Alors je vois la liste complète:

    | date paiement | montant | statut | référence virement |
    |---|---|---|---|
    | 15/02/2025 | 150.00€ | Payé | MANGOPAY-ABC123 |
    | 15/01/2025 | 123.50€ | Payé | MANGOPAY-XYZ789 |
    | 15/12/2024 | 98.75€ | Payé | MANGOPAY-DEF456 |
    | ... | ... | ... | ... |

## 14. Export comptable CSV téléchargeable

Étant donné que je clique sur "Télécharger export comptable"

Quand le fichier CSV est généré

Alors je télécharge un fichier contenant: Et je peux transmettre ce fichier à mon expert-comptable

## 15. Échec virement - Tentative 1 échouée

Étant donné qu'un virement est initié le 15 février Mais que mon RIB est invalide ou le compte est fermé

Quand Mangopay détecte l'échec

Alors le statut passe à "Échec - Retry programmé le 18 février" Et je reçois un email m'alertant du problème

## 16. Échec virement - Retry automatique J+3

Étant donné que le virement du 15 février a échoué

Quand le 18 février arrive (J+3)

Alors Mangopay tente automatiquement un nouveau virement Et si le RIB est toujours invalide, le virement échoue à nouveau

## 17. Échec virement - Retry automatique J+7

Étant donné que les 2 premières tentatives ont échoué

Quand le 22 février arrive (J+7)

Alors Mangopay tente une 3ème et dernière fois Et si le virement échoue encore, la monétisation est suspendue

## 18. Échec virement - Suspension monétisation après 3 échecs

Étant donné que les 3 tentatives de virement ont échoué

Quand le système détecte le 3ème échec

Alors ma monétisation est suspendue automatiquement Et je reçois un email:

## 19. Mise à jour RIB et réactivation paiement

Étant donné que ma monétisation est suspendue pour RIB invalide Et que mon solde en attente est 150.00€

Quand je mets à jour mon RIB avec un compte valide

Alors Mangopay tente immédiatement un nouveau virement Et si le virement réussit, ma monétisation est réactivée automatiquement

## 20. Notification email lors de chaque paiement

Étant donné qu'un virement de 150.00€ est effectué le 15 février

Quand le virement est confirmé par Mangopay

Alors je reçois un email:

## 21. Justification seuil 50€ - Éviter frais bancaires micro-sommes

Étant donné que Mangopay facture des frais fixes par virement Et que les banques peuvent facturer des frais de réception

Quand un créateur génère seulement 5€/mois

Alors un virement mensuel coûterait proportionnellement trop cher Et le seuil de 50€ garantit des frais proportionnels raisonnables

## 22. Comparaison avec YouTube (seuil 100$)

Étant donné que YouTube fixe le seuil à 100$ (~90€)

Quand RoadWave fixe le seuil à 50€

Alors RoadWave est plus accessible pour petits créateurs Et les paiements arrivent plus rapidement

## 23. Comparaison avec Twitch (seuil 50$)

Étant donné que Twitch fixe le seuil à 50$ (~45€)

Quand RoadWave fixe le seuil à 50€

Alors le seuil est aligné sur Twitch Et les créateurs comprennent facilement le système

## 24. Comparaison avec Spotify (seuil 10€ mais délais longs)

Étant donné que Spotify a un seuil bas de 10€ mais verse tous les 3 mois

Quand RoadWave a un seuil de 50€ mais verse chaque mois

Alors les créateurs reçoivent leurs paiements plus régulièrement Et la trésorerie est plus prévisible

## 25. Relevé mensuel PDF automatique

Étant donné que mes revenus de janvier sont calculés

Quand le 1er février arrive

Alors un relevé mensuel PDF est généré automatiquement: Et le PDF est téléchargeable depuis mon tableau de bord

## 26. Conservation relevés 10 ans (obligation comptable)

Étant donné que je génère des revenus sur RoadWave

Quand je télécharge mes relevés mensuels

Alors je dois les conserver 10 ans (obligation légale France) Et RoadWave conserve également une copie pendant 10 ans pour audit

## 27. Dashboard admin - Monitoring paiements

Étant donné qu'un admin RoadWave consulte les paiements du mois

Quand il accède au dashboard admin

Alors il voit:

    | métrique | valeur exemple |
    |---|---|
    | Créateurs payés ce mois | 1,247 |
    | Montant total versé | 127,345€ |
    | Paiements en attente | 34 |
    | Échecs virements | 3 |
    | Délai moyen réception (jours) | 1.8 |

## 28. Alerte admin si taux échec >5%

Étant donné que 8% des virements du mois ont échoué

Quand le système détecte le taux d'échec élevé

Alors une alerte est envoyée à l'équipe technique:

## 29. Statistiques personnelles - Moyenne revenus sur 6 mois

Étant donné que je suis monétisé depuis 6 mois

Quand je consulte mes statistiques

Alors je vois:

    | métrique | valeur |
    |---|---|
    | Revenus moyens/mois | 134.50€ |
    | Meilleur mois | 189.00€ |
    | Mois le plus bas | 87.30€ |
    | Tendance | +12% ↗ |

Et cela m'aide à suivre ma progression

## 30. Projection revenus annuels

Étant donné que mes revenus moyens sont 134.50€/mois

Quand je consulte les projections

Alors le système estime mes revenus annuels à ~1,614€ Et je peux anticiper mes déclarations fiscales

## 31. Notification seuil symbolique 1000€ cumulés

Étant donné que mes revenus cumulés depuis inscription atteignent 1000€

Quand le paiement qui franchit ce seuil est effectué

Alors je reçois une notification:

## 32. Performance calcul avec 100 000 créateurs monétisés

Étant donné que RoadWave a 100 000 créateurs monétisés

Quand le calcul des paiements du 15 du mois est lancé

Alors un job asynchrone traite les paiements par batch de 1000 Et tous les virements sont initiés en 2-4 heures Et les serveurs Mangopay gèrent la charge sans problème

## 33. Backup des données de paiement

Étant donné que les paiements sont critiques pour les créateurs

Quand un paiement est effectué

Alors les données sont sauvegardées dans PostgreSQL (principal) Et répliquées vers une base de backup (replica) Et une copie d'archive est stockée sur S3 (conservation 10 ans)

## 34. Audit trail complet des paiements

Étant donné qu'un paiement est initié, traité et complété

Quand un audit est demandé

Alors tous les événements sont loggés:

    | événement | timestamp | détails |
    |---|---|---|
    | Calcul revenus mois | 2025-01-31 23:59:00 | Montant: 150.00€ |
    | Validation période fraude | 2025-02-14 23:59:00 | Aucune fraude détectée |
    | Initiation virement | 2025-02-15 09:00:00 | Mangopay ref: ABC123 |
    | Confirmation virement | 2025-02-16 14:30:00 | Reçu par banque créateur |

Et ces logs sont conservés 10 ans pour conformité

## 35. Protection fraude - Détection pattern suspect

Étant donné qu'un créateur génère subitement 10 000€ de revenus en 1 mois

Alors que sa moyenne est de 50€/mois

Quand le système détecte cette anomalie

Alors le paiement est mis en attente pour vérification manuelle Et l'équipe modération analyse le compte avant validation

# Sources de revenus créateurs

> En tant que créateur monétisé Je veux générer des revenus via publicités et abonnés Premium Afin d'être rémunéré pour mon travail

34 scénarios

> Contexte commun à tous les scénarios

> Étant donné que je suis un créateur avec la monétisation activée Et que mon KYC est validé

## 1. CPM créateur de 3€ / 1000 écoutes complètes

Étant donné que mes contenus ont généré 1000 écoutes complètes par des utilisateurs gratuits

Quand le calcul des revenus du mois est effectué

Alors je touche 3.00€ pour ces 1000 écoutes Et ce montant est ajouté à mon solde disponible

## 2. 10 000 écoutes gratuits → 30€ de revenus pub

Étant donné que mes contenus ont généré 10 000 écoutes complètes (utilisateurs gratuits)

Quand le mois se termine

Alors je touche 30.00€ de revenus publicitaires Et ces revenus sont visibles en temps réel dans mon tableau de bord

## 3. 50 000 écoutes gratuits → 150€ de revenus pub

Étant donné que mes contenus ont généré 50 000 écoutes complètes (utilisateurs gratuits)

Quand le mois se termine

Alors je touche 150.00€ de revenus publicitaires

## 4. 100 000 écoutes gratuits → 300€ de revenus pub

Étant donné que mes contenus ont généré 100 000 écoutes complètes (utilisateurs gratuits)

Quand le mois se termine

Alors je touche 300.00€ de revenus publicitaires

## 5. Répartition économique - Plateforme garde 94%

Étant donné qu'une publicité facturée 0.05€/écoute génère 50€ CPM

Quand la plateforme calcule la répartition

Alors le créateur touche 3€ (6% du CA pub) Et la plateforme garde 47€ (94%) pour:

    | poste budgétaire | coût estimé |
    |---|---|
    | CDN + infrastructure | 10-15€ |
    | Modération + support | 5-10€ |
    | Développement + R&D | 10-15€ |
    | Marge opérationnelle | 10-15€ |

## 6. Écoute complète = ≥80% du contenu écouté

Étant donné qu'un utilisateur gratuit écoute mon contenu de 10 minutes

Quand il écoute 8 minutes (80%)

Alors l'écoute compte comme "complète" Et je génère 0.003€ de revenus pub (3€/1000)

## 7. Écoute incomplète <80% ne compte pas

Étant donné qu'un utilisateur gratuit écoute mon contenu de 10 minutes Mais il skip après 5 minutes (50%)

Quand le calcul des revenus est effectué

Alors cette écoute ne compte pas comme "complète" Et je ne génère aucun revenu publicitaire pour cette écoute

## 8. Écoutes Premium ne comptent pas pour les revenus pub

Étant donné qu'un utilisateur Premium écoute 100% de mon contenu

Quand le calcul des revenus publicitaires est effectué

Alors cette écoute ne compte pas dans les revenus pub Mais elle compte dans les revenus Premium (système séparé)

## 9. Détection bots - Écoutes exclues

Étant donné qu'un bot génère 10 000 écoutes artificielles sur mes contenus

Quand le système détecte le pattern suspect (rate limiting, IP unique, etc.)

Alors ces écoutes sont marquées comme frauduleuses Et elles sont exclues du calcul des revenus publicitaires

## 10. Comparaison avec YouTube (3-5€/1000 vues)

Étant donné que YouTube paie 3-5€/1000 vues

Quand RoadWave fixe le CPM créateur à 3€/1000 écoutes

Alors le tarif est aligné sur le bas de la fourchette YouTube Et cela est compétitif pour un MVP sans marché publicitaire mature

## 11. Comparaison avec Spotify (3-4€/1000 écoutes)

Étant donné que Spotify paie ~3-4€/1000 écoutes

Quand RoadWave fixe le CPM créateur à 3€/1000 écoutes

Alors le tarif est aligné sur l'industrie musicale Et les créateurs audio peuvent anticiper des revenus similaires

## 12. Tableau de bord - Revenus pub temps réel

Étant donné que j'accède à mon tableau de bord créateur

Quand je consulte mes revenus publicitaires

Alors je vois:

    | métrique | valeur exemple |
    |---|---|
    | Écoutes complètes ce mois (gratuit) | 23 456 |
    | Revenus pub ce mois | 70.37€ |
    | CPM effectif | 3.00€ |

Et ces valeurs sont mises à jour toutes les 10 minutes

## 13. Répartition 70/30 - Créateur touche 70%

Étant donné qu'un utilisateur Premium paie 4.99€/mois

Quand la répartition est calculée

Alors 3.49€ sont reversés aux créateurs écoutés (70%) Et 1.50€ sont gardés par la plateforme (30%)

## 14. Utilisateur écoute 3 créateurs - Répartition proportionnelle

Étant donné qu'un utilisateur Premium paie 4.99€/mois Et qu'il écoute 3 créateurs ce mois:

    | créateur | temps écoute | ratio |
    |---|---|---|
    | Créateur A | 10h | 50% |
    | Créateur B | 6h | 30% |
    | Créateur C | 4h | 20% |

Quand le calcul des revenus Premium est effectué

Alors la répartition est:

    | créateur | revenus |
    |---|---|
    | Créateur A | 1.75€ |
    | Créateur B | 1.05€ |
    | Créateur C | 0.70€ |

Et la somme totale versée aux créateurs est 3.50€ (70% de 4.99€)

## 15. Calcul SQL proportionnel au temps d'écoute

Étant donné qu'un utilisateur Premium a écouté plusieurs créateurs

Quand le système calcule les revenus du mois

Alors la requête SQL suivante est exécutée:

## 16. Utilisateur écoute un seul créateur - 100% à ce créateur

Étant donné qu'un utilisateur Premium paie 4.99€/mois Et qu'il n'écoute qu'un seul créateur (moi)

Quand le mois se termine

Alors je touche 3.49€ (70% de 4.99€) Et je reçois 100% de la part créateurs

## 17. Utilisateur Premium inactif - Aucun revenu généré

Étant donné qu'un utilisateur Premium paie 4.99€/mois Mais qu'il n'écoute aucun contenu ce mois

Quand le calcul des revenus Premium est effectué

Alors aucun créateur ne reçoit de revenus de cet utilisateur Et les 3.49€ de la part créateurs restent à la plateforme Et cela couvre les coûts d'infrastructure

## 18. Comparaison avec YouTube Premium (70/30)

Étant donné que YouTube Premium reverse 70% aux créateurs

Quand RoadWave fixe également 70/30

Alors le modèle est aligné sur le standard industrie Et les créateurs ont confiance dans l'équité du système

## 19. Comparaison avec Spotify (70/30)

Étant donné que Spotify reverse 70% aux artistes

Quand RoadWave fixe également 70/30

Alors le modèle est identique à Spotify Et les créateurs audio comprennent facilement le système

## 20. Apple Music moins avantageux (52/48)

Étant donné qu'Apple Music ne reverse que 52% aux artistes

Quand RoadWave offre 70% aux créateurs

Alors RoadWave est plus avantageux de 18 points Et cela devient un argument marketing fort

## 21. Justification équité - Créateurs les plus écoutés gagnent plus

Étant donné que 2 créateurs ont le même nombre d'abonnés Premium Mais que le Créateur A est écouté 20h/mois et le Créateur B seulement 2h/mois

Quand les revenus Premium sont calculés

Alors le Créateur A gagne 10× plus que le Créateur B Et cela récompense la qualité et l'engagement (pas juste l'abonnement)

## 22. Pas de "winner takes all" - Équité totale

Étant donné qu'un utilisateur Premium écoute 10 créateurs différents

Quand les revenus sont calculés

Alors chacun des 10 créateurs reçoit sa part proportionnelle Et il n'y a pas de système où un seul créateur prend tout

## 23. Marge plateforme 30% couvre absence revenus pub Premium

Étant donné qu'un utilisateur Premium ne voit aucune publicité

Quand la plateforme calcule ses revenus

Alors elle ne touche que les 30% de l'abonnement Premium (1.50€) Et cette marge compense la perte des revenus publicitaires (qui auraient été ~47€/1000 écoutes)

## 24. Tableau de bord - Revenus Premium temps réel

Étant donné que j'accède à mon tableau de bord créateur

Quand je consulte mes revenus Premium

Alors je vois:

    | métrique | valeur exemple |
    |---|---|
    | Abonnés Premium actifs ayant écouté | 47 |
    | Heures d'écoute Premium ce mois | 234h |
    | Revenus Premium ce mois | 89.23€ |

Et ces valeurs sont mises à jour toutes les 10 minutes

## 25. Revenus cumulés pub + premium

Étant donné que j'ai généré ce mois:

    | source | montant |
    |---|---|
    | Revenus pub | 150.00€ |
    | Revenus Premium | 89.23€ |

Quand je consulte mon solde disponible

Alors le total est 239.23€ Et ce solde sera versé le 15 du mois prochain (si ≥50€)

## 26. Dashboard créateur - Vue d'ensemble

Étant donné que j'accède à mon tableau de bord créateur

Quand je consulte la page revenus

Alors je vois:

## 27. Export comptable CSV pour expert-comptable

Étant donné que je clique sur "Exporter pour comptable"

Quand l'export est généré

Alors je télécharge un fichier CSV: Et je peux transmettre ce fichier à mon expert-comptable

## 28. Notification hebdomadaire progression revenus

Étant donné que je suis créateur monétisé

Quand chaque lundi matin arrive

Alors je reçois un email récapitulatif:

## 29. Graphique évolution revenus sur 12 mois

Étant donné que je suis monétisé depuis 12 mois

Quand j'accède à mes statistiques

Alors je vois un graphique en courbes montrant:

    | mois | revenus pub | revenus premium | total |
    |---|---|---|---|
    | Jan 25 | 150€ | 89€ | 239€ |
    | Déc 24 | 123€ | 55€ | 178€ |
    | Nov 24 | 100€ | 56€ | 156€ |
    | ... | ... | ... | ... |

Et cela m'aide à suivre ma progression

## 30. Top 3 contenus les plus rentables du mois

Étant donné que j'ai publié 20 contenus ce mois

Quand je consulte mes statistiques détaillées

Alors je vois mon top 3 contenus:

    | titre | écoutes | revenus pub | revenus premium | total |
    |---|---|---|---|---|
    | Mon meilleur épisode | 12,345 | 37.04€ | 23.45€ | 60.49€ |
    | Discussion tech | 8,901 | 26.70€ | 15.67€ | 42.37€ |
    | Road trip Bretagne | 7,234 | 21.70€ | 12.34€ | 34.04€ |

Et cela m'aide à comprendre quel type de contenu plaît le plus

## 31. Alertes seuils de revenus

Étant donné que j'ai activé les notifications de seuils

Quand mes revenus du mois dépassent 100€ pour la première fois

Alors je reçois une notification:

## 32. Performance calcul avec 100 000 créateurs

Étant donné que RoadWave a 100 000 créateurs monétisés

Quand le calcul des revenus mensuels est lancé le dernier jour du mois

Alors un job asynchrone traite tous les créateurs Et le calcul prend environ 2-4 heures pour tous les créateurs Et les résultats sont stockés dans la table monthly_revenues

## 33. Cache Redis pour métriques temps réel

Étant donné que je consulte mon dashboard plusieurs fois par jour

Quand la page se charge

Alors les compteurs sont récupérés depuis Redis:

    | clé Redis | valeur exemple |
    |---|---|
    | creator:[id]:complete_listens:202501 | 50234 |
    | creator:[id]:premium_hours:202501 | 234 |
    | creator:[id]:revenue_ads:202501 | 150.70 |
    | creator:[id]:revenue_premium:202501 | 89.23 |

Et le temps de réponse est <30ms

## 34. Prévision revenus fin de mois

Étant donné que nous sommes le 20 du mois Et que mes revenus actuels sont 160€

Quand le système calcule la projection

Alors il estime les revenus fin de mois à ~240€ (extrapolation linéaire) Et affiche "Projection fin de mois: ~240€" Et cela m'aide à anticiper mes revenus

# Actions complémentaires à l'arrêt

> En tant qu'auditeur avec véhicule arrêté Je veux accéder à des actions avancées depuis l'application mobile Afin de liker explicitement, m'abonner ou signaler du contenu

23 scénarios (21 standards, 2 plans)

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et qu'un utilisateur est connecté Et que le véhicule est à l'arrêt (vitesse GPS = 0 km/h)

## 1. Like explicite avec bouton cœur

Étant donné que j'écoute un contenu tagué "Automobile" Et que ma jauge "Automobile" est à 60%

Quand je clique sur le bouton cœur "Like"

Alors ma jauge "Automobile" augmente de 2% Et une animation de cœur rouge s'affiche Et une vibration courte est déclenchée Et ma jauge "Automobile" est maintenant à 62%

## 2. Like explicite cumulable avec like automatique

Étant donné que j'ai écouté un contenu "Voyage" à 85% Et que j'ai reçu un like automatique renforcé (+2%) Et que ma jauge "Voyage" est à 52%

Quand je clique sur le bouton cœur "Like"

Alors ma jauge "Voyage" augmente encore de 2% Et ma jauge "Voyage" passe à 54% Et les deux likes sont cumulés

## 3. Unlike retire le like manuel uniquement

Étant donné que j'ai liké manuellement un contenu "Sport" Et que ma jauge "Sport" est à 57%

Quand je clique à nouveau sur le bouton cœur (toggle)

Alors le cœur redevient vide (unlike) Et ma jauge "Sport" diminue de 2% Et ma jauge "Sport" revient à 55%

## 4. Unlike ne retire pas le like automatique

Étant donné que j'ai écouté un contenu "Musique" à 90% Et que j'ai reçu un like automatique renforcé (+2%) Et que ma jauge "Musique" est à 52% Et que je n'ai PAS liké manuellement

Quand je consulte l'interface

Alors le bouton "Unlike" n'est pas disponible Et le cœur reste grisé (aucun like manuel) Et ma jauge reste à 52%

## 5. Abonnement à un créateur

Étant donné qu'un créateur publie des contenus tagués "Automobile" et "Technologie" Et que mes jauges sont:

    | catégorie | niveau |
    |---|---|
    | Automobile | 50% |
    | Technologie | 45% |

Quand je clique sur "S'abonner" sur le profil du créateur

Alors ma jauge "Automobile" augmente de 5% Et ma jauge "Technologie" augmente de 5% Et une animation d'étoile dorée s'affiche Et un badge "Abonné ✓" apparaît sur le profil Et mes nouvelles jauges sont:

    | catégorie | niveau |
    |---|---|
    | Automobile | 55% |
    | Technologie | 50% |

## 6. Désabonnement d'un créateur

Étant donné que je suis abonné à un créateur Et que mes jauges "Automobile" et "Technologie" sont à 55% et 50%

Quand je clique sur "Se désabonner"

Alors ma jauge "Automobile" diminue de 5% Et ma jauge "Technologie" diminue de 5% Et le badge "Abonné ✓" disparaît Et mes nouvelles jauges sont:

    | catégorie | niveau |
    |---|---|
    | Automobile | 50% |
    | Technologie | 45% |

## 7. Signalement d'un contenu inapproprié

Étant donné que j'écoute un contenu

Quand je clique sur le menu contextuel "⋮" Et que je sélectionne "Signaler"

Alors un formulaire de signalement s'ouvre Et je dois sélectionner une catégorie:

    | Catégorie |
    |---|
    | Haine et violence |
    | Contenu sexuel |
    | Illégalité |
    | Droits d'auteur |
    | Spam |
    | Désinformation (fake news) |
    | Autre |

Et je peux ajouter un commentaire optionnel Et le signalement est envoyé au flux de modération

## 8. Feedback visuel pour like explicite

Étant donné que je clique sur le bouton cœur

Quand le like est enregistré

Alors une animation de cœur rouge se lance (0.5s) Et le cœur reste rouge plein Et une vibration haptique courte est déclenchée (iOS: .light, Android: 50ms) Et un badge "♥ Ajouté à vos favoris" s'affiche 2 secondes

## 9. Feedback visuel pour abonnement

Étant donné que je clique sur "S'abonner"

Quand l'abonnement est enregistré

Alors une animation d'étoile dorée se lance (0.8s) Et le bouton devient "Abonné ✓" avec badge doré Et une notification "Abonné à [Créateur]" s'affiche Et les contenus du créateur seront boostés +30% dans l'algo

## 10. Bouton like désactivé si vitesse >10 km/h

Étant donné que je conduis à 50 km/h

Quand j'essaie d'accéder au bouton cœur dans l'app mobile

Alors le bouton est grisé et non cliquable Et un message "Arrêtez-vous pour liker" s'affiche si clic tenté Et seules les commandes au volant physiques fonctionnent

## 11. Bouton abonnement désactivé en conduite

Étant donné que je conduis à 40 km/h

Quand j'essaie d'accéder au profil créateur dans l'app

Alors le bouton "S'abonner" est désactivé Et un message "Arrêtez-vous pour vous abonner" s'affiche Et la navigation dans l'app est limitée aux fonctions lecture

## 12. Signalement possible en conduite via vocal

Étant donné que je conduis à 60 km/h Et que j'utilise CarPlay avec Siri

Quand je dis "Hey Siri, signale ce contenu"

Alors Siri demande "Quelle catégorie ?" Et je peux répondre vocalement "Spam" ou autre catégorie Et le signalement est enregistré sans toucher l'écran

## 13. Actions vocales disponibles avec CarPlay/Android Auto

Étant donné que je conduis avec CarPlay activé

Quand je dis "Hey Siri, like ce podcast"

Alors un like explicite (+2%) est enregistré Et Siri confirme "J'ai ajouté ce contenu à vos favoris"

Quand je dis "OK Google, abonne-moi à ce créateur"

Alors l'abonnement est enregistré (+5% toutes jauges) Et Google Assistant confirme "Vous êtes maintenant abonné"

## 14. Menu contextuel accessible à l'arrêt uniquement

Étant donné que le véhicule est à l'arrêt

Quand je clique sur le menu "⋮" (3 points verticaux)

Alors les options disponibles sont:

    | Option |
    |---|
    | Like (cœur) |
    | S'abonner au créateur |
    | Signaler |
    | Partager |
    | Voir le profil du créateur |
    | Télécharger (mode offline) |

Et toutes les options sont cliquables

## 15. Menu contextuel limité en conduite

Étant donné que je conduis à 30 km/h

Quand j'essaie d'ouvrir le menu "⋮"

Alors seules 2 options sont disponibles:

    | Option |
    |---|
    | Signaler (vocal possible) |
    | Suivant |

Et les actions complexes sont désactivées

## 16. Persistance des likes manuels en base de données

Étant donné que je like manuellement 5 contenus

Quand je ferme l'application Et que je me reconnecte plus tard

Alors tous mes likes manuels sont toujours présents Et les cœurs rouges sont affichés sur les contenus likés Et mes jauges reflètent toujours l'impact (+2% × 5 likes)

## 17. Liste "Mes contenus likés" accessible dans profil

Étant donné que j'ai liké manuellement 10 contenus

Quand j'accède à mon profil utilisateur

Alors je vois une section "❤️ Mes favoris" Et la liste affiche les 10 contenus likés Et je peux cliquer pour réécouter Et je peux retirer un like (unlike) depuis cette liste

## 18. Liste "Mes abonnements" accessible dans profil

Étant donné que je suis abonné à 5 créateurs

Quand j'accède à mon profil utilisateur

Alors je vois une section "⭐ Mes abonnements" Et la liste affiche les 5 créateurs avec leurs avatars Et je peux accéder au profil de chaque créateur Et je peux me désabonner depuis cette liste

## 19. Impact abonnement sur tous les tags du créateur

Étant donné qu'un créateur a publié des contenus avec ces tags:

    | Contenu | Tags |
    |---|---|
    | C1 | Automobile, Voyage |
    | C2 | Automobile, Technologie |
    | C3 | Voyage, Famille |

Et que mes jauges sont toutes à 50%

Quand je m'abonne à ce créateur

Alors les jauges impactées sont:

    | Tag | Impact |
    |---|---|
    | Automobile | +5% |
    | Voyage | +5% |
    | Technologie | +5% |
    | Famille | +5% |

Et toutes les autres jauges restent à 50%

## 20. Limite d'abonnements (200 maximum)

Étant donné que je suis abonné à 200 créateurs

Quand j'essaie de m'abonner à un 201ème créateur

Alors un message "Limite de 200 abonnements atteinte" s'affiche Et je dois me désabonner d'un créateur existant pour en ajouter un nouveau

## 21. Confirmation avant désabonnement

Étant donné que je suis abonné à un créateur

Quand je clique sur "Se désabonner"

Alors une popup de confirmation s'affiche: Et je dois confirmer pour valider Et je peux annuler pour conserver l'abonnement

## 22. 📋 Plan: Cumul like automatique + like manuel

Étant donné qu'un contenu est tagué "Sport" Et que ma jauge "Sport" est à 50%

Quand j'écoute à % (like auto ) Et que je like manuellement (+2%)

Alors l'impact total est Et ma nouvelle jauge est

📊 Exemples de données:

pourcentage | auto | total | nouveau_niveau
--- | --- | --- | ---
10 | 0 | +2% | 52%
30 | +1% | +3% | 53%
50 | +1% | +3% | 53%
80 | +2% | +4% | 54%
95 | +2% | +4% | 54%

## 23. 📋 Plan: Actions disponibles selon vitesse GPS

Étant donné que je roule à km/h

Quand j'essaie d'accéder à

Alors l'action est

📊 Exemples de données:

vitesse | action | disponibilite
--- | --- | ---
0 | Like manuel | disponible
0 | Abonnement | disponible
0 | Signalement | disponible
5 | Like manuel | disponible
5 | Abonnement | disponible
10 | Like manuel | désactivée
10 | Abonnement | désactivée
50 | Like manuel | désactivée
50 | Abonnement | désactivée
50 | Signalement vocal | disponible

# Commande "Précédent"

> En tant qu'auditeur Je veux que le bouton "Précédent" ait un comportement intelligent Afin de rejouer le contenu actuel ou revenir au précédent selon la progression

19 scénarios (17 standards, 2 plans)

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et qu'un utilisateur est connecté

## 1. Précédent après <10s revient au contenu précédent

Étant donné que j'ai écouté le contenu "A" pendant 2 minutes Et que j'écoute maintenant le contenu "B" depuis 5 secondes

Quand j'appuie sur "Précédent"

Alors la lecture revient au contenu "A" Et la position de lecture est à 2 minutes (position exacte sauvegardée) Et le contenu "B" reste en historique

## 2. Précédent après ≥10s rejoue le contenu actuel

Étant donné que j'écoute le contenu "C" depuis 15 secondes

Quand j'appuie sur "Précédent"

Alors le contenu "C" rejoue depuis le début (position 0:00) Et la lecture ne revient pas au contenu précédent Et la progress bar revient à 0%

## 3. Précédent exactement à 10s rejoue le contenu actuel

Étant donné que j'écoute le contenu "D" depuis exactement 10 secondes

Quand j'appuie sur "Précédent"

Alors le contenu "D" rejoue depuis le début Et la lecture ne revient pas au contenu précédent

## 4. Précédent sur le premier contenu de session

Étant donné que je viens de démarrer l'application Et que j'écoute le contenu "Premier" depuis 3 secondes

Quand j'appuie sur "Précédent"

Alors le contenu "Premier" rejoue depuis le début Et aucun contenu précédent n'existe

## 5. Historique de navigation limité à 10 contenus

Étant donné que j'ai écouté 10 contenus [C1, C2, ..., C10] Et que l'historique Redis contient 10 entrées

Quand je passe au contenu C11

Alors le contenu C1 est supprimé de l'historique (FIFO) Et l'historique contient [C2, C3, ..., C10, C11] Et la taille reste à 10 contenus maximum

## 6. Position exacte sauvegardée dans l'historique

Étant donné que j'écoute le contenu "A" (durée 5 minutes)

Quand j'atteins 2 minutes 30 secondes Et que j'appuie sur "Suivant"

Alors l'historique enregistre:

    | content_id | position_seconds | listened_at |
    |---|---|---|
    | A | 150 | 2026-01-21T10:30:00 |

Quand je reviens au contenu "A" via "Précédent"

Alors la lecture reprend exactement à 2 minutes 30 secondes

## 7. Navigation arrière sur plusieurs contenus

Étant donné que j'ai écouté dans l'ordre: A (2min), B (30s), C (3min) Et que j'écoute maintenant D depuis 1 seconde

Quand j'appuie sur "Précédent" (1ère fois)

Alors je reviens au contenu C à la position 3 minutes

Quand j'appuie sur "Précédent" (<10s sur C)

Alors je reviens au contenu B à la position 30 secondes

Quand j'appuie sur "Précédent" (<10s sur B)

Alors je reviens au contenu A à la position 2 minutes

## 8. Précédent après milieu du contenu rejoue depuis début

Étant donné que j'écoute un contenu de 5 minutes

Quand j'atteins 2 minutes 30 secondes (milieu) Et que j'appuie sur "Précédent"

Alors le contenu actuel rejoue depuis 0:00 Et je ne reviens pas au contenu précédent

## 9. Enchaînement Suivant puis Précédent rapide

Étant donné que j'écoute le contenu "A" depuis 1 minute

Quand j'appuie sur "Suivant"

Alors le contenu "B" démarre

Quand j'appuie immédiatement sur "Précédent" (2s après)

Alors je reviens au contenu "A" à la position 1 minute Et le contenu "B" reste dans l'historique

## 10. Transition fluide avec animation 0.3s

Étant donné que j'appuie sur "Précédent"

Quand le changement de contenu se produit

Alors la transition audio utilise un fade out/in de 0.3 secondes Et la progress bar revient avec une animation fluide Et l'interface ne montre aucun message de confirmation

## 11. Historique survit au changement de réseau

Étant donné que j'ai un historique de 5 contenus en cache Redis

Quand je perds la connexion réseau temporairement Et que je reviens en ligne

Alors l'historique de navigation est toujours disponible Et je peux toujours utiliser "Précédent"

## 12. Historique stocké en Redis avec structure complète

Étant donné que j'ai écouté 3 contenus

Quand je consulte le cache Redis

Alors la structure est: Et l'ordre est du plus récent au plus ancien

## 13. Précédent sur contenu en cours au début (<10s) du premier

Étant donné que je démarre une session avec le contenu "Initial" Et que j'écoute depuis 3 secondes

Quand j'appuie sur "Précédent"

Alors le contenu "Initial" rejoue depuis le début Et aucune erreur n'est générée Et l'historique reste vide

## 14. Compteur de temps respecte les seuils exacts

Étant donné que j'écoute un contenu

Quand le temps écoulé est de 9.9 secondes Et que j'appuie sur "Précédent"

Alors je reviens au contenu précédent

Quand le temps écoulé est de 10.0 secondes Et que j'appuie sur "Précédent"

Alors le contenu actuel rejoue depuis le début

## 15. Progress bar visuelle reflète le retour exact

Étant donné que j'ai écouté le contenu "A" jusqu'à 75% (3min45 sur 5min) Et que je suis passé au contenu "B"

Quand je reviens au contenu "A" via "Précédent"

Alors la progress bar affiche 75% Et l'indicateur de temps affiche "3:45 / 5:00" Et la lecture reprend exactement à cet endroit

## 16. Métadonnées d'historique incluent timestamp précis

Étant donné que j'écoute un contenu "X" pendant 45 secondes à 10:30:15

Quand je passe au contenu suivant

Alors l'historique enregistre:

    | content_id | position_seconds | listened_at |
    |---|---|---|
    | X | 45 | 2026-01-21T10:30:15Z |

Et le timestamp précis permet l'analyse d'usage

## 17. Suppression FIFO respecte l'ordre chronologique

Étant donné un historique de [C1@10:00, C2@10:02, ..., C10@10:20]

Quand j'ajoute C11 à 10:22

Alors C1 (le plus ancien) est supprimé Et l'historique contient [C2@10:02, ..., C11@10:22] Et la taille reste exactement 10 entrées

## 18. 📋 Plan: Comportement selon temps écouté

Étant donné que j'écoute un contenu depuis secondes

Quand j'appuie sur "Précédent"

Alors l'action est

📊 Exemples de données:

temps | comportement
--- | ---
1 | revenir au contenu précédent
5 | revenir au contenu précédent
9 | revenir au contenu précédent
10 | rejouer le contenu actuel depuis 0:00
11 | rejouer le contenu actuel depuis 0:00
30 | rejouer le contenu actuel depuis 0:00
180 | rejouer le contenu actuel depuis 0:00

## 19. 📋 Plan: Positions de reprise exactes

Étant donné que j'écoute un contenu de 10 minutes

Quand j'atteins et passe au suivant Et que je reviens via "Précédent"

Alors la lecture reprend exactement à

📊 Exemples de données:

position
0:15
1:30
3:45
5:00
7:23
9:50

# Commandes vocales CarPlay et Android Auto

> En tant que conducteur avec CarPlay ou Android Auto Je veux utiliser des commandes vocales pour interagir avec l'application Afin de garder les mains sur le volant et les yeux sur la route

25 scénarios (23 standards, 2 plans)

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et qu'un utilisateur est connecté Et que CarPlay ou Android Auto est activé

## 1. Disponibilité des commandes vocales uniquement avec CarPlay/Android Auto

Étant donné que je conduis avec CarPlay activé

Quand je dis "Hey Siri"

Alors Siri est disponible pour les commandes RoadWave

Étant donné que je conduis avec Android Auto activé

Quand je dis "OK Google"

Alors Google Assistant est disponible pour les commandes RoadWave

## 2. Parc automobile compatible avec vocal (30-40% en 2026)

Étant donné que nous sommes en 2026

Quand je consulte les statistiques du parc automobile EU

Alors environ 30-40% des véhicules ont CarPlay ou Android Auto Et ces utilisateurs peuvent utiliser les commandes vocales Et les 60-70% restants utilisent les commandes au volant uniquement

## 3. Commande vocale "Like ce podcast" avec Siri

Étant donné que j'écoute un contenu tagué "Automobile" Et que ma jauge "Automobile" est à 60%

Quand je dis "Hey Siri, like ce podcast"

Alors un like explicite (+2%) est enregistré Et ma jauge "Automobile" passe à 62% Et Siri confirme vocalement "J'ai ajouté ce contenu à vos favoris" Et aucune interaction écran n'est requise

## 4. Commande vocale "Like ce contenu" avec Google Assistant

Étant donné que j'écoute un contenu tagué "Voyage"

Quand je dis "OK Google, like ce contenu"

Alors un like explicite est enregistré (+2%) Et Google Assistant confirme "J'ai liké ce contenu pour vous" Et la commande fonctionne sans toucher l'écran

## 5. Commande vocale "Abonne-moi à ce créateur"

Étant donné que j'écoute un contenu d'un créateur tagué "Automobile" et "Technologie" Et que mes jauges sont à 50% et 45%

Quand je dis "Hey Siri, abonne-moi à ce créateur"

Alors l'abonnement est enregistré Et mes jauges augmentent de 5% chacune (55% et 50%) Et Siri confirme "Vous êtes maintenant abonné à [Nom du créateur]"

## 6. Commande vocale "Passe au contenu suivant"

Étant donné que j'écoute un contenu "A"

Quand je dis "Hey Siri, passe au contenu suivant"

Alors le contenu "B" démarre immédiatement Et la commande a le même effet que le bouton physique "Suivant"

## 7. Commande vocale "Signale ce contenu"

Étant donné que j'écoute un contenu inapproprié

Quand je dis "OK Google, signale ce contenu"

Alors Google Assistant demande "Quelle catégorie ?" Et je réponds vocalement "Spam" Alors le signalement est enregistré avec la catégorie "Spam" Et Google Assistant confirme "J'ai signalé ce contenu"

## 8. Commande vocale avec catégorie de signalement

Étant donné que j'écoute un contenu

Quand je dis "Hey Siri, signale ce contenu pour haine"

Alors le signalement est enregistré avec la catégorie "Haine et violence" Et Siri confirme "J'ai signalé ce contenu pour haine et violence" Et le flux de modération reçoit le signalement

## 9. Liste des catégories de signalement vocales supportées

Étant donné que je dis "signale ce contenu pour [catégorie]"

Quand la catégorie est:

    | Mot-clé vocal | Catégorie mappée |
    |---|---|
    | "haine" | Haine et violence |
    | "sexuel" | Contenu sexuel |
    | "illégalité" | Illégalité |
    | "droits d'auteur" | Droits d'auteur |
    | "spam" | Spam |
    | "fake news" | Désinformation |
    | "autre" | Autre |

Alors le signalement est enregistré avec la bonne catégorie

## 10. Commande vocale non reconnue - fallback

Étant donné que je dis "Hey Siri, super ce podcast"

Quand Siri ne reconnaît pas l'intent RoadWave

Alors Siri répond "Je ne comprends pas cette commande RoadWave" Et elle suggère "Dites 'like ce podcast' ou 'passe au suivant'"

## 11. Commandes vocales disponibles en conduite uniquement

Étant donné que je roule à 50 km/h

Quand j'utilise les commandes vocales

Alors toutes les commandes sont disponibles:

    | Commande | Action |
    |---|---|
    | "Like ce podcast" | Like explicite +2% |
    | "Abonne-moi à ce créateur" | Abonnement +5% |
    | "Passe au suivant" | Contenu suivant |
    | "Reviens au précédent" | Contenu précédent (règle 10s) |
    | "Pause" | Pause lecture |
    | "Reprends la lecture" | Play |
    | "Signale ce contenu" | Signalement |

## 12. Intent iOS personnalisé pour RoadWave

Étant donné que l'app iOS implémente les Intents

Quand je configure les Shortcuts iOS

Alors les intents suivants sont disponibles:

    | Intent Name | Action |
    |---|---|
    | LikeCurrentContentIntent | Like explicite |
    | SubscribeToCreatorIntent | Abonnement |
    | ReportContentIntent | Signalement |
    | SkipToNextContentIntent | Suivant |

Et Siri les reconnaît automatiquement

## 13. Intent Android personnalisé pour RoadWave

Étant donné que l'app Android implémente les Voice Actions

Quand je configure les actions Google Assistant

Alors les actions suivantes sont disponibles:

    | Action Name | Action |
    |---|---|
    | com.roadwave.LIKE_CONTENT | Like explicite |
    | com.roadwave.SUBSCRIBE_CREATOR | Abonnement |
    | com.roadwave.REPORT_CONTENT | Signalement |
    | com.roadwave.SKIP_NEXT | Suivant |

Et Google Assistant les reconnaît

## 14. Confirmation vocale après action réussie

Étant donné que je dis "Hey Siri, like ce podcast"

Quand l'action est enregistrée avec succès

Alors Siri répond immédiatement avec confirmation: Et la réponse est naturelle et concise Et elle ne distrait pas de la conduite

## 15. Gestion d'erreur vocale si action échoue

Étant donné que je dis "Hey Siri, abonne-moi à ce créateur" Et que j'ai atteint la limite de 200 abonnements

Quand Siri essaie d'enregistrer l'abonnement

Alors l'action échoue Et Siri répond "Impossible de s'abonner, limite de 200 abonnements atteinte" Et elle suggère "Désabonnez-vous d'un créateur pour continuer"

## 16. Commandes vocales multilingues (français)

Étant donné que mon Siri est configuré en français

Quand je dis "Hey Siri, j'aime ce podcast"

Alors la commande est reconnue (variante de "like ce podcast")

Quand je dis "Hey Siri, mets une étoile"

Alors la commande est reconnue (variante de "like")

## 17. Implémentation post-MVP (Sprint 5)

Étant donné que les commandes vocales sont une feature Sprint 5

Quand le MVP est lancé

Alors seules les commandes au volant physiques sont disponibles

Quand le Sprint 5 est déployé

Alors les intents iOS/Android sont activés Et les commandes vocales deviennent disponibles

## 18. Priorisation commandes vocales vs boutons physiques

Étant donné que je conduis avec CarPlay Et que j'ai accès aux boutons physiques ET aux commandes vocales

Quand je veux liker un contenu

Alors je peux soit: Et les 3 méthodes sont valides

## 19. Statistiques d'usage des commandes vocales

Étant donné que 100 utilisateurs avec CarPlay utilisent RoadWave

Quand je consulte les analytics

Alors je peux voir:

    | Métrique | Exemple valeur |
    |---|---|
    | Taux d'utilisation commandes vocal | 15% |
    | Commande la plus utilisée | "Like" |
    | Taux de reconnaissance réussie | 92% |
    | Taux d'échec / incompréhension | 8% |

## 20. Feedback haptique désactivé pour commandes vocales

Étant donné que je like un contenu via commande vocale

Quand l'action est enregistrée

Alors aucune vibration haptique n'est déclenchée Et seule la confirmation vocale est donnée

## 21. Badge visuel mis à jour après commande vocale

Étant donné que je dis "Hey Siri, like ce podcast"

Quand l'action est enregistrée

Alors le badge "♥ Ajouté à vos favoris" s'affiche sur l'écran CarPlay Et le cœur devient rouge plein dans l'interface Et la mise à jour est visible même sans toucher l'écran

## 22. Commandes vocales avec contenu sans créateur

Étant donné que j'écoute un contenu anonyme (créateur supprimé)

Quand je dis "Hey Siri, abonne-moi à ce créateur"

Alors Siri répond "Ce créateur n'est plus disponible" Et aucun abonnement n'est enregistré

## 23. Limitation temporelle des commandes vocales

Étant donné que je dis "Hey Siri, like ce podcast" Et que le contenu change 1 seconde après

Quand Siri traite la commande 2 secondes plus tard

Alors la commande s'applique au contenu qui était en lecture au moment de la commande Et non au contenu actuel (système de timestamp)

## 24. 📋 Plan: Commandes vocales avec différents assistants

Étant donné que j'utilise

Quand je dis

Alors l'action est exécutée Et la confirmation est

📊 Exemples de données:

assistant | commande | action | confirmation
--- | --- | --- | ---
Siri | "Like ce podcast" | Like +2% | "Ajouté à vos favoris"
Google Assistant | "Like ce contenu" | Like +2% | "J'ai liké ce contenu"
Siri | "Abonne-moi à ce créateur" | Abonnement +5% | "Vous êtes abonné"
Google Assistant | "Abonne-moi à ce créateur" | Abonnement +5% | "Abonnement enregistré"
Siri | "Signale ce contenu" | Signalement | "J'ai signalé ce contenu"
Google Assistant | "Signale ce contenu" | Signalement | "Contenu signalé"

## 25. 📋 Plan: Mapping catégories signalement vocal

Étant donné que je dis "signale ce contenu pour "

Quand est reconnu

Alors la catégorie mappée est

📊 Exemples de données:

mot_cle | categorie
--- | ---
haine | Haine et violence
violence | Haine et violence
sexuel | Contenu sexuel
porno | Contenu sexuel
illégal | Illégalité
terrorisme | Illégalité
copyright | Droits d'auteur
droits auteur | Droits d'auteur
spam | Spam
fake news | Désinformation
fausse info | Désinformation

# Commandes au volant et interactions simplifiées

> En tant que conducteur en sécurité Je veux utiliser uniquement les commandes simplifiées au volant Afin de naviguer sans distraction et en toute sécurité

21 scénarios (19 standards, 2 plans)

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et qu'un utilisateur est connecté Et que l'application est connectée via CarPlay ou Android Auto

## 1. Trois commandes disponibles au volant uniquement

Étant donné que je conduis à 50 km/h

Quand je consulte les commandes physiques disponibles

Alors seules 3 actions sont disponibles:

    | Commande | Action |
    |---|---|
    | Suivant | Passer au contenu suivant |
    | Précédent | Revenir au précédent (règle 10s) |
    | Play/Pause | Pause/reprise avec fade 0.3s |

Et aucune commande complexe n'est proposée

## 2. Commande "Suivant" au volant

Étant donné que j'écoute un contenu "A"

Quand j'appuie sur le bouton physique "Suivant" au volant

Alors le contenu "B" démarre immédiatement Et aucune action supplémentaire n'est requise Et l'interface ne demande aucune confirmation

## 3. Commande "Précédent" au volant respecte règle 10s

Étant donné que j'écoute un contenu depuis 5 secondes

Quand j'appuie sur "Précédent" au volant

Alors je reviens au contenu précédent (règle <10s)

Étant donné que j'écoute un contenu depuis 15 secondes

Quand j'appuie sur "Précédent" au volant

Alors le contenu actuel rejoue depuis le début (règle ≥10s)

## 4. Commande "Play/Pause" avec fade audio

Étant donné qu'un contenu est en lecture

Quand j'appuie sur "Pause" au volant

Alors la lecture se met en pause avec un fade out de 0.3 secondes Et la position de lecture est sauvegardée

Quand j'appuie sur "Play" au volant

Alors la lecture reprend avec un fade in de 0.3 secondes Et la reprise se fait à la position exacte

## 5. Aucune commande complexe supportée

Étant donné que je conduis

Quand j'essaie un appui long sur "Suivant"

Alors l'action n'est pas détectée (non supporté iOS/Android)

Quand j'essaie un double-appui sur "Pause"

Alors l'action n'est pas détectée Et seules les actions simples (clic simple) fonctionnent

## 6. Compatibilité 100% tous véhicules

Étant donné que je conduis une voiture avec commandes basiques Et que mon véhicule a seulement Suivant/Précédent/Pause

Quand j'utilise RoadWave

Alors toutes les fonctions essentielles sont accessibles Et je n'ai pas besoin de boutons supplémentaires

## 7. Feedback visuel discret après action

Étant donné que j'appuie sur "Suivant"

Quand le contenu change

Alors l'interface CarPlay/Android Auto affiche le nouveau titre Et aucune popup ne bloque la vue Et le changement est fluide et immédiat

## 8. Like automatique renforcé après écoute ≥80%

Étant donné que j'écoute un contenu de 5 minutes tagué "Automobile"

Quand j'écoute pendant 4 minutes 30 secondes (90%)

Alors un like automatique renforcé (+2 points) est enregistré Et un badge discret "♥ Ajouté à vos favoris" s'affiche 2 secondes Et aucune action manuelle n'est requise

## 9. Like automatique standard après écoute 30-79%

Étant donné que j'écoute un contenu de 5 minutes tagué "Voyage"

Quand j'écoute pendant 2 minutes (40%) Et que j'appuie sur "Suivant"

Alors un like automatique standard (+1 point) est enregistré Et un badge discret s'affiche brièvement Et je peux continuer à conduire sans interruption

## 10. Signal négatif après skip rapide <10s

Étant donné que j'écoute un contenu tagué "Politique"

Quand j'appuie sur "Suivant" après seulement 5 secondes

Alors un signal négatif (-0.5 point) est enregistré Et la jauge "Politique" diminue légèrement Et aucun message n'est affiché (transparence)

## 11. Pas de like si écoute <30%

Étant donné que j'écoute un contenu de 10 minutes

Quand j'écoute pendant 2 minutes (20%) Et que j'appuie sur "Suivant"

Alors aucun like n'est enregistré Et les jauges ne changent pas Et le système considère l'écoute comme neutre

## 12. Badge de feedback visuel disparaît après 2 secondes

Étant donné que je reçois un like automatique

Quand le badge "♥ Ajouté à vos favoris" apparaît

Alors il reste visible 2 secondes en bas de l'écran Et il disparaît automatiquement sans action Et il ne bloque pas la vue de la route

## 13. Tracking du temps d'écoute précis côté client

Étant donné que je démarre la lecture d'un contenu

Quand le player audio iOS/Android enregistre le temps

Alors le startTime est enregistré à la milliseconde

Quand j'arrête la lecture (Suivant, Pause, ou fin)

Alors la durée exacte écoutée est calculée Et le pourcentage (durée / durée_totale * 100) est envoyé à l'API

## 14. API reçoit les événements d'écoute pour calcul

Étant donné que j'écoute un contenu de 5 minutes à 80%

Quand l'événement est envoyé à l'API

Alors le backend reçoit: Et le backend calcule le like automatique (+2 points) Et les jauges sont mises à jour immédiatement (Redis + PostgreSQL)

## 15. Actions différentes selon arrêt du contenu

Étant donné que j'écoute un contenu

Quand j'appuie sur "Suivant"

Alors l'action envoyée est "skipped"

Quand le contenu se termine naturellement

Alors l'action envoyée est "completed"

Quand j'appuie sur "Pause"

Alors l'action envoyée est "paused" Et le backend traite chaque action différemment

## 16. Calcul immédiat côté backend sans délai

Étant donné que l'API reçoit un événement d'écoute

Quand le backend traite l'événement

Alors les jauges sont mises à jour immédiatement (< 100ms) Et les nouvelles recommandations utilisent les valeurs actualisées Et il n'y a aucun batch différé

## 17. Compatibilité iOS avec AVPlayer

Étant donné que l'app iOS utilise AVPlayer

Quand les commandes physiques sont interceptées

Alors les événements MPRemoteCommandCenter sont capturés:

    | Commande | Événement iOS |
    |---|---|
    | Suivant | nextTrackCommand |
    | Précédent | previousTrackCommand |
    | Play/Pause | playCommand / pauseCommand |

Et le tracking du temps utilise CMTime

## 18. Compatibilité Android avec MediaSession

Étant donné que l'app Android utilise MediaPlayer

Quand les commandes physiques sont interceptées

Alors les événements MediaSession sont capturés:

    | Commande | Action Android |
    |---|---|
    | Suivant | ACTION_SKIP_TO_NEXT |
    | Précédent | ACTION_SKIP_TO_PREVIOUS |
    | Play/Pause | ACTION_PLAY / ACTION_PAUSE |

Et le tracking du temps utilise SystemClock.elapsedRealtime()

## 19. Sécurité maximale - pas de distraction

Étant donné que je conduis à 80 km/h

Quand j'utilise RoadWave avec les commandes au volant

Alors je n'ai jamais besoin de regarder mon téléphone Et je n'ai jamais besoin de toucher l'écran CarPlay/Android Auto Et toutes les actions sont accessibles via boutons physiques Et les likes sont enregistrés automatiquement

## 20. 📋 Plan: Calcul du like automatique selon pourcentage

Étant donné que j'écoute un contenu tagué "Sport"

Quand j'écoute pendant %

Alors le like automatique est Et l'impact sur la jauge est

📊 Exemples de données:

pourcentage | type | points
--- | --- | ---
10 | aucun | 0
25 | aucun | 0
29 | aucun | 0
30 | standard | +1
50 | standard | +1
79 | standard | +1
80 | renforcé | +2
90 | renforcé | +2
100 | renforcé | +2

## 21. 📋 Plan: Signal négatif uniquement si skip très rapide

Étant donné que j'écoute un contenu

Quand je skip après secondes

Alors le signal est Et l'impact est

📊 Exemples de données:

secondes | type | points
--- | --- | ---
3 | négatif | -0.5
5 | négatif | -0.5
9 | négatif | -0.5
10 | neutre | 0
15 | neutre | 0
30 | neutre | 0

# File d'attente et commande "Suivant"

> En tant qu'auditeur en déplacement Je veux que l'application pré-calcule intelligemment les prochains contenus Afin d'avoir une navigation fluide sans latence

20 scénarios (19 standards, 1 plan)

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et qu'un utilisateur est connecté Et que la géolocalisation est activée

## 1. Pré-calcul initial de 5 contenus en cache

Étant donné que je viens de démarrer l'application Et que je suis situé à Paris (48.8566, 2.3522) Et que je suis en mode voiture (vitesse ≥ 5 km/h)

Quand l'application initialise la lecture

Alors une file d'attente de 5 contenus est pré-calculée Et la file est stockée en cache Redis avec la clé "user:{user_id}:queue" Et les métadonnées incluent ma position, le timestamp de calcul et le mode Et le cache a un TTL de 15 minutes

## 2. Commande "Suivant" sans latence

Étant donné qu'une file d'attente de 5 contenus est en cache Et que j'écoute actuellement le contenu "A"

Quand j'appuie sur le bouton "Suivant"

Alors le contenu suivant démarre immédiatement (< 100ms) Et le contenu est retiré de la file d'attente Et il reste 4 contenus dans la file

## 3. Recalcul automatique après déplacement >10km

Étant donné que la file a été calculée à Paris (48.8566, 2.3522) Et que j'ai 5 contenus en cache

Quand je me déplace à Versailles (48.8049, 2.1204) soit 12km

Alors la file d'attente est invalidée automatiquement Et une nouvelle file de 5 contenus est recalculée Et elle est basée sur ma nouvelle position

## 4. Recalcul automatique toutes les 10 minutes

Étant donné qu'une file a été calculée il y a 10 minutes Et que ma position n'a pas changé

Quand le timer de rafraîchissement expire

Alors une nouvelle file de 5 contenus est recalculée Et les anciens contenus non écoutés sont remplacés Et les nouveaux contenus publiés depuis sont inclus

## 5. Recalcul quand il reste moins de 3 contenus

Étant donné qu'il reste 3 contenus dans ma file d'attente

Quand j'appuie sur "Suivant"

Alors il reste 2 contenus Et un recalcul asynchrone est déclenché en arrière-plan Et 3 nouveaux contenus sont ajoutés à la file Et la file contient maintenant 5 contenus

## 6. Insertion prioritaire d'un contenu géolocalisé en mode voiture

Étant donné que j'ai une file de 5 contenus pré-calculée Et que je suis en mode voiture Et que je me déplace à 50 km/h vers un point avec contenu géolocalisé

Quand je suis à 98m du point (ETA = 7 secondes)

Alors une notification est envoyée (icône + compteur 7→1 + son) Et je dois appuyer sur "Suivant" dans les 7 secondes pour valider

Quand j'appuie sur "Suivant"

Alors un décompte de 5 secondes démarre Et après 5 secondes, le contenu géolocalisé s'insère et démarre Et il remplace le contenu actuel dans la lecture

## 7. Contenu géolocalisé ignoré est perdu (cooldown activé)

Étant donné qu'une notification géolocalisée est affichée (compteur 7→1)

Quand je ne clique pas sur "Suivant" pendant les 7 secondes

Alors la notification disparaît Et le contenu géolocalisé est perdu (pas d'insertion dans la file) Et un cooldown de 10 minutes est activé Et aucune nouvelle notification géolocalisée ne sera envoyée pendant 10 minutes

## 8. Validation d'une notification géolocalisée

Étant donné qu'une notification géolocalisée est affichée (compteur à 5) Et que j'écoute un podcast

Quand j'appuie sur "Suivant"

Alors le compteur bascule à "5" (décompte final) Et le podcast actuel continue de jouer Et après 5 secondes, le contenu géolocalisé démarre Et le podcast est mis en pause et sauvegardé dans l'historique

## 9. Invalidation immédiate après modification des préférences

Étant donné que j'ai une file de 5 contenus en cache Et que ma vitesse GPS est de 5 km/h (piéton)

Quand je modifie mes curseurs de préférences (géo/découverte/politique)

Alors la file d'attente est invalidée immédiatement Et une nouvelle file est recalculée avec les nouvelles préférences Et les anciens contenus en cache sont supprimés

## 10. Blocage modification préférences en conduite (>10 km/h)

Étant donné que ma vitesse GPS est de 50 km/h (en voiture)

Quand j'essaie d'accéder aux réglages de préférences

Alors l'interface affiche "Paramètres verrouillés en conduite" Et je ne peux pas modifier les curseurs géo/découverte/politique Et un message "Arrêtez-vous pour modifier vos préférences" s'affiche

## 11. Invalidation lors du démarrage d'un live suivi

Étant donné que je suis abonné au créateur "RadioVoyage" Et que j'ai une file de 5 contenus en cache Et que je suis dans la zone géographique du créateur

Quand le créateur "RadioVoyage" démarre une radio live

Alors je reçois une notification push Et le contenu live s'insère en tête de la file d'attente Et la file d'attente est recalculée

## 12. Métadonnées de cache Redis

Étant donné qu'une file d'attente est calculée

Quand elle est stockée dans Redis

Alors la clé est "user:{user_id}:queue" Et les métadonnées incluent:

    | champ | valeur |
    |---|---|
    | last_lat | 48.8566 |
    | last_lon | 2.3522 |
    | computed_at | 2026-01-21T10:30:00Z |
    | mode | voiture |

Et le TTL est de 15 minutes (900 secondes)

## 13. Contenu géolocalisé remplace le contenu actuel (pas d'insertion en file)

Étant donné que j'écoute le contenu C2 de ma file [C1, C2, C3, C4, C5] Et qu'une notification géolocalisée "Tour Eiffel" est déclenchée

Quand je valide la notification Et que le décompte de 5s se termine

Alors le contenu "Tour Eiffel" remplace C2 et démarre Et C2 est sauvegardé dans l'historique de navigation Et la file reste [C3, C4, C5] (pas de contenu retiré) Et quand "Tour Eiffel" se termine, C3 démarre

## 14. Invalidation après déplacement exactement 10km

Étant donné que la file a été calculée à une position donnée

Quand je me déplace d'exactement 10.0 km

Alors la file d'attente n'est PAS invalidée (seuil strict >10km) Et les contenus en cache restent valides

Quand je me déplace de 10.1 km supplémentaires (total 10.1km)

Alors la file d'attente est invalidée Et une nouvelle file est calculée

## 15. Rafraîchissement exactement après 10 minutes

Étant donné qu'une file a été calculée à 10:00:00

Quand l'heure actuelle est 10:10:00

Alors le timer de rafraîchissement expire Et une nouvelle file de 5 contenus est recalculée Et le timestamp "computed_at" est mis à jour

## 16. Recalcul asynchrone non-bloquant

Étant donné qu'il reste 2 contenus dans la file Et que j'appuie sur "Suivant"

Quand le recalcul asynchrone démarre

Alors la lecture du contenu actuel n'est pas interrompue Et le recalcul se fait en arrière-plan Et les nouveaux contenus sont ajoutés dès disponibles (< 500ms) Et l'utilisateur ne perçoit aucune latence

## 17. Notification basée sur ETA (pas distance fixe)

Étant donné qu'un contenu géolocalisé existe à un point GPS Et que je roule à 130 km/h

Quand je suis à 252m du point (ETA = 7 secondes)

Alors une notification est envoyée

Quand je suis à 300m du point (ETA = 8 secondes)

Alors aucune notification n'est envoyée (ETA >7s)

## 18. 📋 Plan: Différentes distances de déplacement et invalidation

Étant donné qu'une file a été calculée à une position donnée

Quand je me déplace de km

Alors la file est

📊 Exemples de données:

distance | action
--- | ---
5 | conservée
9.9 | conservée
10.0 | conservée
10.1 | invalidée et recalculée
15 | invalidée et recalculée
50 | invalidée et recalculée

## 19. Quota de 6 contenus géolocalisés par heure

Étant donné que j'ai validé 6 notifications géolocalisées dans la dernière heure

Quand un 7ème contenu géolocalisé est détecté (ETA 7s)

Alors aucune notification n'est envoyée Et le quota horaire est respecté

## 20. Mode piéton - pas de notification avec compteur 7s

Étant donné que je suis en mode piéton (vitesse <5 km/h) Et qu'un audio-guide géolocalisé existe à 150m

Quand je passe dans le rayon de 200m

Alors une notification push système est envoyée Et aucun compteur 7s n'est affiché Et je peux ouvrir l'app en tapant sur la notification

# Lecture en boucle et enchaînement automatique

> En tant qu'auditeur Je veux que les contenus s'enchaînent automatiquement avec un délai paramétrable Afin d'avoir une expérience fluide sans interruption

27 scénarios (24 standards, 3 plans)

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et qu'un utilisateur est connecté

## 1. Passage automatique après 2 secondes (mode standard)

Étant donné que j'écoute un contenu "A" en mode standard

Quand la lecture se termine naturellement

Alors un timer de 2 secondes démarre Et un overlay s'affiche: "Contenu suivant dans 2s..." Et une barre de décompte visuelle s'affiche

Quand le timer atteint 0

Alors le contenu "B" démarre automatiquement Et l'overlay disparaît

## 2. Passage automatique après 1 seconde (mode Kids)

Étant donné que je suis en mode Kids Et que j'écoute un contenu pour enfants

Quand la lecture se termine

Alors un timer de 1 seconde démarre Et le message "Contenu suivant dans 1s..." s'affiche

Quand le timer expire

Alors le contenu suivant démarre automatiquement

## 3. Passage immédiat après une radio live (0 seconde)

Étant donné que j'écoute une radio live

Quand le créateur arrête la diffusion

Alors le passage au contenu suivant est immédiat (0s de délai) Et aucun overlay de décompte n'est affiché Et la transition est fluide

## 4. Annulation du passage automatique

Étant donné qu'un contenu se termine Et que le timer de 2 secondes démarre

Quand je clique sur "Rester sur ce contenu" pendant le décompte

Alors le timer est annulé Et le contenu actuel reste en pause à la fin Et le contenu suivant n'est pas lancé

## 5. Insertion de publicité pendant le délai de transition

Étant donné que j'ai écouté 4 contenus sans publicité Et que le 5ème contenu se termine

Quand le délai de 2 secondes démarre

Alors une publicité s'insère dans la file d'attente Et le message devient "Publicité (15s)" Et la publicité démarre après les 2 secondes Et elle ne coupe jamais un contenu en cours

## 6. Fréquence de publicité paramétrable admin

Étant donné que la fréquence pub est configurée à "1/5 contenus"

Quand j'écoute 10 contenus

Alors 2 publicités sont insérées (après les contenus 5 et 10)

Étant donné que l'admin change la fréquence à "1/3 contenus"

Quand j'écoute 9 contenus

Alors 3 publicités sont insérées (après les contenus 3, 6 et 9)

## 7. Publicité skippable après 5 secondes par défaut

Étant donné qu'une publicité de 30 secondes démarre Et que le délai minimal de visionnage est configuré à 5 secondes

Quand j'écoute pendant 3 secondes

Alors le bouton "Passer" n'est pas encore visible

Quand j'atteins 5 secondes d'écoute

Alors le bouton "Passer" apparaît Et je peux cliquer pour passer au contenu suivant

## 8. Délai minimal de publicité paramétrable admin

Étant donné qu'une publicité démarre Et que l'admin a configuré le délai à 10 secondes

Quand j'écoute pendant 9 secondes

Alors le bouton "Passer" n'est pas visible

Quand j'atteins 10 secondes

Alors le bouton "Passer" apparaît Et je peux skipper la publicité

## 9. Like et abonnement autorisés sur une publicité

Étant donné qu'une publicité est en lecture

Quand je clique sur le bouton cœur (véhicule arrêté)

Alors la publicité reçoit un like (+2% jauges tags pub)

Quand je clique sur "S'abonner" au créateur de la pub

Alors je suis abonné (+5% jauges tags créateur) Et le créateur de pub bénéficie de l'engagement

## 10. Métriques d'engagement publicité trackées

Étant donné qu'une publicité de 30s est diffusée à 100 auditeurs

Quand 40 auditeurs écoutent entièrement (30s) Et que 50 auditeurs skippent après 10s Et que 10 auditeurs skippent avant 5s

Alors les métriques sont:

    | Métrique | Valeur |
    |---|---|
    | Taux d'écoute complète | 40% |
    | Taux de skip après seuil | 50% |
    | Taux de skip immédiat | 10% |
    | Durée moyenne d'écoute | 18s |

## 11. Message "Aucun contenu disponible" si file vide

Étant donné que la file d'attente est vide Et qu'aucun contenu n'est disponible dans ma zone

Quand le contenu actuel se termine

Alors un message s'affiche: "Aucun contenu disponible dans cette zone" Et une proposition apparaît: "Élargir la zone de recherche ?" Et un bouton "Élargir" est disponible Et la lecture se met en pause automatiquement

## 12. Élargissement automatique de la zone de recherche

Étant donné que le message "Aucun contenu disponible" s'affiche

Quand je clique sur "Élargir la zone"

Alors l'algorithme relance une recherche avec rayon +50km Et une notification "Recherche élargie à 50km" s'affiche Et la file d'attente est recalculée Et la lecture reprend automatiquement

## 13. Refus d'élargissement laisse en pause

Étant donné que le message "Aucun contenu disponible" s'affiche

Quand je clique sur "Annuler"

Alors la lecture reste en pause Et l'interface affiche "En attente de contenu" Et je peux manuellement naviguer ou chercher du contenu

## 14. Retry avec backoff exponentiel en cas d'échec réseau

Étant donné que le contenu suivant échoue au chargement

Quand la première tentative échoue

Alors le système retente après 1 seconde (backoff 1s)

Quand la 2ème tentative échoue

Alors le système retente après 2 secondes (backoff 2s)

Quand la 3ème tentative échoue

Alors le système retente après 4 secondes (backoff 4s) Et après 3 échecs totaux, le système bascule en mode offline

## 15. Basculement mode offline après 3 échecs réseau

Étant donné que j'ai eu 3 échecs de chargement consécutifs

Quand le 3ème échec se produit

Alors un message "Connexion instable, basculement mode offline" s'affiche Et la lecture continue avec les contenus téléchargés uniquement Et les contenus en ligne sont temporairement désactivés

Quand la connexion revient

Alors le mode en ligne est automatiquement rétabli

## 16. Overlay de décompte avec barre visuelle

Étant donné qu'un contenu se termine

Quand le timer de 2 secondes démarre

Alors un overlay semi-transparent s'affiche en bas de l'écran Et le texte "Contenu suivant dans 2s..." est visible Et une barre de progression décroît de 100% à 0% en 2 secondes Et la couleur de la barre passe de vert à orange Et l'overlay disparaît automatiquement après le décompte

## 17. Bouton "Rester sur ce contenu" pendant décompte

Étant donné que le décompte de 2 secondes est actif

Quand l'overlay s'affiche

Alors un bouton "Rester sur ce contenu" est visible Et il est cliquable pendant les 2 secondes

Quand je clique dessus

Alors le timer est annulé immédiatement Et l'overlay disparaît Et le contenu actuel reste affiché en pause

## 18. Pas d'interruption d'un contenu en cours

Étant donné que j'écoute un contenu de 10 minutes Et que je suis à 5 minutes de lecture

Quand une publicité devrait s'insérer (fréquence 1/5)

Alors la publicité n'interrompt jamais le contenu en cours Et elle attend la fin du contenu actuel Et elle s'insère pendant le délai de transition (2s)

## 19. Publicités uniquement pour utilisateurs gratuits

Étant donné que je suis un utilisateur gratuit

Quand j'écoute 5 contenus

Alors une publicité est insérée après le 5ème contenu

Étant donné que je passe en compte Premium

Quand j'écoute 100 contenus

Alors aucune publicité n'est insérée Et l'enchaînement est direct (2s de transition seulement)

## 20. Message clair pour l'utilisateur lors de la publicité

Étant donné qu'une publicité va démarrer

Quand le délai de transition démarre

Alors le message affiché est: "Publicité (15s)" Et la durée totale de la pub est indiquée Et l'utilisateur sait qu'il s'agit d'une pub Et la transparence est maximale

## 21. Transition fluide entre contenus sans coupure

Étant donné qu'un contenu se termine Et que le suivant est pré-chargé en cache

Quand le timer de 2s expire

Alors la transition audio utilise un crossfade de 0.3s Et il n'y a aucun blanc ou coupure Et l'expérience est fluide

## 22. Gestion des erreurs de chargement avec retry

Étant donné que le contenu suivant échoue au chargement

Quand la 1ère tentative échoue

Alors une notification "Chargement..." s'affiche Et le système retente automatiquement

Quand la 2ème tentative réussit

Alors la lecture démarre normalement Et aucune action utilisateur n'est requise

## 23. Mode offline après échecs multiples

Étant donné que j'ai 50 contenus téléchargés en mode offline Et que j'ai eu 3 échecs réseau consécutifs

Quand le mode offline s'active

Alors seuls les contenus téléchargés sont disponibles Et un badge "Mode offline" s'affiche en haut de l'écran Et la lecture continue sans interruption

## 24. Compteur de contenus avant prochaine publicité

Étant donné que la fréquence pub est 1/5 contenus Et que j'ai écouté 3 contenus depuis la dernière pub

Quand je consulte l'interface

Alors un indicateur discret affiche "2 contenus avant pub" Et l'utilisateur sait quand attendre la prochaine publicité

## 25. 📋 Plan: Délai de transition selon mode

Étant donné que je suis en mode

Quand un contenu se termine

Alors le délai de transition est secondes Et le message affiché est

📊 Exemples de données:

mode | delai | message
--- | --- | ---
Standard | 2 | "Contenu suivant dans 2s..."
Kids | 1 | "Contenu suivant dans 1s..."
Live | 0 | (aucun message)

## 26. 📋 Plan: Fréquence d'insertion des publicités

Étant donné que la fréquence pub est configurée à

Quand j'écoute contenus

Alors publicités sont insérées

📊 Exemples de données:

frequence | contenus | pubs
--- | --- | ---
1/3 | 6 | 2
1/3 | 9 | 3
1/5 | 10 | 2
1/5 | 15 | 3
1/7 | 14 | 2
1/7 | 21 | 3

## 27. 📋 Plan: Backoff exponentiel retry

Étant donné que le chargement échoue

Quand je suis à la tentative

Alors le délai de retry est secondes

📊 Exemples de données:

tentative | delai
--- | ---
1 | 1
2 | 2
3 | 4

# Partage de contenu

> En tant qu'utilisateur de RoadWave Je veux pouvoir partager du contenu audio Afin de faire découvrir l'application à d'autres personnes

22 scénarios (20 standards, 2 plans)

> Contexte commun à tous les scénarios

> Étant donné que l'application RoadWave est démarrée Et que l'utilisateur "jean@example.com" est connecté

## 1. Bouton partager disponible dans le player en lecture

Étant donné que le contenu "Balade à Paris" est en cours de lecture

Quand l'utilisateur consulte les contrôles du player

Alors le bouton "Partager" ⬆️ est visible

## 2. Bouton partager disponible sur la page profil créateur

Étant donné que l'utilisateur consulte le profil de "@paris_stories"

Quand l'utilisateur consulte un contenu dans la liste

Alors le bouton "Partager" est disponible pour chaque contenu

## 3. Bouton partager dans la liste de recherche

Étant donné que l'utilisateur effectue une recherche "voyage paris"

Quand l'utilisateur ouvre le menu contextuel d'un résultat

Alors l'option "Partager" est disponible

## 4. Bouton partager dans l'historique personnel

Étant donné que l'utilisateur consulte son historique d'écoute

Quand l'utilisateur sélectionne un contenu de l'historique

Alors le bouton "Partager" est accessible

## 5. 📋 Plan: Menu de partage avec options multiples

Étant donné que le contenu "" est disponible

Quand l'utilisateur clique sur le bouton "Partager"

Alors le menu natif OS s'ouvre Et les options suivantes sont disponibles:

    | option |
    |---|
    | Copier le lien |
    | WhatsApp |
    | Email |
    | SMS |
    | Plus... |

📊 Exemples de données:

contenu
Balade à Paris
Secrets de Montmartre

## 6. Génération du lien de partage

Étant donné un contenu avec l'ID "content_12345"

Quand l'utilisateur copie le lien de partage

Alors le lien généré est "https://roadwave.fr/share/c/content_12345"

## 7. Ouverture du lien partagé avec l'application installée (Deep link)

Étant donné que l'application RoadWave est installée sur l'appareil Et qu'un lien "https://roadwave.fr/share/c/content_12345" est partagé

Quand l'utilisateur clique sur le lien

Alors l'application RoadWave s'ouvre automatiquement Et le contenu "content_12345" commence à jouer

## 8. Ouverture du lien partagé sans l'application installée (Web player)

Étant donné que l'application RoadWave n'est pas installée Et qu'un lien "https://roadwave.fr/share/c/content_12345" est partagé

Quand l'utilisateur clique sur le lien

Alors une page web responsive s'affiche Et le web player HTML5 est visible Et les boutons de téléchargement App Store et Google Play sont affichés

## 9. Contenu de la page web de partage

Étant donné un contenu public avec les métadonnées suivantes:

    | champ | valeur |
    |---|---|
    | titre | Balade à Paris |
    | créateur | @paris_stories |
    | durée | 12 min |
    | écoutes | 2300 |
    | localisation | Paris 5e |
    | type_geo | Ancré |
    | tags | Voyage, Histoire |

Quand la page de partage est affichée

Alors la page contient:

    | élément |
    |---|
    | Cover image 16:9 |
    | Titre "Balade à Paris" |
    | "@paris_stories" |
    | "12 min · 🎧 2.3K" |
    | "📍 Paris 5e · Ancré" |
    | "🏷️ #Voyage #Histoire" |
    | Description |
    | Player HTML5 |
    | Bouton App Store |
    | Bouton Google Play |

## 10. Métadonnées Open Graph pour partage social

Étant donné un contenu "Balade à Paris" par "@paris_stories"

Quand la page de partage est générée

Alors les métadonnées Open Graph incluent:

    | propriété | valeur |
    |---|---|
    | og:title | Balade à Paris - RoadWave |
    | og:description | Écoutez ce contenu par @paris_stories |
    | og:type | music.song |
    | og:site_name | RoadWave |
    | twitter:card | player |

Et l'aperçu s'affiche correctement sur WhatsApp Et l'aperçu s'affiche correctement sur Facebook Et l'aperçu s'affiche correctement sur Twitter

## 11. 📋 Plan: Deep linking par plateforme

Étant donné que l'application RoadWave est installée sur Et qu'un lien de partage est ouvert

Quand le système détecte l'application

Alors l'application s'ouvre via

📊 Exemples de données:

plateforme | mécanisme
--- | ---
iOS | Universal Links
Android | App Links

## 12. Fallback URL scheme pour deep linking

Étant donné que les App Links ne fonctionnent pas

Quand le système tente d'ouvrir le contenu

Alors l'URL scheme "roadwave://content/content_12345" est utilisé

## 13. Badge Premium visible sur le lien partagé

Étant donné un contenu Premium "Visite VIP Louvre"

Quand l'utilisateur non-premium clique sur le lien partagé

Alors la page web affiche le badge "👑 Contenu Premium"

## 14. Preview 30 secondes d'un contenu Premium partagé

Étant donné un contenu Premium "Visite VIP Louvre" de 15 minutes Et qu'un utilisateur non-premium ouvre le lien partagé

Quand le player démarre automatiquement

Alors l'audio joue pendant 30 secondes exactement Et un fade out de 2 secondes est appliqué Et un overlay "Contenu réservé Premium" s'affiche après 32 secondes

## 15. Contenu de l'overlay paywall Premium

Étant donné qu'un contenu Premium a atteint la limite de 30 secondes

Quand l'overlay paywall s'affiche

Alors le texte suivant est visible:

## 16. Actions disponibles sur l'overlay Premium

Étant donné que l'overlay paywall Premium est affiché

Quand l'utilisateur consulte les options

Alors les actions suivantes sont disponibles:

    | action | comportement |
    |---|---|
    | Passer Premium | Redirection vers paiement Mangopay web |
    | Télécharger l'app | Redirection vers App Store/Google Play |
    | Rejouer les 30 premières sec | Relecture illimitée du preview |

## 17. Relecture illimitée du preview Premium

Étant donné un contenu Premium partagé Et que l'utilisateur a écouté les 30 premières secondes

Quand l'utilisateur clique sur "Rejouer"

Alors les 30 premières secondes sont rejouées Et cette action est possible de manière illimitée

## 18. Tracking des partages Premium

Étant donné un créateur "@guide_louvre" avec un contenu Premium

Quand son contenu est partagé

Alors les métriques suivantes sont enregistrées:

    | métrique | valeur |
    |---|---|
    | Partages Premium | +1 |
    | Ouvertures lien | compteur |
    | Conversions Premium | si souscription |

## 19. Rémunération créateur sur conversion Premium via partage

Étant donné un contenu Premium partagé par "@guide_louvre"

Quand un utilisateur s'abonne via le lien partagé

Alors le créateur reçoit 70% des revenus de cet abonnement Et la conversion est trackée dans son dashboard

## 20. Partage d'un contenu supprimé

Étant donné qu'un lien de partage "https://roadwave.fr/share/c/deleted_content" est ouvert Et que le contenu n'existe plus

Quand la page web se charge

Alors un message "Ce contenu n'est plus disponible" s'affiche Et les boutons de téléchargement de l'app sont affichés

## 21. Partage d'un contenu en attente de modération

Étant donné un contenu en cours de validation modération

Quand un lien de partage est ouvert

Alors le message "Ce contenu est en cours de validation" s'affiche

## 22. Génération du lien hors connexion

Étant donné que l'utilisateur n'a pas de connexion réseau

Quand l'utilisateur tente de partager un contenu

Alors le lien est copié dans le presse-papiers Et un message "Lien copié (nécessite connexion pour ouvrir)" s'affiche

# Avantages Premium

> En tant qu'abonné Premium Je veux bénéficier d'avantages exclusifs Afin de profiter d'une expérience audio améliorée sans publicité

37 scénarios

> Contexte commun à tous les scénarios

> Étant donné que je suis connecté à l'application RoadWave

## 1. Utilisateur gratuit voit 1 publicité tous les 5 contenus

Étant donné que je suis un utilisateur gratuit

Quand j'écoute ma file de contenus

Alors je vois une publicité tous les 5 contenus Et la publicité dure 30 secondes en moyenne Et je ne peux pas la skip

## 2. Utilisateur Premium ne voit aucune publicité

Étant donné que je suis un utilisateur Premium

Quand j'écoute mes contenus

Alors aucune publicité n'est diffusée Et je passe directement d'un contenu à l'autre Et l'expérience d'écoute est fluide et ininterrompue

## 3. Badge "0 publicité" sur page Premium

Étant donné que je consulte la page des avantages Premium

Quand je lis la liste des avantages

Alors je vois en premier: Et c'est l'argument principal mis en avant

## 4. Utilisateur gratuit voit contenus Premium bloqués

Étant donné que je suis un utilisateur gratuit

Quand je consulte les contenus d'un créateur

Alors je vois les contenus marqués Premium avec badge 👑 Mais je ne peux pas les lire (overlay bloquant)

## 5. Utilisateur Premium accède à tous les contenus exclusifs

Étant donné que je suis un utilisateur Premium

Quand je consulte les contenus d'un créateur

Alors tous les contenus Premium sont accessibles Et je peux les lire sans restriction Et j'ai accès à 100% du catalogue (gratuit + Premium)

## 6. Nombre de contenus Premium disponibles

Étant donné que je suis Premium

Quand je consulte les statistiques

Alors je vois combien de contenus Premium sont disponibles sur la plateforme Et par exemple: "8,547 contenus Premium exclusifs disponibles" Et cela justifie la valeur de l'abonnement

## 7. Utilisateur gratuit écoute en 48 kbps Opus

Étant donné que je suis un utilisateur gratuit

Quand je lance un contenu

Alors l'audio est streamé en 48 kbps Opus Et cela consomme environ 20 MB/heure Et la qualité est très correcte pour de la voix

## 8. Utilisateur Premium écoute en 64 kbps Opus

Étant donné que je suis un utilisateur Premium

Quand je lance un contenu

Alors l'audio est streamé en 64 kbps Opus Et cela consomme environ 30 MB/heure Et la qualité est excellente (détails audio supérieurs)

## 9. Comparaison qualité 48 kbps vs 64 kbps

Étant donné que je consulte la page Premium

Quand je lis la section qualité audio

Alors je vois l'explication:

## 10. Justification 48 kbps suffisant pour gratuit

Étant donné que le contenu RoadWave est principalement de la voix

Quand la qualité est fixée à 48 kbps pour gratuit

Alors c'est largement suffisant pour comprendre clairement Et équivalent à la qualité radio FM Et les utilisateurs gratuits ne sont pas frustrés

## 11. Justification 64 kbps avantage tangible Premium

Étant donné que les audiophiles et créateurs audio sont exigeants

Quand la qualité Premium est à 64 kbps

Alors la différence est perceptible à l'oreille Et les ambiances, musiques de fond, nuances de voix sont mieux rendues Et cela justifie l'abonnement Premium

## 12. Switch automatique qualité selon abonnement

Étant donné que je suis gratuit et j'écoute en 48 kbps

Quand je souscris à Premium

Alors dès le contenu suivant, je passe automatiquement en 64 kbps Et je peux entendre la différence de qualité immédiatement

## 13. Consommation data Premium vs Gratuit

Étant donné que je roule 1 heure par jour

Quand je calcule la consommation mensuelle

Alors en gratuit: 20 MB/h × 1h × 22 jours = 440 MB/mois Et en Premium: 30 MB/h × 1h × 22 jours = 660 MB/mois Et la différence est de 220 MB/mois (acceptable pour 4G/5G illimitée)

## 14. Utilisateur gratuit limité à 50 contenus téléchargés

Étant donné que je suis un utilisateur gratuit

Quand j'accède au mode offline

Alors je peux télécharger jusqu'à 50 contenus maximum Et si j'essaie de télécharger un 51ème, je vois:

## 15. Utilisateur Premium téléchargements illimités

Étant donné que je suis un utilisateur Premium

Quand j'accède au mode offline

Alors je peux télécharger autant de contenus que je veux Et la seule limite est l'espace de stockage de mon device Et par exemple 500 contenus × 10 MB = 5 GB

## 16. Justification limite 50 contenus gratuit

Étant donné que 50 contenus de 10 minutes = ~8 heures d'écoute

Quand un utilisateur gratuit prépare un road trip

Alors 8 heures couvrent largement une journée de trajet Et cela permet un usage offline raisonnable sans abuser

## 17. Justification illimité Premium pour longs road trips

Étant donné qu'un road trip de plusieurs jours nécessite 20-50h de contenu

Quand un utilisateur Premium télécharge 200 contenus

Alors il peut partir serein sans connexion internet pendant 1 semaine Et cela justifie pleinement l'abonnement Premium

## 18. Affichage compteur téléchargements gratuit

Étant donné que je suis gratuit et j'ai téléchargé 37 contenus

Quand j'accède à la page Téléchargements

Alors je vois:

## 19. Pas de compteur pour Premium

Étant donné que je suis Premium et j'ai téléchargé 187 contenus

Quand j'accède à la page Téléchargements

Alors je vois simplement: Et aucune limite n'est affichée

## 20. Utilisateur gratuit historique limité à 100 derniers

Étant donné que je suis un utilisateur gratuit

Quand j'accède à mon historique d'écoute

Alors je vois les 100 derniers contenus écoutés Et les contenus plus anciens ne sont pas affichés Et je vois un message "Historique limité à 100 contenus. Passez Premium pour un historique illimité."

## 21. Utilisateur Premium historique illimité

Étant donné que je suis un utilisateur Premium

Quand j'accède à mon historique d'écoute

Alors je vois tous les contenus que j'ai écoutés depuis mon inscription Et je peux scroller jusqu'au premier contenu jamais écouté Et l'historique est complet et permanent

## 22. Recherche dans historique Premium

Étant donné que je suis Premium et j'ai 2 000 contenus dans mon historique

Quand je recherche "Tesla" dans mon historique

Alors tous les contenus écoutés contenant "Tesla" sont affichés Et je peux retrouver facilement un contenu écouté il y a 6 mois

## 23. Justification limite 100 gratuit suffisante

Étant donné que 100 contenus de 10 min = ~16 heures d'écoute

Quand un utilisateur gratuit écoute 1h/jour

Alors l'historique couvre les 16 derniers jours Et cela suffit pour retrouver un contenu récent

## 24. Justification illimité Premium pour power users

Étant donné qu'un power user écoute 3h/jour depuis 2 ans

Quand il veut retrouver un contenu spécifique écouté il y a 1 an

Alors l'historique illimité Premium lui permet de retrouver ce contenu Et cela apporte une vraie valeur ajoutée

## 25. Export historique complet (Premium uniquement)

Étant donné que je suis Premium

Quand je demande l'export de mes données

Alors l'historique complet est inclus dans l'export:

## 26. Affichage tableau comparatif Gratuit vs Premium

Étant donné que je consulte la page Premium

Quand je vois le tableau comparatif

Alors il affiche:

## 27. Justification 0 pub = argument principal

Étant donné qu'une publicité de 30s tous les 5 contenus = 6 min/h de pub

Quand un utilisateur écoute 1h/jour

Alors il subit 180 min de pub/mois (3 heures !) Et payer 4.99€ pour éviter 3h de pub/mois est très rentable Et c'est l'argument de conversion n°1

## 28. Justification qualité audio avantage tangible

Étant donné que la différence 48 kbps → 64 kbps est audible

Quand un audiophile compare les deux

Alors il entend clairement la différence sur un bon système audio voiture Et cela justifie l'abonnement pour les exigeants

## 29. Justification offline illimité pour road trips

Étant donné qu'un road trip de 2 semaines nécessite 50-100h de contenu

Quand un utilisateur Premium télécharge 300 contenus avant de partir

Alors il peut partir en zone sans réseau sereinement Et cela apporte une vraie valeur pratique

## 30. Justification pas d'over-engineering

Étant donné que RoadWave se concentre sur l'essentiel

Quand les avantages Premium sont définis

Alors il n'y a pas de:

    | fonctionnalité superflue | raison exclusion |
    |---|---|
    | Badges cosmétiques | Pas de valeur réelle |
    | Avatar Premium exclusif | Inutile pour audio |
    | Fonctionnalités sociales avancées | Pas prioritaire MVP |
    | Early access nouveaux contenus | Complexité > bénéfice |

Et cela réduit la complexité et le coût de développement

## 31. CTA Premium après 5ème publicité

Étant donné que je suis gratuit et je viens d'entendre ma 5ème pub

Quand la publicité se termine

Alors je vois un message:

## 32. CTA Premium quand limite 50 téléchargements atteinte

Étant donné que je suis gratuit et j'ai atteint 50 téléchargements

Quand j'essaie de télécharger un 51ème contenu

Alors je vois une popup:

## 33. CTA Premium quand contenu exclusif bloqué

Étant donné que je suis gratuit et je clique sur un contenu Premium

Quand l'overlay bloquant apparaît

Alors je vois:

## 34. Statistiques conversion - Quel avantage convertit le mieux ?

Étant donné qu'un admin consulte les statistiques de conversion

Quand il analyse les sources de conversion

Alors il voit:

    | source de conversion | % conversions |
    |---|---|
    | CTA après 5ème pub | 42% |
    | CTA contenu Premium bloqué | 28% |
    | CTA limite 50 téléchargements | 18% |
    | Page Premium directe | 12% |

Et cela aide à optimiser le placement des CTA

## 35. A/B test message CTA

Étant donné que RoadWave veut optimiser les conversions

Quand un A/B test est lancé sur le CTA après pub

Alors groupe A voit "Marre des pubs ?" (focus négatif) Et groupe B voit "Profitez de 0 publicité" (focus positif) Et le taux de conversion est mesuré Et le message le plus performant est déployé

## 36. Notification Premium après 30 jours d'utilisation gratuite

Étant donné que je suis utilisateur gratuit depuis 30 jours Et que j'écoute régulièrement (15h cumulées)

Quand le 30ème jour arrive

Alors je reçois une notification:

## 37. Trial gratuit refusé mais onboarding amélioré

Étant donné qu'il n'y a pas de trial gratuit

Quand un nouvel utilisateur s'inscrit

Alors un onboarding explique clairement les avantages Premium Et il peut comparer gratuit vs Premium dès le premier lancement Et cela l'aide à décider rapidement s'il veut payer

# Gestion abonnement Premium

> En tant qu'utilisateur Je veux gérer facilement mon abonnement Premium Afin de souscrire, renouveler ou annuler en toute transparence

41 scénarios

> Contexte commun à tous les scénarios

> Étant donné que je suis connecté à l'application RoadWave

## 1. Souscription via Web (desktop/mobile) avec Mangopay

Étant donné que je consulte la page Premium sur le site web

Quand je clique sur "S'abonner - Mensuel 4.99€"

Alors je suis redirigé vers le formulaire de paiement Mangopay Et je saisis mes informations de carte bancaire Et le paiement de 4.99€ est prélevé immédiatement Et la commission Mangopay est de 1.8% + 0.18€ = 0.27€ Et RoadWave reçoit 4.72€ net

## 2. Calcul commission Mangopay

Étant donné qu'un utilisateur paie 4.99€ via Mangopay

Quand la commission est calculée

Alors la commission est : 4.99€ × 1.8% + 0.18€ = 0.09€ + 0.18€ = 0.27€ Et RoadWave reçoit : 4.99€ - 0.27€ = 4.72€ Et la commission représente 5.4% du prix

## 3. Souscription via iOS App avec Apple IAP

Étant donné que j'utilise l'app iOS

Quand je clique sur "S'abonner - Mensuel 5.99€"

Alors je suis redirigé vers l'interface Apple In-App Purchase Et le prix affiché est 5.99€ (majoré de 20%) Et le paiement est effectué via mon compte Apple Et Apple prend 30% de commission = 1.80€ Et RoadWave reçoit 4.19€ net

## 4. Souscription via Android App avec Google Play Billing

Étant donné que j'utilise l'app Android

Quand je clique sur "S'abonner - Mensuel 5.99€"

Alors je suis redirigé vers l'interface Google Play Billing Et le prix affiché est 5.99€ (majoré de 20%) Et le paiement est effectué via mon compte Google Et Google prend 30% de commission = 1.80€ Et RoadWave reçoit 4.19€ net

## 5. Majoration 20% sur mobile pour compenser commission 30%

Étant donné que Apple/Google prennent 30% de commission

Quand RoadWave fixe le prix mobile

Alors le prix web est 4.99€ (commission Mangopay 5.4%) Et le prix mobile est 5.99€ (commission Apple/Google 30%) Et la majoration est de 1€ (+20%) Et cela compense partiellement la commission excessive

## 6. Email incitation souscription web moins chère

Étant donné que je consulte Premium depuis l'app mobile

Quand je vois le prix 5.99€

Alors je vois aussi un message: Et un lien vers le site web est fourni

## 7. Calcul économie souscription web vs mobile

Étant donné que le prix web est 4.99€/mois Et que le prix mobile est 5.99€/mois

Quand je calcule l'économie annuelle

Alors web : 4.99€ × 12 = 59.88€/an Et mobile : 5.99€ × 12 = 71.88€/an Et économie : 12€/an (soit 20% d'économie)

## 8. Activation immédiate après paiement réussi

Étant donné que je viens de payer mon abonnement Premium

Quand le paiement est confirmé

Alors mon statut passe immédiatement à "Premium" Et je peux accéder aux avantages Premium dès maintenant Et je reçois un email de confirmation

## 9. Email confirmation souscription

Étant donné que j'ai souscrit à Premium

Quand la souscription est confirmée

Alors je reçois un email:

## 10. Email rappel 7 jours avant renouvellement

Étant donné que mon abonnement mensuel se renouvelle le 15 juillet

Quand le 8 juillet arrive (7 jours avant)

Alors je reçois un email de rappel:

## 11. Renouvellement automatique réussi

Étant donné que mon abonnement mensuel arrive à échéance le 15 juillet

Quand le 15 juillet arrive

Alors Mangopay/Apple/Google prélève automatiquement 4.99€ (ou 5.99€) Et mon abonnement est renouvelé pour 1 mois supplémentaire Et je reçois un email de confirmation

## 12. Email confirmation renouvellement

Étant donné que mon abonnement vient d'être renouvelé

Quand le paiement est confirmé

Alors je reçois un email:

## 13. Échec paiement renouvellement - Tentative 1

Étant donné que mon abonnement doit se renouveler le 15 juillet Mais que ma carte bancaire est expirée ou sans fonds

Quand le prélèvement échoue

Alors je reçois un email:

## 14. Retry automatique paiement après 3 jours

Étant donné que le paiement a échoué le 15 juillet

Quand le 18 juillet arrive (J+3)

Alors Mangopay/Apple/Google tente automatiquement un nouveau prélèvement Et si le paiement réussit, l'abonnement est renouvelé normalement Et si le paiement échoue encore, un 2ème retry est programmé

## 15. Retry automatique paiement après 7 jours

Étant donné que 2 tentatives ont échoué (15 juillet et 18 juillet)

Quand le 22 juillet arrive (J+7)

Alors une 3ème et dernière tentative est effectuée Et si le paiement réussit, l'abonnement est sauvé Et si le paiement échoue, l'abonnement est annulé automatiquement

## 16. Annulation automatique après 3 échecs paiement

Étant donné que les 3 tentatives de renouvellement ont échoué (J+0, J+3, J+7)

Quand la 3ème tentative échoue

Alors mon abonnement Premium est annulé automatiquement Et mon statut repasse à "Gratuit" Et je perds accès aux avantages Premium Et je reçois un email d'annulation

## 17. Email annulation automatique pour impayé

Étant donné que mon abonnement a été annulé pour échec paiement

Quand l'annulation devient effective

Alors je reçois un email:

## 18. Annulation self-service dans Settings

Étant donné que je veux annuler mon abonnement

Quand j'accède à "Paramètres > Abonnement"

Alors je vois un bouton "Annuler l'abonnement" Et je peux annuler en 2 clics sans contacter le support

## 19. Confirmation avant annulation

Étant donné que je clique sur "Annuler l'abonnement"

Quand une popup de confirmation apparaît

Alors je vois:

## 20. Accès Premium maintenu jusqu'à fin période payée

Étant donné que j'ai annulé mon abonnement le 1er juillet Et que mon abonnement mensuel était valable jusqu'au 15 juillet

Quand l'annulation est confirmée

Alors je garde l'accès Premium jusqu'au 15 juillet Et à partir du 16 juillet, je repasse en gratuit Et je ne suis pas remboursé pour les 14 jours restants

## 21. Justification pas de remboursement prorata

Étant donné que l'industrie (Spotify, Netflix, YouTube) ne rembourse pas prorata

Quand RoadWave applique la même règle

Alors c'est le standard accepté par les utilisateurs Et cela simplifie la gestion comptable Et évite les abus (souscription puis annulation immédiate pour remboursement)

## 22. Email confirmation annulation

Étant donné que j'ai annulé mon abonnement

Quand l'annulation est enregistrée

Alors je reçois un email:

## 23. Pas de renouvellement après annulation

Étant donné que j'ai annulé mon abonnement le 1er juillet

Quand le 15 juillet arrive (date de renouvellement prévue)

Alors aucun prélèvement n'est effectué Et mon statut passe automatiquement à "Gratuit" Et je ne reçois pas d'email de renouvellement

## 24. Réabonnement possible immédiatement

Étant donné que j'ai annulé mon abonnement il y a 5 jours

Quand j'accède à la page Premium

Alors je peux me réabonner immédiatement Et le processus de paiement est le même que la première fois

## 25. Pas de nouvelle période d'essai au réabonnement

Étant donné que j'ai annulé mon abonnement il y a 3 mois

Quand je me réabonne

Alors je paie immédiatement 4.99€ (pas d'essai gratuit)

## 26. Offre win-back pour utilisateurs ayant annulé

Étant donné que j'ai annulé mon abonnement il y a 1 mois

Quand je reçois un email de win-back

Alors je vois une offre spéciale:

## 27. Table subscriptions en base PostgreSQL

Étant donné qu'un utilisateur souscrit à Premium

Quand les données sont enregistrées

Alors la table subscriptions contient:

## 28. Statuts possibles dans subscription.status

Étant donné qu'un abonnement peut avoir différents statuts

Quand le statut est stocké en base

Alors les valeurs possibles sont:

    | statut | description |
    |---|---|
    | active | Abonnement actif et payé |
    | cancelled | Annulé par utilisateur (accès jusqu'à fin période) |
    | expired | Période terminée, pas renouvelé |
    | past_due | Échec paiement, en retry automatique |

## 29. Cache Redis pour vérification Premium temps réel

Étant donné qu'un utilisateur lance un contenu

Quand l'app vérifie s'il est Premium

Alors une clé Redis est consultée: Et si la clé n'existe pas, elle est recalculée depuis PostgreSQL Et cela garantit des performances <10ms

## 30. Refresh cache Redis via webhooks

Étant donné qu'un paiement est confirmé par Mangopay/Apple/Google

Quand un webhook est reçu par RoadWave

Alors le cache Redis premium:{user_id} est mis à jour immédiatement Et l'utilisateur voit son statut Premium activé sans délai

## 31. Webhooks Mangopay - PAYIN_NORMAL_SUCCEEDED

Étant donné qu'un paiement Mangopay réussit

Quand Mangopay envoie le webhook PAYIN_NORMAL_SUCCEEDED

Alors RoadWave met à jour subscriptions.status = 'active' Et met à jour current_period_end = NOW() + 1 mois Et refresh le cache Redis premium:{user_id} = true

## 32. Webhooks Mangopay - PAYIN_NORMAL_FAILED

Étant donné qu'un paiement Mangopay échoue

Quand Mangopay envoie le webhook PAYIN_NORMAL_FAILED

Alors RoadWave met à jour subscriptions.status = 'past_due' Et programme un retry automatique dans 3 jours Et envoie un email à l'utilisateur

## 33. Webhooks Apple - App Store Server Notifications

Étant donné qu'un paiement Apple IAP change de statut

Quand Apple envoie une notification serveur

Alors RoadWave parse la notification (JSON) Et met à jour la subscription en conséquence Et refresh le cache Redis

## 34. Webhooks Google - Real-time Developer Notifications

Étant donné qu'un paiement Google Play change de statut

Quand Google envoie une notification temps réel

Alors RoadWave parse la notification (JSON) Et met à jour la subscription en conséquence Et refresh le cache Redis

## 35. Dashboard admin - Métriques abonnements

Étant donné qu'un admin consulte les métriques Premium

Quand il accède au dashboard

Alors il voit:

    | métrique | valeur |
    |---|---|
    | Abonnés actifs | 12,547 |
    | Nouveaux abonnements ce mois | 1,234 |
    | Annulations ce mois | 287 (2.3%) |
    | Churn rate mensuel | 2.3% |
    | MRR (Revenus mensuels récurrents) | 58,890€ |
    | Taux conversion gratuit → Premium | 8.5% |

## 36. Calcul churn rate mensuel

Étant donné que 287 utilisateurs ont annulé ce mois Et qu'il y avait 12,547 abonnés au début du mois

Quand le churn rate est calculé

Alors churn = 287 / 12,547 = 2.3% Et un churn <5% est considéré comme excellent Et RoadWave surveille cette métrique de près

## 37. Alerte si churn rate >5%

Étant donné que le churn rate mensuel dépasse 5%

Quand le système détecte cette anomalie

Alors une alerte est envoyée à l'équipe:

## 38. Enquête satisfaction à l'annulation

Étant donné que je viens d'annuler mon abonnement

Quand l'annulation est confirmée

Alors je vois un questionnaire rapide: Et les réponses aident à améliorer l'offre Premium

## 39. Répartition canaux souscription

Étant donné qu'un admin analyse les canaux de souscription

Quand il consulte les statistiques

Alors il voit:

    | canal | abonnés | % total | revenus/mois |
    |---|---|---|---|
    | Web (Mangopay) | 8,234 | 65.6% | 41,088€ |
    | iOS (Apple) | 2,845 | 22.7% | 17,042€ |
    | Android (Google) | 1,468 | 11.7% | 8,793€ |

Et cela aide à orienter les efforts marketing (inciter web = moins de commission)

## 40. Performance vérification Premium <10ms

Étant donné que 100 000 utilisateurs consultent des contenus simultanément

Quand chaque requête vérifie le statut Premium via Redis

Alors le temps de réponse moyen est <10ms Et Redis gère facilement 100 000 requêtes/seconde Et l'expérience utilisateur est fluide

## 41. Backup données abonnements

Étant donné que les données d'abonnements sont critiques

Quand un backup est effectué

Alors PostgreSQL est répliqué en temps réel sur un replica Et un snapshot quotidien est stocké sur S3 Et en cas de crash, les données peuvent être restaurées <5 minutes

# Multi-devices et détection simultanée

> En tant qu'abonné Premium Je veux utiliser mon compte sur plusieurs appareils Mais limité à 1 seul stream actif à la fois pour éviter le partage abusif

30 scénarios

> Contexte commun à tous les scénarios

> Étant donné que je suis un utilisateur Premium actif Et que mon compte est valide

## 1. 1 seul stream actif autorisé par compte

Étant donné que je n'écoute rien actuellement

Quand je lance un contenu sur mon iPhone

Alors le stream démarre normalement Et Redis enregistre: active_streams:{user_id} = {device_id: "iPhone", started_at: timestamp}

## 2. Détection connexion simultanée - Arrêt premier device

Étant donné que j'écoute un contenu sur mon iPhone

Quand je lance un contenu sur mon iPad

Alors le système détecte une session active sur iPhone Et la lecture sur iPhone est arrêtée immédiatement (WebSocket close) Et je vois sur iPhone: "Lecture interrompue : votre compte est utilisé sur un autre appareil" Et la lecture démarre sur iPad normalement

## 3. Message explicite sur device interrompu

Étant donné que ma lecture sur iPhone vient d'être interrompue

Quand je regarde l'écran de mon iPhone

Alors je vois une overlay avec le message: Et un bouton "Reprendre ici" est disponible

## 4. Reprendre lecture sur device interrompu

Étant donné que ma lecture sur iPhone a été interrompue Et que je veux reprendre sur iPhone

Quand je clique sur "Reprendre ici"

Alors la lecture démarre sur iPhone Et l'iPad est à son tour interrompu avec le même message Et le "ping-pong" entre devices est possible (mais pénible)

## 5. Enregistrement session active dans Redis

Étant donné que je lance un contenu sur mon iPhone

Quand la lecture démarre

Alors une entrée Redis est créée:

## 6. Heartbeat toutes les 30 secondes pour maintenir session

Étant donné que j'écoute un contenu sur mon iPhone

Quand 30 secondes s'écoulent

Alors l'app envoie un heartbeat au serveur Et le serveur refresh le TTL Redis à 300 secondes Et la session reste active

## 7. Session considérée morte après 5 minutes sans heartbeat

Étant donné que j'écoute un contenu sur mon iPhone Mais que l'app crash ou que le réseau coupe

Quand 5 minutes s'écoulent sans heartbeat

Alors l'entrée Redis expire automatiquement (TTL atteint) Et je peux relancer sur n'importe quel device sans conflit

## 8. Vérification session avant démarrage lecture

Étant donné que je veux lancer un contenu sur mon iPad

Quand j'appuie sur Play

Alors le serveur vérifie Redis: active_streams:{user_id} Et si une session existe sur un autre device, elle est tuée Et la nouvelle session iPad est enregistrée dans Redis

## 9. Gestion multi-utilisateurs simultanés

Étant donné que 100 000 utilisateurs Premium écoutent simultanément

Quand Redis stocke 100 000 entrées active_streams

Alors chaque entrée a un TTL de 5 minutes Et Redis gère facilement cette charge (~10 MB de RAM) Et les vérifications sont quasi-instantanées (O(1))

## 10. Contenus téléchargés (offline) ne comptent pas comme stream

Étant donné que j'ai téléchargé 20 contenus en mode offline

Quand j'écoute un contenu téléchargé sur mon iPhone sans réseau

Alors aucune session active n'est enregistrée dans Redis Et je peux écouter offline pendant qu'un autre device stream online

## 11. Transition rapide device <10s tolérée

Étant donné que j'écoute dans ma voiture sur mon iPhone Et que j'arrive chez moi

Quand je lance la lecture sur mon iPad dans les 10 secondes

Alors la transition est considérée comme un changement de device légitime Et aucun message d'erreur n'est affiché sur iPhone Et la lecture reprend exactement où j'étais sur iPad

## 12. Détection transition rapide via timestamps

Étant donné que la session iPhone a started_at = 14:30:00

Quand je lance sur iPad à 14:30:05 (5 secondes après)

Alors le serveur détecte: diff = 5s < 10s Et applique une "graceful transition" (pas de message d'erreur iPhone) Et Redis met à jour: active_streams:{user_id} = {device_id: "iPad", ...}

## 13. Plusieurs devices disponibles mais 1 seul actif

Étant donné que je possède:

    | device | status |
    |---|---|
    | iPhone | Installé |
    | iPad | Installé |
    | MacBook (web) | Connecté |
    | Android (conjoint) | Installé |

Quand je lance un stream sur n'importe quel device

Alors seulement 1 peut être actif à la fois Et les autres devices sont en "standby"

## 14. Justification anti-partage compte

Étant donné qu'un utilisateur Premium partage son compte avec un ami

Quand les 2 personnes essaient d'écouter simultanément

Alors la lecture est constamment interrompue sur l'un ou l'autre Et l'expérience devient inutilisable Et cela décourage fortement le partage de compte

## 15. Justification protection revenus créateurs

Étant donné que 1 abonnement Premium = 4.99€/mois

Quand 70% sont reversés aux créateurs (3.49€)

Alors les créateurs sont rémunérés pour 1 personne Et si 2 personnes utilisent le même compte simultanément, c'est injuste Et la limite 1 stream protège l'équité du système

## 16. Justification UX claire

Étant donné qu'un stream est interrompu sur un device

Quand l'utilisateur voit le message explicite

Alors il comprend immédiatement pourquoi (autre device actif) Et il peut choisir de reprendre sur le device actuel ou l'autre Et il n'y a pas de confusion ou frustration

## 17. Comparaison avec Spotify (limite 1 stream)

Étant donné que Spotify Premium limite aussi à 1 stream actif

Quand RoadWave applique la même règle

Alors les utilisateurs connaissent déjà ce comportement Et cela paraît normal et accepté par l'industrie

## 18. Comparaison avec Netflix (plusieurs streams selon formule)

Étant donné que Netflix permet 1-4 streams selon la formule

Quand RoadWave limite à 1 stream pour tous

Alors c'est plus strict que Netflix Mais Netflix cible le foyer familial (TV partagée) Alors que RoadWave cible l'individu conducteur (usage personnel)

## 19. Détection pattern suspect - Changements devices fréquents

Étant donné qu'un utilisateur change de device 50 fois en 1 heure

Quand le système détecte ce pattern anormal

Alors une alerte est générée pour l'équipe modération Et le compte peut être marqué pour surveillance Et si abus confirmé, suspension possible

## 20. Logs des changements de device

Étant donné que je change de device plusieurs fois par jour

Quand les changements sont loggés

Alors chaque événement est enregistré:

    | timestamp | from_device | to_device | content_id |
    |---|---|---|---|
    | 2025-06-15 08:30:00 | null | iPhone | abc123 |
    | 2025-06-15 09:15:00 | iPhone | iPad | def456 |
    | 2025-06-15 18:30:00 | iPad | iPhone | ghi789 |

Et ces logs aident à détecter les partages de compte

## 21. Métriques admin - Changements devices par utilisateur

Étant donné qu'un admin consulte les métriques de streaming

Quand il accède au dashboard

Alors il voit:

    | métrique | valeur |
    |---|---|
    | Utilisateurs Premium actifs | 12,547 |
    | Changements de device/jour (médiane) | 2 |
    | Utilisateurs >10 changements/jour | 47 (0.4%) |
    | Comptes suspects (>20 changements/j) | 3 |

## 22. Email d'avertissement si changements excessifs

Étant donné que je change de device 30 fois par jour pendant 3 jours

Quand le système détecte ce pattern

Alors je reçois un email d'avertissement:

## 23. Suspension compte après avertissement ignoré

Étant donné que j'ai reçu un email d'avertissement il y a 7 jours Mais que je continue à changer de device 30 fois par jour

Quand l'équipe modération examine le compte

Alors mon compte Premium peut être suspendu pour partage abusif Et je reçois un email de suspension avec justification

## 24. FAQ - Pourquoi ma lecture s'arrête quand j'utilise un autre device ?

Étant donné que je consulte la FAQ Premium

Quand je cherche "lecture interrompue"

Alors je trouve la réponse:

## 25. Support - Utilisateur pense être piraté

Étant donné qu'un utilisateur voit constamment "Lecture interrompue" Et qu'il pense que son compte est piraté

Quand il contacte le support

Alors le support vérifie les logs de changements de device Et peut identifier les devices (iPhone, iPad perso vs iPhone inconnu) Et conseille de changer le mot de passe si device inconnu détecté

## 26. Changement mot de passe déconnecte tous les devices

Étant donné que je pense que mon compte est compromis

Quand je change mon mot de passe

Alors tous mes devices sont déconnectés immédiatement Et les sessions actives dans Redis sont supprimées Et je dois me reconnecter sur chaque device Et cela sécurise mon compte

## 27. Test charge - 100 000 vérifications/seconde

Étant donné que 100 000 utilisateurs Premium lancent des contenus

Quand chaque lancement vérifie Redis (GET active_streams:{user_id})

Alors Redis peut gérer facilement 100 000 requêtes/seconde Et le temps de réponse moyen est <1ms Et aucun ralentissement n'est constaté

## 28. Test failover Redis

Étant donné que le serveur Redis principal tombe en panne

Quand le failover automatique vers le replica Redis s'active

Alors les sessions actives peuvent être perdues temporairement (max 5 min) Mais les utilisateurs peuvent relancer immédiatement Et l'impact est minimal (pas de perte de données critiques)

## 29. Test concurrence - Lancement simultané 2 devices

Étant donné que je lance exactement au même instant sur iPhone et iPad

Quand les 2 requêtes arrivent en parallèle au serveur

Alors Redis utilise un lock (SETNX) pour atomicité Et 1 seul device gagne (par exemple iPhone) Et l'autre device (iPad) reçoit immédiatement une erreur Et l'utilisateur peut retry sur iPad si souhaité

## 30. Nettoyage automatique sessions expirées

Étant donné que 1000 sessions Redis ont expiré (TTL atteint)

Quand Redis supprime automatiquement ces entrées

Alors la mémoire est libérée Et les nouveaux streams peuvent démarrer sans conflit Et aucune intervention manuelle n'est nécessaire

# Offre et tarification Premium

> En tant qu'utilisateur Je veux pouvoir souscrire à un abonnement Premium Afin de profiter d'une expérience sans publicité avec des avantages exclusifs

31 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et que je suis connecté en tant qu'utilisateur

## 1. Formule mensuelle à 4.99€/mois

Étant donné que je consulte les offres Premium

Quand je vois la formule mensuelle

Alors le prix affiché est 4.99€/mois Et il n'y a aucune réduction Et le prix effectif par mois est 4.99€

## 2. Formule annuelle à 49.99€/an (2 mois offerts)

Étant donné que je consulte les offres Premium

Quand je vois la formule annuelle

Alors le prix affiché est 49.99€/an Et l'économie affichée est "2 mois offerts" Et le prix effectif par mois est 4.16€ Et je vois le badge "Meilleure offre"

## 3. Calcul économie formule annuelle

Étant donné que la formule mensuelle coûte 4.99€/mois

Quand je calcule le coût annuel en mensuel

Alors 12 mois × 4.99€ = 59.88€/an Et la formule annuelle coûte 49.99€ Et l'économie est de 9.89€ (≈ 2 mois gratuits) Et la réduction est de 16.5%

## 4. Pas d'essai gratuit disponible

Étant donné que je consulte les offres Premium

Quand je recherche une option "Essai gratuit"

Alors aucune option d'essai gratuit n'est proposée Et je dois payer dès le premier jour pour accéder au Premium

## 5. Justification absence essai gratuit - Anti-abus vacances

Étant donné que RoadWave ne propose pas d'essai gratuit

Quand un utilisateur envisage un road trip de 14 jours

Alors il ne peut pas s'abonner pour l'essai gratuit puis annuler Et cela évite les inscriptions opportunistes Et protège les revenus des créateurs

## 6. Justification absence essai gratuit - Protection revenus créateurs

Étant donné qu'un utilisateur Premium écoute des contenus

Quand il génère des écoutes dès le jour 1

Alors les créateurs sont rémunérés immédiatement (70% de 4.99€) Et il n'y a pas de "période gratuite" sans rémunération créateurs

## 7. Justification absence essai gratuit - Simplicité

Étant donné que RoadWave gère les abonnements

Quand il n'y a pas d'essai gratuit

Alors pas de gestion complexe de période trial Et pas de workflow de conversion trial → payant Et cela réduit la complexité technique

## 8. Justification absence essai gratuit - Engagement

Étant donné qu'un utilisateur paie dès le début

Quand il souscrit à Premium

Alors il est plus engagé qu'un utilisateur en essai gratuit Et le taux de churn est généralement plus faible Et la lifetime value (LTV) est plus élevée

## 9. Pas de partage familial au MVP

Étant donné que je consulte les offres Premium

Quand je recherche une option "Famille" ou "Partage"

Alors aucune option de partage familial n'est disponible Et seuls les abonnements individuels sont proposés

## 10. Justification absence partage familial - Complexité technique

Étant donné que le partage familial nécessite:

    | fonctionnalité | complexité |
    |---|---|
    | Gestion invitations | Moyenne |
    | Validation liens famille | Moyenne |
    | Limite devices par membre | Élevée |
    | Dashboard admin famille | Élevée |

Quand RoadWave évalue le ROI

Alors le coût dev/support est trop élevé pour le MVP Et la fonctionnalité est reportée post-MVP

## 11. Justification absence partage familial - Risque abus

Étant donné qu'une offre famille permet 5-6 membres

Quand il n'y a pas de vérification stricte de lien familial

Alors des "familles" de 6 inconnus pourraient se former Et cela réduirait fortement les revenus (6 personnes pour 1 abonnement)

## 12. Justification absence partage familial - Cible individuelle

Étant donné que RoadWave cible principalement les conducteurs

Quand chaque conducteur utilise l'app individuellement en voiture

Alors le besoin de partage familial est limité Et la plupart des utilisateurs sont des individus (pas des familles)

## 13. Post-MVP - Offre Famille à 9.99€/mois pour 5 comptes

Étant donné que RoadWave envisage une offre Famille post-MVP

Quand la fonctionnalité est spécifiée

Alors le prix serait 9.99€/mois pour 5 comptes Et cela représente 2€/mois/personne Mais cette offre n'est pas disponible au MVP

## 14. Comparaison tarif - Spotify à 10.99€/mois

Étant donné que Spotify Premium coûte 10.99€/mois

Quand RoadWave fixe son prix à 4.99€/mois

Alors RoadWave est 54.5% moins cher que Spotify Et cela positionne RoadWave comme très accessible

## 15. Comparaison tarif - YouTube Premium à 11.99€/mois

Étant donné que YouTube Premium coûte 11.99€/mois

Quand RoadWave fixe son prix à 4.99€/mois

Alors RoadWave est 58.4% moins cher que YouTube Premium Et cela est un argument commercial fort

## 16. Comparaison tarif - Apple Music à 10.99€/mois

Étant donné qu'Apple Music coûte 10.99€/mois

Quand RoadWave fixe son prix à 4.99€/mois

Alors RoadWave est 54.5% moins cher qu'Apple Music Et cela attire les utilisateurs sensibles au prix

## 17. Justification tarif bas - Cible conducteurs quotidiens

Étant donné que RoadWave cible les trajets quotidiens domicile-travail

Quand le prix est fixé à 4.99€/mois

Alors c'est un budget raisonnable pour un conducteur Et équivalent à ~1-2 cafés/mois Et psychologiquement acceptable pour un usage quotidien

## 18. Justification formule annuelle - Engagement long terme

Étant donné que la formule annuelle offre 2 mois gratuits

Quand un utilisateur souscrit pour 1 an

Alors il s'engage sur le long terme Et RoadWave sécurise 49.99€ de revenus immédiatement Et le cash flow est amélioré

## 19. Justification formule annuelle - Réduction churn

Étant donné qu'un utilisateur paie 49.99€ pour l'année

Quand il envisage d'arrêter après 3 mois

Alors il a déjà payé pour 12 mois Et il continuera probablement à utiliser l'app Et le taux de churn est réduit significativement

## 20. Affichage comparatif des deux formules

Étant donné que je consulte la page Premium

Quand je vois les deux formules côte à côte

Alors je vois:

## 21. Mise en avant formule annuelle

Étant donné que je consulte la page Premium

Quand je vois les deux formules

Alors la formule annuelle a un badge "Meilleure offre" ⭐ Et elle est visuellement mise en avant (bordure colorée, taille plus grande) Et l'économie de 2 mois est affichée en gros Et cela incite à choisir la formule annuelle

## 22. Lien "Pourquoi pas d'essai gratuit ?" en FAQ

Étant donné que je consulte la page Premium

Quand je clique sur "FAQ"

Alors je vois une question "Pourquoi pas d'essai gratuit ?" Et la réponse explique:

## 23. A/B test formule annuelle (post-MVP)

Étant donné que RoadWave veut optimiser la conversion annuelle

Quand un A/B test est lancé

Alors groupe A voit "2 mois offerts" (économie en durée) Et groupe B voit "Économisez 9.89€" (économie en argent) Et les taux de souscription sont mesurés Et le message le plus performant est déployé

## 24. Promo temporaire exceptionnelle (Black Friday, etc.)

Étant donné que c'est le Black Friday

Quand une promo temporaire est activée

Alors la formule annuelle peut passer à 39.99€/an (au lieu de 49.99€) Et l'économie affichée est "4 mois offerts !" Et la promo dure 3 jours uniquement Et cela génère un pic de souscriptions

## 25. Code promo partenariat influenceur

Étant donné qu'un influenceur promeut RoadWave

Quand il partage un code promo "INFLUENCEUR20"

Alors les utilisateurs obtiennent -20% sur le premier mois (3.99€ au lieu de 4.99€) Et le code est valable 1 mois Et les conversions sont trackées par code promo

## 26. Statistiques admin - Répartition formules

Étant donné qu'un admin consulte les métriques d'abonnements

Quand il accède au dashboard

Alors il voit:

    | métrique | valeur |
    |---|---|
    | Abonnés Premium total | 12,547 |
    | Abonnés mensuels | 7,234 (58%) |
    | Abonnés annuels | 5,313 (42%) |
    | Revenus mensuels récurrents | 58,890€ |

Et ces données aident à piloter la stratégie tarifaire

## 27. Calcul revenus mensuels récurrents (MRR)

Étant donné que RoadWave a:

    | formule | nombre abonnés | prix |
    |---|---|---|
    | Mensuel | 7,234 | 4.99€/mois |
    | Annuel | 5,313 | 49.99€/an |

Quand le MRR est calculé

Alors MRR mensuel = 7,234 × 4.99€ = 36,098€ Et MRR annuel ramené au mois = 5,313 × 49.99€ / 12 = 22,139€ Et MRR total = 58,237€/mois

## 28. Projection revenus annuels (ARR)

Étant donné que le MRR est de 58,237€

Quand l'ARR est calculé

Alors ARR = 58,237€ × 12 = 698,844€/an Et cela aide à évaluer la valorisation de l'entreprise

## 29. Affichage prix TTC (TVA incluse)

Étant donné que RoadWave est une plateforme française

Quand les prix sont affichés

Alors tous les prix sont TTC (TVA 20% incluse) Et le prix 4.99€ inclut déjà la TVA Et cela respecte la réglementation française

## 30. Performance page Premium avec cache

Étant donné que la page Premium est consultée fréquemment

Quand un utilisateur charge la page

Alors les prix et avantages sont servis depuis un cache CDN Et le temps de chargement est <200ms Et cela garantit une expérience fluide

## 31. Localisation prix selon pays (post-MVP)

Étant donné que RoadWave se lance à l'international post-MVP

Quand un utilisateur se connecte depuis l'Allemagne

Alors les prix peuvent être ajustés (ex: 4.99€ en France, 4.49€ en Pologne) Et cela respecte le pouvoir d'achat local Mais cette fonctionnalité n'est pas au MVP (France uniquement)

# Profil créateur

> En tant qu'utilisateur de RoadWave Je veux consulter les profils des créateurs Afin de découvrir leur contenu et décider de m'abonner

31 scénarios (28 standards, 3 plans)

> Contexte commun à tous les scénarios

> Étant donné que l'application RoadWave est démarrée

## 1. URL du profil créateur

Étant donné un créateur avec le pseudo "paris_stories"

Quand l'utilisateur accède au profil

Alors l'URL est "https://roadwave.fr/@paris_stories"

## 2. Informations principales du profil

Étant donné un créateur "@paris_stories" avec les informations suivantes:

    | champ | valeur |
    |---|---|
    | photo | avatar_120x120.jpg |
    | pseudo | paris_stories |
    | badge_vérifié | true |
    | bio | Histoires et anecdotes de Paris |
    | abonnés | 1200 |
    | contenus | 42 |
    | durée_totale | 18h |
    | écoutes_totales | 54000 |

Quand le profil est affiché

Alors les éléments suivants sont visibles:

    | élément | valeur affichée |
    |---|---|
    | Photo profil | 120×120 px |
    | @pseudo | @paris_stories |
    | Badge vérifié | ✓ |
    | Bio | Histoires et... |
    | Nombre abonnés | 1.2K abonnés |
    | Nombre contenus | 42 contenus |
    | Durée totale | 18h de contenu créé |
    | Écoutes totales | 54K écoutes totales |

## 3. 📋 Plan: Arrondi des statistiques publiques

Étant donné un créateur avec

Quand le profil est affiché

Alors la valeur affichée est ""

📊 Exemples de données:

métrique | valeur_exacte | valeur_affichée
--- | --- | ---
abonnés | 342 | 342
abonnés | 1200 | 1.2K
abonnés | 54000 | 54K
abonnés | 1200000 | 1.2M
écoutes | 842 | 842
écoutes | 5400 | 5.4K
écoutes | 142000 | 142K
écoutes | 2100000 | 2.1M
durée (heures) | 18 | 18h
durée (heures) | 142 | 142h

## 4. Bio avec markdown basique

Étant donné un créateur avec la bio suivante en markdown:

Quand le profil est affiché

Alors le texte en gras "Histoires de Paris" est formaté Et le texte en italique "Nouveau contenu chaque semaine" est formaté Et le lien "https://paris-stories.fr" est cliquable

## 5. Limitation de la bio à 300 caractères

Étant donné un créateur qui entre une bio de 350 caractères

Quand la bio est sauvegardée

Alors seuls les 300 premiers caractères sont conservés Et un message "Maximum 300 caractères" s'affiche

## 6. Boutons d'action principaux

Étant donné que l'utilisateur consulte un profil créateur

Quand la page est chargée

Alors les boutons suivants sont visibles:

    | bouton | action |
    |---|---|
    | S'abonner | Abonnement au créateur |
    | Partager profil | Menu de partage |
    | ••• | Menu contextuel |

## 7. Menu contextuel du profil [•••]

Étant donné que l'utilisateur clique sur le bouton [•••]

Quand le menu s'ouvre

Alors les options suivantes sont disponibles:

    | option | description |
    |---|---|
    | Partager profil | Partager le lien du profil |
    | Signaler profil | Signaler spam ou usurpation d'identité |
    | Bloquer créateur | Masquer tous les contenus du créateur |

## 8. Liste des contenus du créateur

Étant donné un créateur avec 3 contenus publiés

Quand le profil est affiché

Alors chaque contenu affiche:

    | élément | exemple |
    |---|---|
    | Cover image | Image 16:9 |
    | Titre | Balade à Paris |
    | Durée et écoutes | 12 min · 🎧 2.3K |
    | Localisation | 📍 Paris |
    | Bouton lecture | ▶️ |

## 9. 📋 Plan: Options de tri des contenus

Étant donné un créateur avec 10 contenus publiés

Quand l'utilisateur sélectionne le tri ""

Alors les contenus sont triés par

📊 Exemples de données:

option_tri | critère
--- | ---
Plus récents | Date publication DESC (défaut)
Plus populaires | Écoutes × facteur temporel (90 jours)
Plus anciens | Date publication ASC

## 10. Filtrage des contenus par tag

Étant donné un créateur avec des contenus taggés "Voyage", "Histoire", "Gastronomie"

Quand l'utilisateur filtre par tags "Voyage, Histoire"

Alors seuls les contenus avec ces tags sont affichés Et le nombre de résultats est indiqué "12 contenus"

## 11. Recherche locale dans le profil

Étant donné que l'utilisateur consulte le profil de "@paris_stories" Et que le créateur a publié 50 contenus

Quand l'utilisateur entre "Montmartre" dans la barre de recherche

Alors la recherche s'effectue sur les titres et descriptions Et seuls les contenus correspondants sont affichés Et le placeholder indique "Rechercher dans les contenus de @paris_stories"

## 12. Chargement paginé des contenus

Étant donné un créateur avec 100 contenus publiés

Quand le profil est affiché

Alors 20 contenus sont chargés initialement Et un bouton "Charger plus" est visible en bas de page

Quand l'utilisateur clique sur "Charger plus"

Alors 20 contenus supplémentaires sont chargés

## 13. Informations publiques visibles par tous

Étant donné que l'utilisateur consulte un profil créateur

Alors les informations suivantes sont publiques:

    | information | visible |
    |---|---|
    | Photo et pseudo | ✅ |
    | Badge vérifié | ✅ |
    | Bio | ✅ |
    | Nombre abonnés | ✅ |
    | Nombre contenus | ✅ |
    | Durée totale créée | ✅ |
    | Écoutes totales | ✅ |

## 14. Informations privées non visibles

Étant donné que l'utilisateur consulte un profil créateur

Alors les informations suivantes sont privées:

    | information | visible |
    |---|---|
    | Liste des abonnés | ❌ |
    | Revenus | ❌ |
    | Localisation précise | ❌ |
    | Email | ❌ |

## 15. Dashboard créateur avec métriques privées

Étant donné que le créateur "@paris_stories" consulte son propre dashboard

Quand la page statistiques est affichée

Alors les métriques suivantes sont accessibles:

    | métrique | type |
    |---|---|
    | Taux complétion moyen | 78% |
    | Évolution abonnés | Graphique |
    | Écoutes par contenu | Tableau |
    | Revenus | Dashboard |
    | Taux conversion Premium | Pourcentage |
    | Démographie (âge/zone) | Agrégée |

## 16. Graphique d'évolution des abonnés

Étant donné que le créateur consulte son dashboard

Quand il sélectionne la période "30 jours"

Alors un graphique d'évolution des abonnés est affiché Et les périodes disponibles sont:

    | période |
    |---|
    | 30j |
    | 90j |
    | 1 an |

## 17. Tableau détaillé des écoutes par contenu

Étant donné un créateur avec 10 contenus publiés

Quand il consulte le tableau des performances

Alors chaque contenu affiche:

    | métrique | exemple |
    |---|---|
    | Titre | Balade |
    | Écoutes totales | 2300 |
    | Écoutes complètes >80% | 1840 |
    | Taux complétion | 80% |
    | Likes | 420 |
    | Partages | 56 |

## 18. Affichage du badge vérifié

Étant donné un créateur vérifié "@paris_stories"

Quand son profil est affiché

Alors le badge bleu "✓" est accolé au pseudo Et un tooltip "Compte vérifié" s'affiche au survol

## 19. Badge vérifié visible partout

Étant donné un créateur vérifié "@paris_stories"

Alors le badge "✓" est affiché dans:

    | emplacement |
    |---|
    | Page profil |
    | Player en lecture |
    | Résultats de recherche |
    | Notifications |

## 20. 📋 Plan: Attribution automatique du badge selon critères

Étant donné un créateur avec

Quand les conditions sont validées

Alors le badge vérifié est attribué

📊 Exemples de données:

critère | automatique
--- | ---
KYC Mangopay validé | Oui
≥10K abonnés + compte >6 mois | Oui
Célébrité / Média officiel | Manuel

## 21. Attribution automatique via KYC

Étant donné un créateur qui complète son KYC Mangopay

Quand les documents sont validés

Alors le badge vérifié est attribué automatiquement Et une notification "Votre compte est maintenant vérifié ✓" est envoyée

## 22. Attribution automatique à 10K abonnés

Étant donné un créateur avec 9999 abonnés et un compte de 7 mois

Quand il atteint 10000 abonnés

Alors le badge vérifié est attribué automatiquement Et une notification de félicitations est envoyée

## 23. Demande manuelle de vérification (célébrité)

Étant donné un créateur reconnu publiquement

Quand il soumet le formulaire de demande de vérification

Alors une requête est créée pour l'équipe RoadWave Et l'équipe vérifie l'identité sous 48-72h Et le badge est attribué si validation réussie

## 24. Retrait du badge en cas de suspension

Étant donné un créateur vérifié avec le badge "✓"

Quand sa monétisation est suspendue

Alors le badge vérifié est retiré temporairement Et le badge est restauré après levée de la suspension

## 25. Retrait définitif du badge pour strikes multiples

Étant donné un créateur vérifié avec 3 strikes actifs

Quand un 4ème strike est appliqué (ban)

Alors le badge vérifié est retiré définitivement Et le compte est banni

## 26. Retrait du badge pour usurpation d'identité

Étant donné un créateur vérifié qui usurpe l'identité d'une célébrité

Quand la fraude est détectée

Alors le badge est retiré immédiatement Et le compte est banni Et une enquête est ouverte

## 27. Profil créateur supprimé

Étant donné qu'un utilisateur tente d'accéder à "@deleted_user"

Quand la page est chargée

Alors un message "Ce profil n'existe pas ou a été supprimé" s'affiche

## 28. Blocage d'un créateur

Étant donné que l'utilisateur bloque le créateur "@spam_account"

Quand l'utilisateur consulte son flux de recommandations

Alors aucun contenu de "@spam_account" n'est affiché Et le créateur n'apparaît plus dans les recherches

## 29. Déblocage d'un créateur

Étant donné que l'utilisateur a bloqué "@paris_stories"

Quand il accède à ses paramètres "Comptes bloqués" Et qu'il débloque "@paris_stories"

Alors les contenus du créateur réapparaissent dans les recommandations

## 30. Signalement d'un profil pour spam

Étant donné que l'utilisateur signale le profil "@spam_account"

Quand il sélectionne la raison "Spam"

Alors le signalement est envoyé à la modération Et un message de confirmation s'affiche Et le profil reste visible jusqu'à décision de modération

## 31. Signalement pour usurpation d'identité

Étant donné que l'utilisateur signale le profil "@fake_celebrity"

Quand il sélectionne "Usurpation d'identité" Et qu'il fournit une preuve

Alors le signalement est priorisé (priorité HAUTE) Et l'équipe modération traite sous 24h

# Création de campagnes publicitaires

> En tant que publicitaire Je veux créer des campagnes avec ciblage précis et maîtrise du budget Afin d'optimiser mes investissements publicitaires

30 scénarios (27 standards, 3 plans)

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et qu'un compte publicitaire est créé et vérifié

## 1. Création d'une campagne publicitaire complète

Étant donné que je suis connecté en tant que publicitaire

Quand je crée une nouvelle campagne avec les paramètres:

    | Paramètre | Valeur |
    |---|---|
    | Budget total | 300€ |
    | Date début | 2026-02-01 |
    | Date fin | 2026-02-14 |
    | Zone géographique | Département du Var |
    | Plages horaires | 7h-9h, 17h-19h |
    | Tags ciblés | Automobile, Voyage |
    | Tranche d'âge | 18+ |

Alors la campagne est créée avec succès Et le budget quotidien calculé est de 21.43€/jour Et les diffusions estimées sont de ~430 écoutes complètes Et un statut "En attente de validation" est assigné

## 2. Budget minimum 50€ requis

Étant donné que je crée une nouvelle campagne

Quand je définis un budget de 40€

Alors une erreur s'affiche: "Budget minimum requis: 50€" Et la campagne n'est pas créée

## 3. Budget de 50€ exactement accepté

Étant donné que je crée une nouvelle campagne

Quand je définis un budget de 50€

Alors la campagne est créée avec succès Et aucune erreur n'est affichée

## 4. Calcul automatique du budget quotidien

Étant donné une campagne avec:

    | Budget total | 300€ |
    |---|---|
    | Durée | 14 j |

Quand le système calcule le budget quotidien

Alors le budget/jour est de 21.43€ Et le nombre estimé de diffusions/jour est de 430 (à 0.05€/écoute)

## 5. Ciblage géographique point GPS précis

Étant donné que je crée une campagne

Quand je sélectionne "Point GPS" avec coordonnées (43.1234, 5.9234) Et que je définis un rayon de 5km

Alors la campagne cible uniquement les utilisateurs dans ce rayon Et la zone est représentée par un cercle sur la carte

## 6. Ciblage géographique ville

Étant donné que je crée une campagne

Quand je sélectionne "Ville" et choisis "Marseille"

Alors la campagne cible tous les utilisateurs dans la commune de Marseille Et les limites administratives sont affichées sur la carte

## 7. Ciblage géographique département

Étant donné que je crée une campagne

Quand je sélectionne "Département" et choisis "Var (83)"

Alors la campagne cible tout le département du Var Et une estimation de population cible est affichée

## 8. Ciblage géographique région

Étant donné que je crée une campagne

Quand je sélectionne "Région" et choisis "Provence-Alpes-Côte d'Azur"

Alors la campagne cible toute la région PACA Et l'estimation de population cible est mise à jour

## 9. Ciblage géographique national

Étant donné que je crée une campagne

Quand je sélectionne "National"

Alors la campagne cible tous les utilisateurs en France Et aucune limite géographique n'est appliquée

## 10. Ciblage horaire plages multiples

Étant donné que je crée une campagne

Quand je définis les plages horaires:

    | Plage |
    |---|
    | 7h-9h |
    | 12h-14h |
    | 17h-19h |

Alors la publicité est diffusée uniquement pendant ces plages Et elle n'est jamais diffusée en dehors (ex: 10h, 15h, 20h)

## 11. Ciblage horaire toute la journée

Étant donné que je crée une campagne

Quand je ne définis aucune plage horaire spécifique

Alors la publicité est diffusée 24h/24 Et aucune restriction horaire n'est appliquée

## 12. Ciblage par centres d'intérêt

Étant donné que je crée une campagne pour un garage automobile

Quand je sélectionne les tags:

    | Tag |
    |---|
    | Automobile |
    | Mécanique |
    | Sport |

Alors la publicité est prioritaire pour les utilisateurs avec jauges élevées sur ces tags Et elle peut quand même être diffusée à d'autres utilisateurs (ciblage non exclusif)

## 13. Classification d'âge obligatoire

Étant donné que je crée une campagne

Quand j'essaie de valider sans sélectionner une tranche d'âge

Alors une erreur s'affiche: "Classification d'âge obligatoire" Et les options proposées sont:

    | Option |
    |---|
    | Tout public |
    | 13+ |
    | 16+ |
    | 18+ |

## 14. Upload audio publicitaire formats acceptés

Étant donné que je crée une campagne

Quand j'upload un fichier audio format MP3

Alors le fichier est accepté

Quand j'upload un fichier audio format AAC (.aac ou .m4a)

Alors le fichier est accepté

Quand j'upload un fichier audio format WAV

Alors une erreur s'affiche: "Format non supporté. Utilisez MP3 ou AAC"

## 15. Durée audio publicitaire validée

Étant donné que je crée une campagne

Quand j'upload un audio de 8 secondes

Alors une erreur s'affiche: "Durée minimale: 10 secondes"

Quand j'upload un audio de 65 secondes

Alors une erreur s'affiche: "Durée maximale: 60 secondes"

Quand j'upload un audio de 30 secondes

Alors le fichier est accepté

## 16. Prépaiement obligatoire via Mangopay

Étant donné que j'ai configuré une campagne à 300€

Quand j'arrive à l'étape de paiement

Alors je dois payer les 300€ avant validation Et le paiement est traité via Mangopay Et seule la carte bancaire est acceptée

## 17. Recharge automatique optionnelle

Étant donné que j'ai une campagne active

Quand je configure la recharge automatique à 10% du budget

Alors si le budget restant passe sous 30€ (10% de 300€) Et que la campagne recharge automatiquement 100€ Et ma carte bancaire est débitée de 100€ Et le budget total passe à 130€

## 18. Désactivation recharge automatique

Étant donné que j'ai activé la recharge automatique

Quand je désactive cette option

Alors aucune recharge ne se produit automatiquement Et la campagne s'arrête quand le budget atteint 0€

## 19. Étalement budget sur période longue

Étant donné une campagne avec:

    | Budget total | 1000€ |
    |---|---|
    | Durée | 30 j |

Quand le système calcule l'étalement

Alors le budget/jour est de 33.33€ Et si le budget se consomme plus vite (ex: 50€/jour) Alors une alerte "Budget épuisé dans 10 jours" est envoyée

## 20. Estimation population cible selon zone

Étant donné que je sélectionne la zone "Marseille"

Quand le système calcule la population cible

Alors l'estimation affichée est "~15 000 utilisateurs potentiels" Et un message "Estimation basée sur utilisateurs actifs dans la zone" s'affiche

## 21. Campagne avec date de début différée

Étant donné que je crée une campagne

Quand je définis la date de début au 2026-03-01 (dans 1 mois)

Alors la campagne a le statut "Programmée" Et elle démarre automatiquement le 2026-03-01 à 00h00 Et le budget n'est pas consommé avant cette date

## 22. Interface self-service accessible

Étant donné que je suis un publicitaire

Quand j'accède à l'interface publicitaire

Alors je peux créer une campagne sans contact commercial RoadWave Et toutes les options sont configurables en autonomie Et un tutoriel guidé est disponible (première utilisation)

## 23. Aperçu zone ciblée sur carte interactive

Étant donné que je configure une zone géographique

Quand je sélectionne "Département du Var"

Alors une carte Leaflet affiche les limites du département en surbrillance Et un compteur "~50 000 utilisateurs actifs" est affiché Et je peux zoomer/dézoomer pour visualiser la zone

## 24. Tags multiples pour ciblage affiné

Étant donné que je crée une campagne pour un restaurant

Quand je sélectionne les tags:

    | Tag |
    |---|
    | Gastronomie |
    | Tourisme |
    | Famille |

Alors la publicité est prioritaire pour utilisateurs intéressés par ces 3 thèmes Et le score de ciblage combine les 3 jauges d'intérêt

## 25. Validation des dates de campagne

Étant donné que je crée une campagne

Quand je définis une date de début postérieure à la date de fin

Alors une erreur s'affiche: "Date de fin doit être après date de début" Et la campagne n'est pas créée

## 26. Durée minimale de campagne

Étant donné que je crée une campagne

Quand je définis une durée de moins de 24 heures

Alors une erreur s'affiche: "Durée minimale: 1 jour" Et je dois ajuster les dates

## 27. Durée maximale de campagne

Étant donné que je crée une campagne

Quand je définis une durée de plus de 90 jours

Alors une erreur s'affiche: "Durée maximale: 90 jours" Et je dois ajuster les dates ou créer plusieurs campagnes

## 28. 📋 Plan: Calcul budget quotidien selon durée

Étant donné une campagne avec un budget de €

Quand la durée est de jours

Alors le budget quotidien est de €/jour

📊 Exemples de données:

budget | duree | budget_jour
--- | --- | ---
100 | 10 | 10.00
300 | 14 | 21.43
500 | 30 | 16.67
1000 | 60 | 16.67

## 29. 📋 Plan: Estimation diffusions selon budget

Étant donné un budget quotidien de €

Quand le coût par écoute complète est 0.05€

Alors le nombre estimé de diffusions/jour est

📊 Exemples de données:

budget_jour | diffusions
--- | ---
10.00 | 200
21.43 | 429
50.00 | 1000
100.00 | 2000

## 30. 📋 Plan: Formats audio acceptés/rejetés

Étant donné que j'upload un fichier

Quand le format est

Alors le résultat est

📊 Exemples de données:

fichier | format | resultat
--- | --- | ---
pub.mp3 | MP3 | accepté
pub.aac | AAC | accepté
pub.m4a | AAC | accepté
pub.wav | WAV | rejeté
pub.ogg | OGG | rejeté
pub.flac | FLAC | rejeté

# Caractéristiques et facturation des publicités

> En tant que système RoadWave Je veux appliquer des règles précises de durée, skippabilité et facturation Afin d'équilibrer expérience utilisateur et rentabilité publicitaire

32 scénarios (29 standards, 3 plans)

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et qu'un utilisateur gratuit écoute du contenu

## 1. Durée minimale 10 secondes

Étant donné qu'un publicitaire uploade une publicité de 8 secondes

Quand le système valide la durée

Alors une erreur s'affiche: "Durée minimale: 10 secondes" Et l'upload est rejeté

## 2. Durée maximale 60 secondes

Étant donné qu'un publicitaire uploade une publicité de 65 secondes

Quand le système valide la durée

Alors une erreur s'affiche: "Durée maximale: 60 secondes" Et l'upload est rejeté

## 3. Durée recommandée 15-30 secondes

Étant donné qu'un publicitaire crée une campagne

Quand il voit les recommandations

Alors un message s'affiche:

## 4. Publicité de 10 secondes acceptée

Étant donné qu'un publicitaire uploade une publicité de 10 secondes

Quand le système valide la durée

Alors le fichier est accepté Et aucune erreur n'est affichée

## 5. Publicité de 60 secondes acceptée

Étant donné qu'un publicitaire uploade une publicité de 60 secondes

Quand le système valide la durée

Alors le fichier est accepté Et un avertissement s'affiche: "⚠️ Durée longue: taux de skip potentiellement élevé"

## 6. Délai minimum skippable 5 secondes par défaut

Étant donné qu'une publicité de 30 secondes démarre Et que le délai minimal est configuré à 5 secondes

Quand j'écoute pendant 3 secondes

Alors le bouton "Passer" n'est pas visible Et je dois attendre 2 secondes supplémentaires

Quand j'atteins 5 secondes d'écoute

Alors le bouton "Passer" apparaît Et je peux cliquer pour passer au contenu suivant

## 7. Délai minimum paramétrable admin (3 secondes)

Étant donné que l'admin configure le délai à 3 secondes Et qu'une publicité démarre

Quand j'écoute pendant 3 secondes

Alors le bouton "Passer" apparaît immédiatement Et je peux skipper

## 8. Délai minimum paramétrable admin (10 secondes)

Étant donné que l'admin configure le délai à 10 secondes Et qu'une publicité démarre

Quand j'écoute pendant 9 secondes

Alors le bouton "Passer" n'est toujours pas visible

Quand j'atteins 10 secondes

Alors le bouton "Passer" apparaît

## 9. Facturation écoute complète (>80%) - 0.05€

Étant donné qu'une publicité de 30 secondes est diffusée

Quand j'écoute pendant 25 secondes (83%)

Alors l'écoute est considérée comme "complète" Et le publicitaire est facturé 0.05€ Et le compteur "écoutes complètes" s'incrémente

## 10. Facturation écoute complète exactement 80%

Étant donné qu'une publicité de 30 secondes est diffusée

Quand j'écoute pendant exactement 24 secondes (80%)

Alors l'écoute est considérée comme "complète" Et le publicitaire est facturé 0.05€

## 11. Facturation skip après délai minimal - 0.02€

Étant donné qu'une publicité de 30 secondes est diffusée Et que le délai minimal est 5 secondes

Quand j'écoute pendant 10 secondes (33%) Et que je clique sur "Passer"

Alors l'écoute est considérée comme "partielle" Et le publicitaire est facturé 0.02€

## 12. Facturation skip immédiat (<5s) - 0€

Étant donné qu'une publicité de 30 secondes est diffusée Et que le délai minimal est 5 secondes

Quand j'écoute pendant 3 secondes Et que je clique sur "Suivant" (pas de bouton skip encore)

Alors l'écoute est considérée comme "non engagée" Et le publicitaire n'est PAS facturé (0€)

## 13. Comptabilisation écoute complète à 79%

Étant donné qu'une publicité de 30 secondes est diffusée

Quand j'écoute pendant 23 secondes (77%)

Alors l'écoute est considérée comme "partielle" (pas complète) Et le publicitaire est facturé 0.02€

## 14. Comptabilisation écoute complète à 100%

Étant donné qu'une publicité de 30 secondes est diffusée

Quand j'écoute les 30 secondes complètes (100%)

Alors l'écoute est considérée comme "complète" Et le publicitaire est facturé 0.05€

## 15. Budget consommé selon mix écoutes

Étant donné qu'une campagne à 300€ a généré:

    | Type écoute | Nombre | Coût unitaire | Total |
    |---|---|---|---|
    | Complète (>80%) | 4000 | 0.05€ | 200€ |
    | Partielle (5-80%) | 2000 | 0.02€ | 40€ |
    | Skip immédiat | 1000 | 0€ | 0€ |

Quand je calcule le budget consommé

Alors le total est 240€ Et il reste 60€ de budget disponible

## 16. Affichage compteur secondes restantes

Étant donné qu'une publicité de 30s démarre Et que le délai minimal est 5s

Quand j'écoute pendant 2 secondes

Alors un compteur s'affiche: "Passer dans 3s..."

Quand j'atteins 5 secondes

Alors le compteur disparaît Et le bouton "Passer la publicité" s'affiche

## 17. Progress bar publicité visible

Étant donné qu'une publicité de 30s est en lecture

Quand 10 secondes se sont écoulées

Alors la progress bar affiche 33% (10/30) Et l'indicateur temporel affiche "0:10 / 0:30" Et l'utilisateur visualise la progression

## 18. Message "Publicité" clairement affiché

Étant donné qu'une publicité démarre

Quand l'audio commence

Alors un badge "Publicité" est affiché en haut de l'écran Et la durée totale est indiquée: "Publicité (30s)" Et la transparence est maximale (utilisateur sait que c'est une pub)

## 19. Transition fluide après publicité

Étant donné qu'une publicité de 30s se termine

Quand la lecture atteint 30 secondes

Alors le délai de transition de 2s démarre Et le contenu normal suivant est annoncé Et l'enchaînement est naturel (même UX que entre contenus)

## 20. Like autorisé sur publicité

Étant donné qu'une publicité est en lecture Et que le véhicule est à l'arrêt

Quand je clique sur le bouton cœur

Alors un like explicite (+2%) est enregistré Et mes jauges d'intérêt sont mises à jour selon les tags de la pub Et le publicitaire voit un compteur "Likes" incrémenté

## 21. Abonnement autorisé sur publicité

Étant donné qu'une publicité est diffusée par un créateur Et que le véhicule est à l'arrêt

Quand je clique sur "S'abonner"

Alors l'abonnement est enregistré (+5% jauges) Et le publicitaire bénéficie de l'engagement fort Et cela compte comme une conversion majeure

## 22. Bouton skip visible et accessible

Étant donné qu'une publicité a dépassé le délai minimal

Quand le bouton "Passer" s'affiche

Alors il est positionné en bas à droite de l'écran Et il a une taille de clic confortable (44×44px minimum iOS) Et il est clairement visible (contraste élevé)

## 23. Analytics tracking précis par type

Étant donné qu'une publicité est diffusée

Quand un événement se produit

Alors il est tracké en temps réel:

    | Événement | Données enregistrées |
    |---|---|
    | Impression | timestamp, user_id, pub_id, zone_geo |
    | Écoute complète | durée_ecoutee, pourcentage, coût (0.05€) |
    | Skip après délai | durée_ecoutee, pourcentage, coût (0.02€) |
    | Skip immédiat | durée_ecoutee, pourcentage, coût (0€) |
    | Like | timestamp, tags impactés |
    | Abonnement | timestamp, creator_id |

## 24. Recommandation sweet spot 15-30s

Étant donné les statistiques RoadWave globales:

    | Durée pub | Taux complétion moyen |
    |---|---|
    | 10s | 65% |
    | 15s | 55% |
    | 30s | 45% |
    | 45s | 30% |
    | 60s | 20% |

Quand un publicitaire consulte les recommandations

Alors le sweet spot affiché est "15-30 secondes" Et l'explication est "Meilleur compromis engagement/message"

## 25. Optimisation durée selon taux de skip campagne

Étant donné qu'une campagne de 60s a un taux de skip de 85%

Quand le publicitaire consulte les recommandations

Alors le système suggère:

## 26. Coût effectif moyen (CEM) calculé

Étant donné une campagne avec:

    | Type écoute | Nombre | Coût unitaire | Total |
    |---|---|---|---|
    | Complète | 2000 | 0.05€ | 100€ |
    | Partielle | 3000 | 0.02€ | 60€ |
    | Skip immédiat | 1000 | 0€ | 0€ |

Quand je calcule le coût effectif moyen

Alors CEM = 160€ / 6000 impressions = 0.027€/impression Et cette métrique aide à comparer avec CPM industrie

## 27. Publicité non skippable interdite

Étant donné qu'un publicitaire demande "Publicité non skippable"

Quand il configure sa campagne

Alors cette option n'existe pas Et toutes les publicités sont obligatoirement skippables après 5s minimum

## 28. Délai minimal jamais <3 secondes

Étant donné qu'un admin essaie de configurer le délai à 2 secondes

Quand il valide le paramètre

Alors une erreur s'affiche: "Délai minimal: 3 secondes minimum"

## 29. Délai minimal jamais >10 secondes

Étant donné qu'un admin essaie de configurer le délai à 15 secondes

Quand il valide le paramètre

Alors une erreur s'affiche: "Délai maximal: 10 secondes maximum"

## 30. 📋 Plan: Facturation selon durée écoutée

Étant donné qu'une publicité de 30s est diffusée

Quand j'écoute pendant s (%)

Alors le type d'écoute est Et le coût facturé est €

📊 Exemples de données:

duree | pourcentage | type | cout
--- | --- | --- | ---
3 | 10 | skip immédiat | 0
5 | 17 | partielle | 0.02
10 | 33 | partielle | 0.02
20 | 67 | partielle | 0.02
24 | 80 | complète | 0.05
27 | 90 | complète | 0.05
30 | 100 | complète | 0.05

## 31. 📋 Plan: Budget consommé selon distribution écoutes

Étant donné écoutes complètes à 0.05€ Et écoutes partielles à 0.02€ Et skips immédiats à 0€

Quand je calcule le budget total consommé

Alors le résultat est €

📊 Exemples de données:

completes | partielles | skips | budget_total
--- | --- | --- | ---
1000 | 500 | 100 | 60
2000 | 1000 | 500 | 120
5000 | 2000 | 1000 | 290
0 | 1000 | 0 | 20
1000 | 0 | 0 | 50

## 32. 📋 Plan: Apparition bouton skip selon délai configuré

Étant donné que le délai minimal est configuré à s

Quand j'écoute pendant s

Alors le bouton "Passer" est

📊 Exemples de données:

delai | temps_ecoute | visible
--- | --- | ---
5 | 3 | non visible
5 | 5 | visible
5 | 10 | visible
10 | 8 | non visible
10 | 10 | visible
3 | 2 | non visible
3 | 3 | visible

# Gestion du budget et alertes publicitaires

> En tant que publicitaire Je veux suivre en temps réel mon budget et recevoir des alertes Afin de maîtriser mes dépenses et optimiser mes campagnes

30 scénarios (27 standards, 3 plans)

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et qu'un compte publicitaire est connecté Et qu'une campagne active est en cours

## 1. Dashboard budget temps réel

Étant donné que ma campagne a un budget de 300€ Et que j'ai consommé 220€

Quand je consulte le dashboard budget

Alors je vois:

    | Métrique | Valeur |
    |---|---|
    | Budget total | 300€ |
    | Budget consommé | 220€ |
    | Budget restant | 80€ |
    | Pourcentage | 73% consommé |

## 2. Jauge visuelle budget consommé

Étant donné que j'ai consommé 220€ sur 300€

Quand je consulte le dashboard

Alors une jauge de progression affiche 73% Et la couleur est orange (seuil 50-80%) Et un indicateur "80€ restants" est affiché clairement

## 3. Couleur jauge selon seuil

Étant donné un budget de 300€

Quand j'ai consommé 150€ (50%)

Alors la jauge est verte

Quand j'ai consommé 240€ (80%)

Alors la jauge est orange

Quand j'ai consommé 285€ (95%)

Alors la jauge est rouge Et un message "Budget presque épuisé" s'affiche

## 4. Projection épuisement budget

Étant donné que j'ai consommé 220€ en 10 jours Et qu'il reste 4 jours de campagne

Quand le système calcule la projection

Alors la consommation quotidienne moyenne est 22€/jour Et la projection affiche "Budget épuisé dans 3.6 jours" Et un avertissement "Campagne s'arrêtera avant la fin prévue" s'affiche

## 5. Projection avec budget suffisant

Étant donné que j'ai consommé 100€ en 10 jours Et qu'il reste 4 jours de campagne Et que le budget total est 300€

Quand le système calcule la projection

Alors la consommation quotidienne moyenne est 10€/jour Et la projection affiche "Budget suffisant pour toute la campagne" Et le budget restant estimé à la fin est 160€

## 6. Alerte 80% budget consommé

Étant donné que mon budget est de 300€

Quand je consomme 240€ (80%)

Alors je reçois immédiatement un email: Et une notification push est envoyée Et une notification in-app s'affiche

## 7. Alerte 90% budget consommé

Étant donné que mon budget est de 300€

Quand je consomme 270€ (90%)

Alors je reçois immédiatement un email:

## 8. Alerte budget épuisé (100%)

Étant donné que mon budget est de 300€

Quand je consomme les 300€ (100%)

Alors je reçois immédiatement un email: Et la campagne est automatiquement mise en pause Et plus aucune diffusion ne se produit

## 9. Pause manuelle de campagne

Étant donné que ma campagne est active Et qu'il reste 150€ de budget

Quand je clique sur "Mettre en pause"

Alors le statut passe à "En pause" Et les diffusions s'arrêtent immédiatement Et le budget de 150€ est conservé Et je peux réactiver la campagne plus tard

## 10. Reprise campagne pausée

Étant donné que ma campagne est en pause Et qu'il reste 150€ de budget

Quand je clique sur "Reprendre la campagne"

Alors le statut passe à "Active" Et les diffusions reprennent immédiatement Et le budget restant de 150€ continue de se consommer

## 11. Prolongation campagne avec recharge

Étant donné que ma campagne se termine dans 2 jours Et qu'il reste 20€ de budget

Quand je clique sur "Prolonger la campagne" Et que j'ajoute 200€ supplémentaires

Alors le budget total passe à 220€ Et la date de fin peut être prolongée de 10 jours Et un nouveau paiement Mangopay de 200€ est traité

## 12. Recharge automatique activée

Étant donné que j'ai configuré la recharge automatique Et que le seuil est fixé à 10% (30€ sur budget 300€) Et que le montant de recharge est 100€

Quand le budget restant passe sous 30€

Alors une recharge automatique de 100€ est déclenchée Et ma carte bancaire est débitée via Mangopay Et le budget total passe à budget_restant + 100€ Et je reçois un email de confirmation

## 13. Échec recharge automatique (carte expirée)

Étant donné que la recharge automatique est activée Et que ma carte bancaire a expiré

Quand le budget passe sous le seuil de 10%

Alors la recharge automatique échoue Et je reçois un email urgent: Et la campagne continue jusqu'à épuisement du budget restant

## 14. Modification ciblage si budget <50% consommé

Étant donné que j'ai consommé 120€ sur 300€ (40%)

Quand j'essaie de modifier le ciblage géographique

Alors la modification est autorisée Et le ciblage est mis à jour immédiatement Et les nouvelles diffusions utilisent le nouveau ciblage

## 15. Blocage modification ciblage si budget >50% consommé

Étant donné que j'ai consommé 180€ sur 300€ (60%)

Quand j'essaie de modifier le ciblage géographique

Alors une erreur s'affiche:

## 16. Modification audio nécessite nouvelle validation

Étant donné que ma campagne est active

Quand je veux modifier le fichier audio

Alors un message s'affiche:

## 17. Modification plages horaires autorisée

Étant donné que ma campagne cible 7h-9h et 17h-19h

Quand je modifie pour cibler 12h-14h aussi

Alors la modification est appliquée immédiatement Et les diffusions suivantes incluent la nouvelle plage Et aucune re-validation n'est nécessaire

## 18. Historique consommation budget jour par jour

Étant donné que ma campagne a duré 10 jours

Quand je consulte l'historique

Alors je vois un graphique avec:

    | Jour | Consommation | Cumulé |
    |---|---|---|
    | 1 | 22€ | 22€ |
    | 2 | 25€ | 47€ |
    | 3 | 20€ | 67€ |
    | ... | ... | ... |
    | 10 | 18€ | 220€ |

Et je peux identifier les pics de consommation

## 19. Notification fin de campagne programmée

Étant donné que ma campagne se termine le 14/02

Quand la date de fin est atteinte

Alors je reçois un email:

## 20. Remboursement budget non utilisé

Étant donné que ma campagne avait 300€ de budget Et qu'elle s'est terminée avec 280€ consommés

Quand la campagne se termine (date ou épuisement)

Alors un remboursement de 20€ est initié via Mangopay Et le délai est de 5-7 jours ouvrés Et je reçois une notification de confirmation

## 21. Aucun remboursement si budget entièrement consommé

Étant donné que ma campagne avait 300€ de budget Et qu'elle s'est terminée avec 300€ consommés

Quand la campagne se termine

Alors aucun remboursement n'est initié Et le message final indique "Budget entièrement utilisé"

## 22. Statistiques comparatives budget vs objectif

Étant donné que j'avais défini un objectif de 5000 impressions Et que mon budget était 300€

Quand je consulte les statistiques finales

Alors je vois:

    | Métrique | Objectif | Réalisé | Écart |
    |---|---|---|---|
    | Impressions | 5000 | 6000 | +20% |
    | Budget | 300€ | 280€ | -7% |
    | Coût/impression | 0.06€ | 0.047€ | -22% |

Et une analyse "✅ Objectifs dépassés avec budget optimisé"

## 23. Export rapport financier détaillé

Étant donné que je veux analyser mes dépenses

Quand je clique sur "Exporter rapport financier"

Alors je télécharge un CSV avec:

    | Colonne |
    |---|
    | Date/Heure |
    | Type écoute |
    | Coût unitaire |
    | Zone géographique |
    | Utilisateur (anonyme) |
    | Durée écoutée |

Et je peux l'importer dans Excel pour analyses

## 24. Tableau de bord multi-campagnes

Étant donné que j'ai 3 campagnes actives

Quand je consulte la vue d'ensemble

Alors je vois un tableau récapitulatif:

    | Campagne | Budget | Consommé | % | Jours restants | Projection |
    |---|---|---|---|---|---|
    | A | 300€ | 220€ | 73 | 4j | Suffisant |
    | B | 500€ | 480€ | 96 | 10j | Épuisé 2j |
    | C | 200€ | 50€ | 25 | 20j | Suffisant |

Et un badge alerte rouge sur la campagne B

## 25. Alerte consolidée multi-campagnes

Étant donné que j'ai 5 campagnes actives Et que 2 campagnes ont >80% budget consommé

Quand je reçois les notifications

Alors un email consolidé unique est envoyé: Et je ne reçois pas 2 emails séparés (évite spam)

## 26. Configuration seuils alertes personnalisés

Étant donné que je configure mes préférences d'alerte

Quand je définis les seuils:

    | Seuil | Valeur |
    |---|---|
    | Alerte 1 | 70% |
    | Alerte 2 | 85% |
    | Alerte 3 | 95% |

Alors je reçois des alertes à 70%, 85% et 95% Et non aux seuils par défaut 80%, 90%, 100%

## 27. Désactivation alertes email

Étant donné que je préfère uniquement les notifications in-app

Quand je désactive les alertes email dans mes préférences

Alors je ne reçois plus d'emails d'alerte budget Mais les notifications in-app continuent Et les alertes critiques (échec paiement) sont toujours envoyées par email

## 28. 📋 Plan: Couleur jauge selon pourcentage consommé

Étant donné un budget de 300€

Quand j'ai consommé € (%)

Alors la couleur de la jauge est

📊 Exemples de données:

montant | pourcentage | couleur
--- | --- | ---
100 | 33 | verte
150 | 50 | verte
180 | 60 | orange
240 | 80 | orange
270 | 90 | rouge
285 | 95 | rouge
300 | 100 | rouge

## 29. 📋 Plan: Projection épuisement selon consommation

Étant donné un budget de 300€ Et une consommation actuelle de € Et une durée écoulée de jours

Quand je calcule la consommation quotidienne moyenne

Alors elle est de €/jour Et le budget sera épuisé dans jours

📊 Exemples de données:

consomme | jours_ecoules | conso_jour | jours_restants
--- | --- | --- | ---
100 | 5 | 20 | 10
200 | 10 | 20 | 5
150 | 10 | 15 | 10
270 | 12 | 22.5 | 1.3

## 30. 📋 Plan: Alertes envoyées selon seuils

Étant donné un budget de 500€

Quand je consomme € (%)

Alors je reçois une alerte

📊 Exemples de données:

montant | pourcentage | niveau
--- | --- | ---
350 | 70 | aucune
400 | 80 | alerte 80%
450 | 90 | alerte 90%
500 | 100 | budget épuisé

# Insertion et fréquence des publicités

> En tant que système RoadWave Je veux insérer les publicités de manière équilibrée et non intrusive Afin de préserver l'expérience utilisateur tout en monétisant

31 scénarios (28 standards, 3 plans)

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et qu'un utilisateur gratuit est connecté

## 1. Fréquence par défaut 1 pub / 5 contenus

Étant donné que la fréquence par défaut est configurée à 1/5 Et que je suis un utilisateur gratuit

Quand j'écoute 5 contenus

Alors 1 publicité est insérée après le 5ème contenu

Quand j'écoute 10 contenus

Alors 2 publicités sont insérées (après les contenus 5 et 10)

## 2. Aucune publicité pour utilisateurs Premium

Étant donné que je suis un utilisateur Premium

Quand j'écoute 100 contenus

Alors aucune publicité n'est insérée Et je bénéficie d'une expérience sans interruption publicitaire

## 3. Fréquence paramétrable par admin (1/3)

Étant donné que l'admin configure la fréquence à 1/3 Et que je suis un utilisateur gratuit

Quand j'écoute 6 contenus

Alors 2 publicités sont insérées (après contenus 3 et 6)

## 4. Fréquence paramétrable par admin (1/10)

Étant donné que l'admin configure la fréquence à 1/10 Et que je suis un utilisateur gratuit

Quand j'écoute 20 contenus

Alors 2 publicités sont insérées (après contenus 10 et 20)

## 5. Jamais d'interruption d'un contenu en cours

Étant donné que j'écoute un contenu de 10 minutes Et que je suis à 5 minutes de lecture Et qu'une publicité devrait être insérée selon la fréquence

Quand le système vérifie l'insertion

Alors la publicité attend la fin du contenu actuel Et elle s'insère pendant le délai de transition (2s) Et le contenu n'est jamais interrompu

## 6. Insertion entre deux contenus uniquement

Étant donné que le contenu "A" se termine Et que le délai de transition de 2s démarre

Quand le système détecte qu'une publicité doit être insérée

Alors le message "Publicité (30s)" s'affiche Et la publicité démarre après les 2 secondes Et l'enchaînement est naturel et fluide

## 7. Rotation limite 3 fois/jour par utilisateur

Étant donné qu'un utilisateur a entendu la publicité "A" 3 fois aujourd'hui

Quand le système sélectionne une nouvelle publicité à diffuser

Alors la publicité "A" n'est plus éligible pour cet utilisateur aujourd'hui Et une autre publicité "B" est sélectionnée Et cela évite la saturation publicitaire

## 8. Compteur de diffusions par pub et par utilisateur

Étant donné qu'un utilisateur écoute la pub "RestaurantX"

Quand la diffusion se termine

Alors un compteur Redis "pub:RestaurantX:user:123:count" s'incrémente Et le TTL est de 24h (reset à minuit)

Quand le compteur atteint 3

Alors la pub "RestaurantX" est exclue des prochaines sélections aujourd'hui

## 9. Limite max 6 pubs/heure par utilisateur

Étant donné qu'un utilisateur a entendu 6 publicités dans la dernière heure

Quand le système devrait insérer une 7ème pub

Alors l'insertion est reportée à l'heure suivante Et un compteur horaire Redis "pub:user:123:hourly" est vérifié Et cela évite le spam publicitaire

## 10. Ciblage géographique prioritaire - Point GPS

Étant donné qu'une publicité cible un point GPS à 2km de ma position Et qu'une autre publicité cible ma ville entière

Quand le système sélectionne une publicité

Alors la publicité point GPS est priorisée (score géo plus élevé) Et le ciblage précis est favorisé

## 11. Ciblage géographique prioritaire - Hiérarchie

Étant donné que 4 publicités sont éligibles:

    | Publicité | Zone | Distance |
    |---|---|---|
    | A | Point GPS | 1km |
    | B | Ville | 0km |
    | C | Département | 0km |
    | D | National | N/A |

Quand le système sélectionne selon priorité géographique

Alors l'ordre de priorité est: A > B > C > D Et la publicité A (Point GPS, la plus précise) est diffusée

## 12. Ciblage centres d'intérêt secondaire

Étant donné que 2 publicités ciblent ma zone géographique:

    | Publicité | Tags | Mes jauges |
    |---|---|---|
    | A | Automobile | 80% |
    | B | Voyage | 40% |

Quand le système applique le score centres d'intérêt

Alors la publicité A est favorisée (meilleur match jauges) Et le ciblage thématique affine la sélection

## 13. Ciblage horaire strict

Étant donné qu'une campagne cible uniquement 7h-9h Et qu'il est 10h30

Quand le système sélectionne une publicité

Alors cette campagne n'est PAS éligible Et seules les campagnes "toute la journée" ou avec plage horaire actuelle sont considérées

## 14. Ciblage horaire pendant plage active

Étant donné qu'une campagne cible 7h-9h et 17h-19h Et qu'il est 8h15

Quand le système sélectionne une publicité

Alors cette campagne est éligible Et elle peut être diffusée

## 15. Normalisation volume audio -14 LUFS

Étant donné qu'une publicité est uploadée avec volume trop élevé (-6 LUFS)

Quand le système encode l'audio via FFmpeg

Alors le volume est normalisé automatiquement à -14 LUFS Et le publicitaire reçoit une notification "Volume audio ajusté pour conformité" Et cela évite l'effet "pub trop forte" frustrant

## 16. Validation volume audio lors encodage

Étant donné qu'une publicité est soumise

Quand FFmpeg encode le fichier

Alors une commande loudnorm est appliquée: Et le fichier final respecte le standard broadcast -14 LUFS

## 17. Sélection aléatoire si critères équivalents

Étant donné que 3 publicités ont le même score géo Et qu'elles ont toutes des jauges centres d'intérêt équivalentes Et qu'aucune n'a été diffusée 3 fois aujourd'hui

Quand le système sélectionne une publicité

Alors une sélection aléatoire équitable est faite Et chaque campagne a 33% de chances d'être diffusée

## 18. Exclusion publicités avec budget épuisé

Étant donné qu'une campagne "A" a épuisé son budget Et qu'une campagne "B" a encore du budget disponible

Quand le système sélectionne une publicité

Alors seule la campagne "B" est éligible Et la campagne "A" est automatiquement exclue

## 19. Exclusion publicités hors dates de campagne

Étant donné qu'une campagne "A" est programmée du 01/02 au 14/02 Et que nous sommes le 20/01

Quand le système sélectionne une publicité

Alors la campagne "A" n'est pas éligible Et seules les campagnes actives aujourd'hui sont considérées

## 20. Publicité visible uniquement dans zone géographique

Étant donné qu'une publicité cible "Marseille uniquement" Et que je suis à Lyon

Quand le système sélectionne une publicité

Alors cette publicité n'est jamais éligible pour moi Et je ne la verrai jamais tant que je reste à Lyon

## 21. Tracking compteur horaire avec TTL

Étant donné qu'un utilisateur entend une pub à 10h05

Quand le compteur horaire est incrémenté

Alors la clé Redis "pub:user:123:hourly:2026012110" est créée Et le TTL est de 1 heure (expire à 11h05) Et le système compte les pubs dans la fenêtre glissante d'1h

## 22. Reset compteur quotidien à minuit

Étant donné qu'un utilisateur a entendu la pub "A" 3 fois le 20/01

Quand minuit passe et on est le 21/01

Alors le compteur "pub:A:user:123:count" est expiré (TTL 24h) Et l'utilisateur peut à nouveau entendre la pub "A" jusqu'à 3 fois

## 23. Aucune pub si aucune campagne éligible

Étant donné qu'aucune campagne n'a de budget disponible

Quand le système devrait insérer une publicité

Alors aucune pub n'est insérée Et l'enchaînement de contenus continue normalement Et le prochain contenu démarre directement

## 24. Priorisation campagnes avec budget important restant

Étant donné que 2 campagnes sont éligibles:

    | Campagne | Budget restant | Jours restants |
    |---|---|---|
    | A | 500€ | 2j |
    | B | 50€ | 10j |

Quand le système applique la priorisation budgétaire

Alors la campagne A est légèrement favorisée (urgence dépense) Et cela aide à épuiser les budgets avant fin de campagne

## 25. Log des sélections pour analytics

Étant donné qu'une publicité "RestaurantX" est sélectionnée

Quand elle est diffusée à l'utilisateur "123"

Alors un événement est loggé en base:

    | Champ | Valeur |
    |---|---|
    | pub_id | RestaurantX |
    | user_id | 123 |
    | timestamp | 2026-01-21 10:30 |
    | zone_geo | Marseille |
    | score_geo | 0.85 |
    | score_interet | 0.70 |

Et cela permet l'analytics publicitaire

## 26. Détection changement statut utilisateur (gratuit → premium)

Étant donné que je suis un utilisateur gratuit Et que j'entends des publicités

Quand je souscris à Premium

Alors le système détecte le changement de statut immédiatement Et plus aucune publicité n'est insérée dès le prochain contenu Et mon expérience devient sans pub instantanément

## 27. Interface admin pour ajuster fréquence globale

Étant donné que je suis admin RoadWave

Quand j'accède aux paramètres publicitaires

Alors je peux ajuster le curseur de fréquence:

    | Option | Fréquence |
    |---|---|
    | 1/3 | Haute (agressif) |
    | 1/5 | Standard (défaut) |
    | 1/7 | Modérée |
    | 1/10 | Faible |

Et le changement s'applique en temps réel à tous les utilisateurs

## 28. A/B testing fréquence sur cohortes utilisateurs

Étant donné que l'admin active un test A/B

Quand 50% des utilisateurs ont fréquence 1/5 Et 50% des utilisateurs ont fréquence 1/7

Alors les métriques sont trackées séparément:

    | Cohorte | Fréquence | Taux désabonnement | Revenus/user |
    |---|---|---|---|
    | A | 1/5 | 2.5% | 0.50€ |
    | B | 1/7 | 1.8% | 0.40€ |

Et l'admin peut identifier la fréquence optimale

## 29. 📋 Plan: Insertion publicité selon fréquence

Étant donné que la fréquence est

Quand j'écoute contenus

Alors publicités sont insérées

📊 Exemples de données:

frequence | contenus | pubs
--- | --- | ---
1/3 | 9 | 3
1/5 | 10 | 2
1/5 | 25 | 5
1/7 | 14 | 2
1/10 | 30 | 3

## 30. 📋 Plan: Priorité géographique selon type zone

Étant donné qu'une publicité cible

Quand le système calcule le score géographique

Alors la priorité est

📊 Exemples de données:

type_zone | score
--- | ---
Point GPS | 1.0
Ville | 0.8
Département | 0.6
Région | 0.4
National | 0.2

## 31. 📋 Plan: Exclusion publicité selon compteur quotidien

Étant donné qu'une publicité a été entendue fois aujourd'hui

Quand le système vérifie l'éligibilité

Alors la publicité est

📊 Exemples de données:

fois | eligible
--- | ---
0 | éligible
1 | éligible
2 | éligible
3 | non éligible
4 | non éligible

# Métriques d'engagement et dashboard publicitaire

> En tant que publicitaire Je veux consulter des métriques détaillées en temps réel Afin d'optimiser mes campagnes et mesurer leur ROI

27 scénarios (24 standards, 3 plans)

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et qu'un compte publicitaire est connecté Et qu'une campagne active est en cours

## 1. Dashboard temps réel avec métriques essentielles

Étant donné que ma campagne a généré 1000 diffusions

Quand je consulte le dashboard

Alors je vois les métriques suivantes mises à jour en temps réel:

    | Métrique | Valeur |
    |---|---|
    | Impressions | 1000 |
    | Écoutes complètes (>80%) | 400 |
    | Taux d'écoute complète | 40% |
    | Taux de skip | 60% |
    | Durée moyenne d'écoute | 18s |
    | Likes | 25 |
    | Abonnements | 5 |
    | Coût par écoute | 0.05€ |

## 2. Calcul impressions totales

Étant donné que ma publicité a été diffusée 2500 fois

Quand je consulte le dashboard

Alors le compteur "Impressions" affiche 2500 Et il s'incrémente en temps réel à chaque nouvelle diffusion

## 3. Calcul écoutes complètes (>80%)

Étant donné que ma publicité de 30s a été:

    | Durée écoutée | Nombre |
    |---|---|
    | 25s (83%) | 300 |
    | 20s (67%) | 200 |
    | 10s (33%) | 150 |
    | 5s (17%) | 50 |

Quand je consulte les écoutes complètes

Alors le compteur affiche 300 (uniquement ≥80%) Et le taux d'écoute complète est de 43% (300/700)

## 4. Calcul taux de skip

Étant donné 1000 diffusions totales Et 400 écoutes complètes

Quand je consulte le taux de skip

Alors il affiche 60% ((1000-400)/1000) Et il est calculé comme (total - complètes) / total

## 5. Durée moyenne d'écoute calculée

Étant donné que ma publicité de 30s a été écoutée:

    | Durée | Nombre d'utilisateurs |
    |---|---|
    | 30s | 400 |
    | 20s | 300 |
    | 10s | 200 |
    | 5s | 100 |

Quand je consulte la durée moyenne

Alors le calcul est: (30×400 + 20×300 + 10×200 + 5×100) / 1000 Et le résultat affiché est 21s

## 6. Métriques de likes sur publicité

Étant donné que 50 utilisateurs ont liké ma publicité

Quand je consulte le dashboard

Alors le compteur "Likes" affiche 50 Et un taux de like de 5% est calculé (50/1000 impressions) Et cela indique une forte appréciation du contenu

## 7. Métriques d'abonnements générés

Étant donné que 10 utilisateurs se sont abonnés après avoir entendu ma pub

Quand je consulte le dashboard

Alors le compteur "Abonnements" affiche 10 Et un taux de conversion de 1% est calculé (10/1000) Et cela représente un engagement très fort

## 8. Calcul coût par écoute (CPE)

Étant donné que j'ai dépensé 200€ Et obtenu 4000 écoutes complètes

Quand je consulte le coût par écoute

Alors le CPE affiché est 0.05€ (200/4000) Et il correspond au tarif standard RoadWave

## 9. Répartition géographique avec heatmap

Étant donné que ma campagne cible le département du Var Et que j'ai 1000 diffusions réparties:

    | Zone | Diffusions | Pourcentage |
    |---|---|---|
    | Toulon | 400 | 40% |
    | Hyères | 250 | 25% |
    | Fréjus | 200 | 20% |
    | Autres | 150 | 15% |

Quand je consulte la heatmap géographique

Alors une carte Leaflet affiche les zones avec intensité proportionnelle Et Toulon apparaît en rouge foncé (forte concentration) Et les autres villes en dégradé orange/jaune

## 10. Répartition horaire avec graphique

Étant donné que ma campagne cible les plages 7h-9h et 17h-19h Et que j'ai 1000 diffusions:

    | Plage horaire | Diffusions |
    |---|---|
    | 7h-8h | 300 |
    | 8h-9h | 250 |
    | 17h-18h | 280 |
    | 18h-19h | 170 |

Quand je consulte le graphique horaire

Alors un histogramme Chart.js affiche les 4 barres Et je peux identifier que 7h-8h est le pic d'écoute Et optimiser mes futures campagnes sur cette plage

## 11. Taux de complétion par tranche d'âge

Étant donné que ma campagne est Tout Public Et que j'ai des écoutes sur différentes tranches:

    | Tranche d'âge | Écoutes complètes | Total diffusions | Taux |
    |---|---|---|---|
    | 18-24 ans | 120 | 400 | 30% |
    | 25-34 ans | 200 | 400 | 50% |
    | 35-44 ans | 80 | 200 | 40% |

Quand je consulte l'analyse par âge

Alors je vois que les 25-34 ans ont le meilleur taux (50%) Et je peux cibler cette tranche pour mes prochaines campagnes

## 12. Comparatif de campagnes A/B testing

Étant donné que j'ai 2 campagnes actives:

    | Campagne | Budget | Écoutes complètes | Taux | CPE |
    |---|---|---|---|---|
    | A | 300€ | 4000 | 40% | 0.075€ |
    | B | 300€ | 6000 | 60% | 0.05€ |

Quand je consulte le comparatif

Alors je vois que la campagne B performe mieux Et le tableau recommande "Campagne B: +50% écoutes, -33% CPE" Et je peux allouer plus de budget à la campagne B

## 13. Export données CSV pour analyse externe

Étant donné que je veux analyser mes données dans Excel

Quand je clique sur "Exporter CSV"

Alors je télécharge un fichier avec les colonnes:

    | Colonne |
    |---|
    | Date |
    | Heure |
    | Zone géographique |
    | Tranche d'âge |
    | Durée écoute |
    | Skip (Oui/Non) |
    | Like (Oui/Non) |
    | Abonnement (Oui/Non) |

Et je peux faire des analyses personnalisées

## 14. Export graphiques interactifs

Étant donné que je consulte le dashboard

Quand je clique sur un graphique Chart.js

Alors je peux zoomer/filtrer interactivement Et je peux exporter le graphique en PNG Et l'image est en haute résolution pour présentations

## 15. Rapport PDF automatique fin de campagne

Étant donné que ma campagne de 14 jours se termine

Quand la date de fin est atteinte

Alors un rapport PDF est généré automatiquement Et il contient:

    | Section |
    |---|
    | Résumé exécutif |
    | Métriques clés |
    | Graphiques de performance |
    | Heatmap géographique |
    | Répartition horaire |
    | Analyse tranches d'âge |
    | Recommandations optimisation |

Et je reçois un email avec le PDF en pièce jointe

## 16. Métriques temps réel rafraîchies automatiquement

Étant donné que je consulte le dashboard à 10h00

Quand une nouvelle diffusion se produit à 10h01

Alors les métriques sont rafraîchies automatiquement (polling 30s) Et je vois les nouveaux chiffres sans recharger la page Et un badge "Mis à jour il y a 15s" s'affiche

## 17. Alertes performance personnalisées

Étant donné que je configure une alerte "Taux de skip >70%" Et que ma campagne atteint 72% de skip

Quand le seuil est dépassé

Alors je reçois un email d'alerte:

## 18. Benchmark vs moyennes RoadWave

Étant donné que ma campagne a 45% d'écoutes complètes

Quand je consulte le benchmark

Alors je vois "Votre taux: 45% | Moyenne RoadWave: 40%" Et un badge "📊 Performance: +12% vs moyenne" s'affiche Et je sais que ma campagne performe au-dessus de la moyenne

## 19. Coût total consommé vs budget

Étant donné que j'ai un budget de 300€ Et que j'ai consommé 220€

Quand je consulte le dashboard

Alors je vois une jauge "Budget consommé: 73%" (220/300) Et le montant restant "80€ restants" Et une projection "Épuisé dans 3 jours à ce rythme"

## 20. Répartition coûts par type d'écoute

Étant donné que j'ai dépensé 200€ avec:

    | Type d'écoute | Nombre | Coût unitaire | Total |
    |---|---|---|---|
    | Écoute complète | 3000 | 0.05€ | 150€ |
    | Skip après 5s | 2000 | 0.02€ | 40€ |
    | Skip immédiat | 500 | 0€ | 0€ |

Quand je consulte la répartition

Alors un graphique camembert affiche:

    | Segment | Pourcentage |
    |---|---|
    | Écoutes complètes | 75% (150€) |
    | Skips partiels | 20% (40€) |
    | Skips immédiats | 5% (0€) |

## 21. Évolution performance dans le temps

Étant donné une campagne de 30 jours

Quand je consulte le graphique d'évolution

Alors je vois une courbe Chart.js avec:

    | Axe | Donnée |
    |---|---|
    | X | Jours (1-30) |
    | Y | Taux d'écoute complète (%) |

Et je peux identifier les tendances (amélioration/dégradation) Et les jours avec pics d'engagement

## 22. Métriques avancées - Taux de réécoute

Étant donné qu'un utilisateur a entendu ma pub 3 fois Et qu'il l'a écoutée complètement les 3 fois

Quand je consulte les métriques avancées

Alors le "Taux de réécoute" affiche 100% Et cela indique que le contenu n'est pas perçu comme spam Et les utilisateurs tolèrent bien la répétition

## 23. Recommandations automatiques d'optimisation

Étant donné que ma campagne a un taux de skip de 75% Et que la durée moyenne d'écoute est de 8s sur 30s

Quand je consulte les recommandations

Alors le système suggère:

## 24. Suivi multi-campagnes avec vue consolidée

Étant donné que j'ai 3 campagnes actives simultanément

Quand je consulte la vue consolidée

Alors je vois un tableau récapitulatif:

    | Campagne | Budget | Dépensé | Diffusions | Taux complète | CPE |
    |---|---|---|---|---|---|
    | A | 300€ | 220€ | 4000 | 40% | 0.05€ |
    | B | 500€ | 150€ | 3000 | 60% | 0.05€ |
    | C | 200€ | 180€ | 3600 | 35% | 0.05€ |

Et je peux comparer les performances d'un coup d'œil

## 25. 📋 Plan: Calcul taux d'écoute complète

Étant donné diffusions totales Et écoutes complètes (≥80%)

Quand je calcule le taux

Alors le résultat est %

📊 Exemples de données:

total | completes | taux
--- | --- | ---
1000 | 400 | 40
2000 | 1200 | 60
500 | 100 | 20
1000 | 850 | 85

## 26. 📋 Plan: Calcul coût par écoute (CPE)

Étant donné un budget dépensé de € Et écoutes complètes

Quand je calcule le CPE

Alors le résultat est €

📊 Exemples de données:

depense | ecoutes | cpe
--- | --- | ---
100 | 2000 | 0.05
300 | 6000 | 0.05
50 | 1000 | 0.05
500 | 10000 | 0.05

## 27. 📋 Plan: Classification performance vs benchmark

Étant donné un taux d'écoute complète de % Et une moyenne RoadWave de 40%

Quand je compare à la moyenne

Alors la performance est

📊 Exemples de données:

taux | classification
--- | ---
60 | Excellente (+50%)
50 | Bonne (+25%)
40 | Moyenne
30 | Faible (-25%)
20 | Très faible (-50%)

# Validation et modération des publicités

> En tant que modérateur RoadWave Je veux valider manuellement toutes les publicités avant diffusion Afin de garantir la qualité et la légalité des contenus publicitaires

29 scénarios (27 standards, 2 plans)

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et qu'un modérateur RoadWave est connecté

## 1. Validation manuelle obligatoire avant diffusion

Étant donné qu'un publicitaire a créé une campagne Et que le paiement de 300€ a été effectué

Quand la campagne est soumise

Alors elle passe en statut "En attente de validation" Et elle est ajoutée à la file d'attente des modérateurs Et la diffusion ne démarre PAS avant validation manuelle Et le publicitaire reçoit un email "Votre campagne est en cours de validation (24-48h)"

## 2. Délai de validation 24-48h ouvrées

Étant donné qu'une campagne est soumise le lundi 10h

Quand le modérateur la valide le mardi 15h

Alors le délai est de 29h (dans les 48h ouvrées) Et le publicitaire reçoit une notification "Votre campagne est approuvée"

## 3. Validation dépassant 48h avec notification

Étant donné qu'une campagne est soumise le lundi 10h

Quand 48h ouvrées se sont écoulées Et que la campagne n'est toujours pas validée

Alors le publicitaire reçoit un email automatique: Et un modérateur senior est assigné automatiquement

## 4. Acceptation de campagne publicitaire

Étant donné qu'une campagne est en attente de validation Et que l'audio respecte toutes les règles

Quand le modérateur clique sur "Approuver"

Alors le statut passe à "Approuvée" Et la campagne démarre à la date programmée Et le publicitaire reçoit un email de confirmation Et le budget commence à être consommé dès le début

## 5. Refus de campagne avec motif détaillé

Étant donné qu'une campagne contient du contenu alcool

Quand le modérateur clique sur "Refuser" Et qu'il sélectionne le motif "Contenu interdit: Alcool" Et qu'il ajoute le commentaire "La publicité pour l'alcool est interdite en France"

Alors le statut passe à "Refusée" Et le publicitaire reçoit un email détaillé avec:

    | Champ | Valeur |
    |---|---|
    | Motif | Contenu interdit: Alcool |
    | Commentaire | La publicité pour l'alcool est interdite en France |
    | Action requise | Modifier votre contenu et soumettre à nouveau |

Et un remboursement automatique de 300€ est déclenché

## 6. Remboursement automatique après refus

Étant donné qu'une campagne à 500€ est refusée

Quand le statut passe à "Refusée"

Alors un remboursement Mangopay de 500€ est initié automatiquement Et le délai de remboursement est de 5-7 jours ouvrés Et le publicitaire reçoit un email "Remboursement en cours"

## 7. Contenus interdits - Alcool

Étant donné qu'une publicité mentionne "Whisky premium 40°"

Quand le modérateur écoute l'audio

Alors il doit refuser la campagne Et sélectionner le motif "Contenu interdit: Alcool"

## 8. Contenus interdits - Tabac

Étant donné qu'une publicité mentionne "Cigarettes électroniques"

Quand le modérateur écoute l'audio

Alors il doit refuser la campagne Et sélectionner le motif "Contenu interdit: Tabac/Vape"

## 9. Contenus interdits - Jeux d'argent

Étant donné qu'une publicité mentionne "Gagnez 10 000€ - Paris sportifs"

Quand le modérateur écoute l'audio

Alors il doit refuser la campagne Et sélectionner le motif "Contenu interdit: Jeux d'argent"

## 10. Contenus interdits - Politique pendant campagne électorale

Étant donné qu'une publicité politique est soumise Et que nous sommes en période de campagne électorale officielle

Quand le modérateur écoute l'audio

Alors il doit refuser la campagne Et sélectionner le motif "Contenu interdit: Publicité politique (période électorale)"

## 11. Contenus interdits - Contenu sexuel

Étant donné qu'une publicité contient des propos sexuellement explicites

Quand le modérateur écoute l'audio

Alors il doit refuser la campagne Et sélectionner le motif "Contenu interdit: Contenu sexuel"

## 12. Contenus interdits - Violence

Étant donné qu'une publicité contient des descriptions violentes

Quand le modérateur écoute l'audio

Alors il doit refuser la campagne Et sélectionner le motif "Contenu interdit: Violence"

## 13. Contenu légal autorisé - Commerce local

Étant donné qu'une publicité pour un restaurant local dit "Découvrez notre menu du jour"

Quand le modérateur écoute l'audio

Alors il doit approuver la campagne

## 14. Contenu légal autorisé - Service professionnel

Étant donné qu'une publicité pour un garage dit "Révision complète à partir de 99€"

Quand le modérateur écoute l'audio

Alors il doit approuver la campagne

## 15. Critères de validation - Qualité audio

Étant donné qu'une publicité a une qualité audio très basse (bruits, saturation)

Quand le modérateur écoute l'audio

Alors il peut refuser avec le motif "Qualité audio insuffisante" Et recommander "Veuillez soumettre un fichier audio de meilleure qualité"

## 16. Critères de validation - Classification d'âge correcte

Étant donné qu'une publicité contient du langage familier Et qu'elle est classée "Tout public"

Quand le modérateur écoute l'audio

Alors il peut refuser avec le motif "Classification d'âge incorrecte" Et recommander "Reclasser en 13+ minimum"

## 17. Critères de validation - Respect réglementation française

Étant donné qu'une publicité fait des promesses mensongères "Perdez 10kg en 1 semaine"

Quand le modérateur écoute l'audio

Alors il doit refuser avec le motif "Non-conformité réglementaire: Publicité mensongère"

## 18. File d'attente modération priorisée

Étant donné que 10 campagnes sont en attente de validation Et que la campagne A a été soumise il y a 40h Et que la campagne B a été soumise il y a 2h

Quand le modérateur consulte sa file

Alors la campagne A apparaît en premier (priorité temporelle) Et un badge "Urgente - >40h" est affiché

## 19. Dashboard modération - Vue d'ensemble

Étant donné que je suis modérateur

Quand j'accède au dashboard modération publicités

Alors je vois:

    | Métrique | Exemple valeur |
    |---|---|
    | Campagnes en attente | 5 |
    | Délai moyen de validation | 28h |
    | Campagnes validées aujourd'hui | 12 |
    | Campagnes refusées aujourd'hui | 3 |
    | Taux d'acceptation | 80% |

## 20. Transcription automatique pour aide modération

Étant donné qu'une publicité audio est soumise

Quand le système traite l'audio

Alors une transcription automatique est générée via Whisper Et elle est affichée au modérateur pour faciliter la revue Et elle permet une recherche par mots-clés (alcool, tabac, etc.)

## 21. Détection automatique mots-clés interdits

Étant donné qu'une publicité audio est soumise

Quand la transcription contient "whisky" ou "vodka"

Alors un flag automatique "⚠️ Alcool détecté" est ajouté Et la campagne est priorisée pour validation manuelle rapide Et le modérateur est alerté du contenu potentiellement interdit

## 22. Historique modération publicitaire

Étant donné qu'un publicitaire a eu 2 campagnes refusées

Quand il soumet une 3ème campagne

Alors le modérateur voit l'historique:

    | Date | Statut | Motif |
    |---|---|---|
    | 2026-01-15 | Refusée | Contenu interdit: Alcool |
    | 2026-01-20 | Refusée | Qualité audio faible |

Et il peut en tenir compte dans sa décision

## 23. Appel possible après refus

Étant donné que ma campagne a été refusée pour "Classification incorrecte"

Quand je conteste la décision via le formulaire d'appel

Alors un modérateur senior revoit la campagne Et il peut approuver si la classification est en fait correcte Et le délai de réponse est de 48-72h

## 24. Notification temps réel pour modérateurs

Étant donné que je suis modérateur connecté

Quand une nouvelle campagne est soumise

Alors je reçois une notification in-app Et le compteur "Campagnes en attente" s'incrémente en temps réel Et je peux cliquer pour consulter immédiatement

## 25. Statistiques conformité par catégorie

Étant donné que je suis admin modération

Quand je consulte les statistiques mensuelles

Alors je vois les motifs de refus:

    | Motif | Nombre | Pourcentage |
    |---|---|---|
    | Alcool | 15 | 30% |
    | Qualité audio | 12 | 24% |
    | Classification erronée | 10 | 20% |
    | Publicité mensongère | 8 | 16% |
    | Autres | 5 | 10% |

## 26. Export rapport modération

Étant donné que je suis modérateur senior

Quand j'exporte le rapport mensuel

Alors je reçois un fichier CSV avec:

    | Colonne |
    |---|
    | Campagne ID |
    | Publicitaire |
    | Date soumission |
    | Date décision |
    | Statut |
    | Motif (si refus) |
    | Modérateur |

Et je peux l'analyser dans Excel

## 27. Validation partielle avec demande modification

Étant donné qu'une campagne a un contenu acceptable Mais que la classification d'âge est incorrecte

Quand le modérateur clique sur "Demander modification"

Alors le publicitaire reçoit un email: Et le statut devient "Modification requise" Et le publicitaire peut modifier sans repayer

## 28. 📋 Plan: Contenus interdits automatiquement détectés

Étant donné qu'une publicité contient le mot

Quand la transcription automatique est analysée

Alors un flag est ajouté Et le motif de refus suggéré est

📊 Exemples de données:

mot_cle | flag | motif
--- | --- | ---
whisky | ⚠️ Alcool | Contenu interdit: Alcool
vodka | ⚠️ Alcool | Contenu interdit: Alcool
cigarette | ⚠️ Tabac | Contenu interdit: Tabac
casino | ⚠️ Jeux argent | Contenu interdit: Jeux
paris sportifs | ⚠️ Jeux argent | Contenu interdit: Jeux

## 29. 📋 Plan: Délais de validation selon soumission

Étant donné qu'une campagne est soumise à

Quand elle est validée heures plus tard

Alors le statut est

📊 Exemples de données:

jour | heure | delai | conformite
--- | --- | --- | ---
Lundi | 10h | 24 | Dans les délais (24h)
Lundi | 10h | 48 | Dans les délais (48h)
Lundi | 10h | 50 | Hors délais (>48h)
Vendredi | 16h | 72 | Dans les délais (we)

# Architecture technique radio live

> En tant que système Je veux gérer efficacement les flux audio en temps réel Afin d'assurer une diffusion stable et scalable des lives

24 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'infrastructure RoadWave est opérationnelle Et que les serveurs Go avec Pion WebRTC sont actifs

## 1. Ingestion WebRTC du flux créateur

Étant donné qu'un créateur démarre un live depuis son application mobile

Quand le flux audio WebRTC (Opus 48 kbps) arrive sur le serveur

Alors le serveur Go avec Pion WebRTC accepte la connexion Et le flux est traité en temps réel

## 2. Conversion temps réel Opus vers segments HLS

Étant donné qu'un flux WebRTC Opus est reçu par le serveur

Quand le serveur traite le flux

Alors FFmpeg convertit en segments HLS (.ts) Et un fichier manifest .m3u8 est généré et mis à jour régulièrement Et les segments ont une durée de 2 secondes chacun

## 3. Distribution via Bunny CDN

Étant donné que les segments HLS sont générés

Quand un auditeur demande à rejoindre le live

Alors le manifest .m3u8 est servi via Bunny CDN Et les segments .ts sont cachés sur le CDN Et la distribution est globale avec latence minimale

## 4. Lecture HLS native sur mobile iOS

Étant donné qu'un auditeur iOS rejoint un live

Quand l'application charge le flux HLS

Alors le player natif AVPlayer gère la lecture Et le buffer de 15 secondes est appliqué automatiquement Et la qualité s'adapte selon la connexion

## 5. Lecture HLS native sur mobile Android

Étant donné qu'un auditeur Android rejoint un live

Quand l'application charge le flux HLS

Alors le player natif ExoPlayer gère la lecture Et le buffer de 15 secondes est configuré Et la qualité s'adapte selon la connexion

## 6. Enregistrement parallèle du flux pour replay

Étant donné qu'un live est en cours

Alors un processus parallèle enregistre le flux Opus raw Et l'enregistrement est stocké temporairement sur le serveur Et l'enregistrement est indépendant de la diffusion HLS

## 7. Traitement post-live asynchrone

Étant donné qu'un live vient de se terminer

Quand le processus post-live démarre

Alors un job asynchrone est créé dans la queue Redis Et un worker Go récupère le job Et le worker exécute FFmpeg pour les conversions

## 8. Conversion Opus raw vers MP3 256 kbps

Étant donné qu'un worker traite un job post-live

Quand la conversion démarre

Alors FFmpeg convertit Opus raw en MP3 256 kbps Et la normalisation audio à -14 LUFS est appliquée Et les silences prolongés (>3 secondes) sont détectés et nettoyés

## 9. Génération segments HLS pour le replay

Étant donné que le MP3 256 kbps est généré

Quand le worker crée les segments HLS

Alors des segments .ts de 10 secondes sont créés Et un manifest .m3u8 est généré Et les segments sont uploadés vers le stockage Bunny

## 10. Publication automatique du replay

Étant donné que tous les segments HLS sont uploadés

Quand le worker finalise le job

Alors une entrée de contenu "replay" est créée en base PostgreSQL Et le titre est "[REPLAY] [Titre live original]" Et le type géographique est "Géo-neutre" Et le replay est immédiatement disponible pour les auditeurs

## 11. Suppression automatique fichier Opus raw après 7 jours

Étant donné qu'un replay est publié depuis 7 jours

Quand le job de nettoyage quotidien s'exécute

Alors le fichier Opus raw est supprimé du stockage Et seul le MP3 256 kbps et les segments HLS sont conservés Et l'espace de stockage est libéré

## 12. Scalabilité horizontale des workers de conversion

Étant donné que 50 lives se terminent simultanément

Quand les jobs post-live sont créés

Alors les workers Go disponibles traitent les jobs en parallèle Et si tous les workers sont occupés, les jobs attendent en queue Redis Et de nouveaux workers peuvent être lancés automatiquement (Kubernetes)

## 13. Limitation du nombre de lives simultanés (MVP)

Étant donné que l'infrastructure MVP est configurée pour 100 lives simultanés Et que 100 lives sont actuellement en cours

Quand un nouveau créateur essaie de démarrer un live

Alors la demande est refusée avec le code erreur 503 Et le message "Capacité maximale atteinte. Veuillez réessayer dans quelques minutes" est retourné Et la demande peut être mise en queue prioritaire si créateur Premium

## 14. Monitoring des ressources serveur en temps réel

Étant donné que plusieurs lives sont en cours

Alors le système monitore en temps réel:

    | métrique | seuil alerte |
    |---|---|
    | CPU utilisation | >80% |
    | Mémoire utilisation | >85% |
    | Bande passante upload | >80% capacité |
    | Nombre connexions WebRTC | >90 |
    | Latence moyenne CDN | >200ms |

Et si un seuil est dépassé, une alerte est envoyée à l'équipe technique

## 15. Calcul du coût de bande passante CDN

Étant donné qu'un live a 100 auditeurs simultanés Et que la qualité est 48 kbps Opus

Quand le live dure 1 heure

Alors la bande passante totale est d'environ 2.16 GB Et le coût Bunny CDN est d'environ 0.02€ (tarif ~0.01€/GB) Et ces métriques sont enregistrées pour facturation créateur si nécessaire

## 16. Cache CDN des segments HLS

Étant donné qu'un live est diffusé via Bunny CDN

Quand un segment .ts est généré

Alors le segment est uploadé vers Bunny origin Et Bunny CDN cache le segment sur ses edge servers Et les auditeurs suivants récupèrent le segment depuis le cache Et la charge sur le serveur origin est réduite de ~90%

## 17. Gestion de la latence WebRTC créateur

Étant donné qu'un créateur diffuse avec une connexion 4G

Quand la latence réseau augmente ponctuellement

Alors le buffer côté serveur absorbe les fluctuations Et la qualité peut être réduite temporairement (48 kbps → 32 kbps) Et un warning est affiché au créateur si la connexion est trop instable

## 18. Détection automatique de la musique protégée (post-MVP)

Étant donné qu'un live contient de la musique en arrière-plan

Quand le système d'audio fingerprint analyse le flux

Alors une empreinte audio est calculée toutes les 30 secondes Et l'empreinte est comparée à une base de données de contenus protégés Et si une correspondance est trouvée, un warning est envoyé au créateur Et si le créateur ne corrige pas sous 30 secondes, le live peut être arrêté

## 19. Stockage des métadonnées de live en PostgreSQL

Étant donné qu'un créateur démarre un live

Alors les métadonnées suivantes sont enregistrées:

    | champ | exemple valeur |
    |---|---|
    | live_id | uuid v4 |
    | creator_id | uuid créateur |
    | title | "Mon super live" |
    | started_at | timestamp UTC |
    | zone_geo | "Île-de-France" |
    | tags | ["Actualité", "Tech"] |
    | classification_age | "Tout public" |

Et ces données sont indexées pour recherche et analytics

## 20. Cache Redis pour compteurs temps réel

Étant donné qu'un live est en cours

Alors Redis stocke les compteurs temps réel:

    | clé Redis | valeur exemple |
    |---|---|
    | live:[live_id]:listeners | 247 |
    | live:[live_id]:likes | 89 |
    | live:[live_id]:reports | 0 |

Et ces compteurs sont mis à jour toutes les 2 secondes Et les compteurs sont persistés en PostgreSQL toutes les 60 secondes

## 21. Heartbeat auditeurs pour compteur précis

Étant donné qu'un auditeur écoute un live

Alors l'application envoie un heartbeat toutes les 10 secondes Et le heartbeat met à jour le timestamp dans Redis Et si aucun heartbeat n'est reçu pendant 30 secondes, l'auditeur est retiré du compteur

## 22. Gestion des pannes serveur pendant un live

Étant donné qu'un live est en cours sur serveur A

Quand le serveur A tombe en panne

Alors Kubernetes redémarre automatiquement un pod Mais le live en cours est perdu (pas de failover temps réel en MVP) Et le créateur voit le message "Connexion perdue. Veuillez redémarrer le live" Et les auditeurs voient "Le live est terminé suite à un problème technique"

## 23. Backup automatique des enregistrements live

Étant donné qu'un live est enregistré en Opus raw

Quand l'enregistrement dépasse 10 minutes

Alors un backup incrémental est créé toutes les 10 minutes Et le backup est stocké sur un stockage secondaire (S3-compatible) Et en cas de crash serveur, le live peut être récupéré jusqu'au dernier backup

## 24. Logs et audit trail des lives

Étant donné qu'un live démarre, se déroule et se termine

Alors tous les événements sont loggés:

    | événement | détails enregistrés |
    |---|---|
    | Démarrage live | timestamp, creator_id, zone_geo |
    | Auditeur rejoint | timestamp, user_id, position GPS |
    | Auditeur quitte | timestamp, user_id, durée écoute |
    | Signalement | timestamp, user_id, catégorie |
    | Fin live | timestamp, durée totale, stats finales |

Et ces logs sont conservés 90 jours pour analytics et conformité RGPD

# Arrêt du live

> En tant que créateur Je veux arrêter ma diffusion en direct de manière contrôlée Afin de terminer proprement mon live et générer un replay automatiquement

19 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et que je suis connecté en tant que créateur Et que je diffuse actuellement un live

## 1. Arrêt manuel avec compte à rebours 5 secondes

Quand j'appuie sur le bouton "Arrêter live"

Alors un compte à rebours de 5 secondes démarre Et je vois le message "Ce live se termine dans 5... 4... 3... 2... 1" Et un bouton "Annuler" est affiché pendant le décompte Et l'audio du compte à rebours est diffusé aux auditeurs

## 2. Annulation du compte à rebours

Étant donné que j'ai appuyé sur "Arrêter live" Et que le compte à rebours affiche "3 secondes"

Quand j'appuie sur "Annuler"

Alors le compte à rebours s'arrête Et le live continue normalement Et aucune notification n'est envoyée aux auditeurs

## 3. Arrêt effectif après compte à rebours

Étant donné que le compte à rebours est à 0

Alors le live s'arrête Et la diffusion aux auditeurs se termine Et le message "Live terminé" s'affiche Et le processus de traitement post-live démarre automatiquement

## 4. Déconnexion créateur courte (moins de 60 secondes)

Étant donné que je diffuse un live

Quand ma connexion est perdue pendant 30 secondes

Alors les auditeurs voient le message "Connexion créateur perdue, reconnexion en cours..." Et le live continue de bufferer Et quand ma connexion revient, le live reprend normalement

## 5. Déconnexion créateur longue (60 secondes ou plus)

Étant donné que je diffuse un live

Quand ma connexion est perdue pendant 60 secondes

Alors le live s'arrête automatiquement Et les auditeurs voient le message "Le live est terminé suite à une coupure de connexion" Et le processus de traitement post-live démarre

## 6. Enregistrement automatique pendant le live

Étant donné que je diffuse un live

Alors mon flux audio est enregistré en continu Et le format d'enregistrement est Opus raw Et l'enregistrement est stocké temporairement sur le serveur

## 7. Génération automatique du replay après arrêt

Étant donné que mon live vient de se terminer Et que l'option "Publier replay automatiquement" est activée (par défaut)

Quand le traitement post-live démarre

Alors un job asynchrone est créé Et le job effectue les opérations suivantes:

    | opération | détail |
    |---|---|
    | Conversion format | Opus raw → MP3 256 kbps |
    | Génération segments HLS | Segments .ts pour streaming |
    | Normalisation volume | -14 LUFS |
    | Détection silences prolongés | Nettoyage automatique |

## 8. Publication du replay

Étant donné que le traitement post-live est terminé

Alors le replay est publié automatiquement sous 5 à 10 minutes Et le titre est "[REPLAY] [Titre live original]" Et la zone de diffusion est la même que le live Et les tags sont identiques au live Et la classification d'âge est identique Et le type géographique est "Géo-neutre" (contenu pérenne)

## 9. Notification de disponibilité du replay aux auditeurs

Étant donné que le replay de mon live est publié

Quand un auditeur qui a écouté le live se reconnecte

Alors il voit une notification in-app "Le replay de [Titre] est disponible"

## 10. Option désactivation publication automatique replay

Étant donné que je configure un nouveau live

Quand je désactive l'option "Publier replay automatiquement" Et que je démarre puis arrête le live

Alors le live est enregistré Mais le replay n'est pas publié automatiquement Et je peux décider manuellement de le publier plus tard

## 11. Suppression manuelle du replay après publication

Étant donné que mon live a généré un replay publié

Quand j'accède à mes contenus

Alors je vois le replay dans ma liste Et je peux le supprimer comme n'importe quel contenu

Quand je supprime le replay

Alors le fichier source Opus raw est supprimé immédiatement

## 12. Conservation fichier source Opus raw

Étant donné que mon live est terminé Et que le replay est publié

Alors le fichier Opus raw est conservé pendant 7 jours Et après 7 jours, le fichier raw est supprimé automatiquement Et seul le MP3 256 kbps est conservé

## 13. Modification du replay interdite

Étant donné que mon live a généré un replay publié

Quand j'essaie de modifier l'audio du replay

Alors l'action est refusée Et je vois le message "Les replays ne peuvent pas être modifiés pour garantir l'intégrité de l'enregistrement" Et je peux uniquement modifier les métadonnées (titre, description)

## 14. Statistiques du live disponibles après arrêt

Étant donné que mon live est terminé

Quand j'accède aux statistiques

Alors je vois:

    | métrique | exemple valeur |
    |---|---|
    | Durée totale | 1h 23min |
    | Nombre d'auditeurs max | 247 |
    | Nombre d'auditeurs moyen | 183 |
    | Nombre de likes | 89 |
    | Nombre d'abonnements | 12 |
    | Signalements reçus | 0 |

## 15. Live terminé avec signalements en cours

Étant donné que mon live a reçu 3 signalements pendant la diffusion

Quand le live se termine

Alors le replay n'est pas publié automatiquement Et le contenu est en attente de modération Et je vois le message "Votre replay sera publié après vérification suite aux signalements reçus" Et un modérateur doit valider ou refuser le replay sous 24h

## 16. Arrêt forcé par un modérateur

Étant donné que je diffuse un live Et qu'un modérateur détecte du contenu interdit

Quand le modérateur clique sur "Arrêter le live immédiatement"

Alors le live s'arrête sans compte à rebours Et je vois le message "Votre live a été interrompu par la modération" Et je reçois une notification détaillant la raison Et le replay n'est pas publié Et le fichier source est conservé 30 jours pour appel

## 17. Métriques de bande passante pendant le live

Étant donné que je diffuse un live Et que 100 auditeurs écoutent simultanément

Alors la bande passante consommée est d'environ 4.8 Mbps via CDN Et le coût estimé Bunny CDN est d'environ 0.02€ par heure de diffusion Et je peux voir ces métriques en temps réel dans l'interface créateur

## 18. Live sans auditeurs pendant 5 minutes

Étant donné que je diffuse un live Et qu'aucun auditeur n'écoute depuis 5 minutes

Alors je vois un message d'information "Aucun auditeur actuellement connecté" Mais le live continue normalement Et je peux choisir de continuer ou d'arrêter

## 19. Qualité audio du replay supérieure au live

Étant donné que mon live était diffusé en Opus 48 kbps

Quand le replay est généré

Alors le replay est encodé en MP3 256 kbps Et la qualité audio du replay est supérieure au live Et la taille du fichier est optimisée pour le stockage long terme

# Comportement auditeur pendant un live

> En tant qu'auditeur Je veux écouter des lives de manière stable Afin de profiter du contenu en temps réel sans coupures

27 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et que je suis connecté en tant qu'auditeur Et qu'un créateur diffuse actuellement un live

## 1. Rejoindre un live avec buffer de synchronisation 15 secondes

Quand je clique sur "Rejoindre le live"

Alors la connexion au flux HLS s'établit Et je commence à écouter avec un décalage de 15 secondes par rapport au créateur Et le buffer de 15 secondes garantit une lecture stable

## 2. Justification du buffer 15 secondes

Étant donné les alternatives de buffer possibles:

    | buffer | stabilité 3G | stabilité 4G | décalage perceptible | décision |
    |---|---|---|---|---|
    | 5s | Faible | Moyenne | Non | ❌ |
    | 10s | Moyenne | Bonne | Non | ❌ |
    | 15s | Bonne | Excellente | Léger acceptable | ✅ |
    | 20s+ | Excellente | Excellente | Oui | ❌ |

Alors le buffer optimal est 15 secondes

## 3. Lecture stable sur réseau 3G

Étant donné que je suis sur réseau 3G Et que j'écoute un live

Quand des micro-coupures réseau surviennent

Alors le buffer de 15 secondes absorbe les coupures Et la lecture continue sans interruption perceptible

## 4. Lecture stable sur réseau 4G

Étant donné que je suis sur réseau 4G Et que j'écoute un live

Alors la lecture est fluide Et le buffer de 15 secondes prévient les coupures lors de changement de cellule

## 5. Continuation du live en sortant de la zone géographique

Étant donné que j'écoute un live régional "Île-de-France" Et que je suis situé en Île-de-France

Quand je me déplace et sors du département

Alors le live continue de jouer normalement Et je peux écouter jusqu'à la fin naturelle du live Et après la fin du live, l'algorithme propose du contenu correspondant à ma nouvelle position

## 6. Abonné dans la zone reçoit notification push

Étant donné que je suis abonné au créateur "JeanDupont" Et que je suis situé en Île-de-France

Quand "JeanDupont" démarre un live en Île-de-France

Alors je reçois une notification push "🔴 JeanDupont est en direct : [Titre du live]" Et quand je tape sur la notification, l'app s'ouvre et le live démarre immédiatement

## 7. Abonné hors zone ne reçoit pas de notification

Étant donné que je suis abonné au créateur "JeanDupont" Et que je suis situé à Lyon

Quand "JeanDupont" démarre un live en Île-de-France

Alors je ne reçois pas de notification push Et cela évite la frustration de ne pas pouvoir écouter un live hors zone

## 8. Découverte d'un live via l'algorithme de recommandation

Étant donné que je suis dans la zone géographique du live Et que je navigue dans l'app avec "Suivant"

Quand l'algorithme propose un live en cours

Alors je vois l'indicateur "🔴 EN DIRECT" Et je peux choisir de le rejoindre ou de passer au suivant

## 9. Reconnexion rapide après coupure réseau (moins de 90 secondes)

Étant donné que j'écoute un live

Quand je perds ma connexion réseau pendant 45 secondes Et que je retrouve ma connexion

Alors je reprends le live au moment actuel (pas au buffer ancien) Et le saut temporel est transparent (pas de message d'erreur) Et je ne rate que quelques secondes de contenu

## 10. Reconnexion longue après coupure réseau (90 secondes ou plus)

Étant donné que j'écoute un live

Quand je perds ma connexion réseau pendant 90 secondes Et que je retrouve ma connexion

Alors je vois le message "Live en cours perdu, passage au contenu suivant" Et l'algorithme propose automatiquement le contenu suivant Et je peux manuellement revenir au live s'il est toujours en cours

## 11. Interactions disponibles pendant le live - Like

Étant donné que j'écoute un live Et que mon véhicule est à l'arrêt

Quand je clique sur le bouton "❤️ Like"

Alors le like est enregistré immédiatement Et le compteur de likes visible par le créateur s'incrémente Et ma jauge d'intérêt pour les tags du live augmente de +2%

## 12. Interactions disponibles pendant le live - Abonnement

Étant donné que j'écoute un live Et que je ne suis pas encore abonné au créateur

Quand je clique sur le bouton "S'abonner"

Alors je m'abonne au créateur Et ma jauge d'intérêt pour tous les tags du créateur augmente de +5% Et je recevrai des notifications pour ses prochains lives

## 13. Interactions disponibles pendant le live - Skip

Étant donné que j'écoute un live

Quand j'appuie sur "Suivant" (ou commande au volant)

Alors je quitte le live immédiatement Et l'algorithme propose le contenu suivant Et si j'ai écouté moins de 10 secondes, ma jauge d'intérêt diminue de -0.5%

## 14. Commande Précédent désactivée pendant un live

Étant donné que j'écoute un live

Quand j'appuie sur "Précédent" (ou commande au volant)

Alors rien ne se passe Et un message d'information s'affiche brièvement "Précédent non disponible sur les lives"

## 15. Chat en direct désactivé (décision définitive)

Étant donné que j'écoute un live

Alors aucune interface de chat n'est disponible Et je ne peux pas envoyer de messages au créateur Et je ne peux pas voir de messages d'autres auditeurs Et cette fonctionnalité ne sera jamais implémentée

## 16. Réactions emoji désactivées (décision définitive)

Étant donné que j'écoute un live

Alors aucune réaction emoji n'est disponible Et je ne peux pas envoyer d'emoji en temps réel Et cette fonctionnalité ne sera jamais implémentée

## 17. Message d'information sur l'absence de chat

Étant donné que j'écoute mon premier live

Quand j'accède à l'interface du live

Alors je vois un bandeau informatif "💬 Les discussions ne sont pas disponibles sur RoadWave pour garantir votre sécurité en voiture et éviter le harcèlement." Et ce bandeau n'apparaît qu'une seule fois (première expérience)

## 18. Signalement d'un live en cours

Étant donné que j'écoute un live Et que le contenu me semble inapproprié

Quand je clique sur le bouton "Signaler"

Alors je vois les catégories de signalement:

    | catégorie |
    |---|
    | Haine et violence |
    | Contenu sexuel |
    | Illégalité |
    | Droits d'auteur |
    | Désinformation dangereuse |
    | Harcèlement |
    | Autre |

Et quand je sélectionne une catégorie Alors le signalement est envoyé en priorité selon la catégorie Et un modérateur peut écouter le live en temps réel si besoin

## 19. Statistiques visibles par les auditeurs pendant le live

Étant donné que j'écoute un live

Quand je consulte les informations du live

Alors je vois:

    | information | exemple valeur |
    |---|---|
    | Nombre d'auditeurs | 247 personnes |
    | Durée du live | 1h 23min |
    | Nom du créateur | @JeanDupont |
    | Zone de diffusion | Île-de-France |
    | Tags | Actualité, Société |

Mais je ne vois pas les likes ou autres métriques détaillées

## 20. Compteur d'auditeurs arrondi pour préserver la vie privée

Étant donné que j'écoute un live avec exactement 247 auditeurs

Quand je consulte le nombre d'auditeurs

Alors je vois "~250 auditeurs" (arrondi à la dizaine supérieure)

## 21. Qualité audio adaptative pendant le live

Étant donné que j'écoute un live

Quand ma connexion passe de 4G à 3G

Alors la qualité audio s'adapte automatiquement Et je passe de 48 kbps à 24 kbps Opus Et la transition est transparente sans coupure

## 22. Consommation de données pendant un live

Étant donné que j'écoute un live en qualité standard 48 kbps Et que j'écoute pendant 1 heure

Alors j'ai consommé environ 21.6 MB de données mobiles Et cette consommation est affichée dans les paramètres de l'app

## 23. Lecture du replay après la fin du live

Étant donné que j'écoute un live depuis 30 minutes

Quand le créateur arrête le live

Alors je vois le message "Le live est terminé. Le replay sera disponible dans quelques minutes" Et le contenu suivant est automatiquement proposé après 2 secondes

## 24. Notification de disponibilité du replay

Étant donné que j'ai écouté un live jusqu'à la fin Et que le replay est publié 8 minutes plus tard

Quand je rouvre l'application

Alors je vois une notification in-app "Le replay de [Titre] est maintenant disponible" Et je peux cliquer pour l'écouter immédiatement

## 25. Aucune publicité pendant un live pour utilisateurs gratuits

Étant donné que je suis un utilisateur gratuit Et que j'écoute un live

Alors aucune publicité n'est insérée pendant le live Et la publicité apparaît seulement entre le live et le contenu suivant

## 26. Détection de contexte voiture pendant un live

Étant donné que j'écoute un live Et que ma vitesse est supérieure à 10 km/h

Alors l'interface tactile est désactivée pour la sécurité Et seules les commandes au volant sont actives (Play/Pause/Suivant)

## 27. Détection de contexte piéton pendant un live

Étant donné que j'écoute un live Et que ma vitesse est inférieure à 5 km/h

Alors l'interface tactile complète est disponible Et je peux liker, m'abonner, signaler via l'écran tactile

# Démarrage d'un live

> En tant que créateur Je veux démarrer une diffusion en direct Afin de partager du contenu audio en temps réel avec mes auditeurs

20 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et que je suis connecté en tant que créateur vérifié Et que j'ai les permissions de diffusion live

## 1. Vérifications pré-live réussies

Étant donné que ma connexion upload est supérieure à 1 Mbps Et que j'ai autorisé l'accès au microphone Et que j'ai défini une zone de diffusion "Île-de-France"

Quand je lance les vérifications pré-live

Alors toutes les vérifications sont validées Et je peux démarrer le live

## 2. Échec pré-live avec connexion insuffisante

Étant donné que ma connexion upload est de 0.5 Mbps

Quand je lance les vérifications pré-live

Alors je vois un warning "Connexion insuffisante pour garantir une diffusion stable (minimum 1 Mbps)" Et je peux choisir de continuer quand même ou d'annuler

## 3. Échec pré-live sans autorisation microphone

Étant donné que je n'ai pas autorisé l'accès au microphone

Quand j'essaie de démarrer un live

Alors je vois le message "Accès au microphone requis pour démarrer un live" Et je suis redirigé vers les paramètres système

## 4. Échec pré-live sans zone de diffusion définie

Étant donné que je n'ai pas défini de zone de diffusion

Quand j'essaie de démarrer un live

Alors je vois le message "Veuillez définir une zone de diffusion avant de démarrer" Et je suis redirigé vers le formulaire de configuration du live

## 5. Démarrage live avec buffer 15 secondes

Étant donné que toutes les vérifications pré-live sont validées

Quand j'appuie sur "Démarrer live"

Alors je vois le message "Live démarre dans 15s... Testez votre micro" Et un compte à rebours de 15 secondes s'affiche Et mon flux audio est enregistré pendant ces 15 secondes Et le live n'est pas encore visible publiquement

## 6. Live devient public après buffer initial

Étant donné que j'ai démarré un live Et que le buffer de 15 secondes s'est écoulé

Alors le live devient public Et les auditeurs peuvent le rejoindre Et les abonnés dans la zone reçoivent une notification push

## 7. Notification push aux abonnés dans la zone géographique

Étant donné que j'ai 1000 abonnés au total Et que 300 abonnés sont situés en Île-de-France Et que 700 abonnés sont situés hors Île-de-France

Quand mon live en Île-de-France devient public

Alors 300 abonnés reçoivent une notification push "🔴 [Mon pseudo] est en direct : [Titre live]" Et 700 abonnés ne reçoivent pas de notification

## 8. Configuration métadonnées obligatoires pour un live

Quand je configure un nouveau live

Alors je dois renseigner:

    | champ | format | validation |
    |---|---|---|
    | Titre | 5-100 caractères | Obligatoire |
    | Tags | 1-3 centres intérêt | Sélection liste prédéfinie |
    | Classification âge | Enum | Tout public / 13+ / 16+ / 18+ |
    | Zone diffusion | Geo | Ville / Département / Région / National |

## 9. Validation échouée avec titre trop court

Quand j'essaie de créer un live avec le titre "Live"

Alors la validation échoue Et je vois le message "Le titre doit contenir entre 5 et 100 caractères"

## 10. Validation échouée sans tags

Étant donné que j'ai rempli tous les champs sauf les tags

Quand j'essaie de démarrer le live

Alors la validation échoue Et je vois le message "Veuillez sélectionner entre 1 et 3 centres d'intérêt"

## 11. Limite de durée 8 heures

Étant donné que mon live dure depuis 7 heures et 30 minutes

Alors je vois un warning "Votre live se terminera dans 30 min" Et le message est affiché de manière non intrusive

## 12. Arrêt automatique à 8 heures

Étant donné que mon live dure depuis 8 heures

Alors le live s'arrête automatiquement Et je vois le message "Durée maximale atteinte (8 heures). Vous pouvez redémarrer un nouveau live si nécessaire" Et le processus de traitement post-live démarre

## 13. Diffusion contenu interdit - Concert en direct

Étant donné que je diffuse un concert en direct depuis une salle Et qu'un auditeur signale le contenu pour "Violation droits d'auteur"

Quand un modérateur écoute le live Et qu'il confirme la violation

Alors le live est arrêté immédiatement Et je reçois un Strike 2 (suspension 7 jours) Et je vois le message "Votre live a été interrompu pour violation des droits d'auteur" Et le replay n'est pas publié

## 14. Diffusion contenu interdit - Événement sportif payant

Étant donné que je diffuse un match de football avec droits TV Et que le contenu est détecté par l'IA audio fingerprint

Quand la détection est confirmée

Alors le live est arrêté immédiatement Et je reçois un Strike 2 (suspension 7 jours)

## 15. Diffusion contenu violent

Étant donné que je diffuse du contenu violent (agression physique) Et que 5 auditeurs signalent le contenu

Quand un modérateur vérifie en temps réel Et confirme la violence

Alors le live est coupé immédiatement Et mon compte est banni définitivement Et les autorités sont notifiées

## 16. Détection musique protégée en arrière-plan

Étant donné que mon live contient de la musique protégée en fond

Quand l'IA audio fingerprint détecte la violation après 2 minutes

Alors je reçois un avertissement en direct "Musique protégée détectée. Veuillez couper le son ou risquez un arrêt du live" Et j'ai 30 secondes pour corriger Et si je ne corrige pas, le live est arrêté avec Strike 1

## 17. Signalement pendant un live

Étant donné que je diffuse un live Et qu'un auditeur clique sur "Signaler"

Quand l'auditeur sélectionne la catégorie "Harcèlement"

Alors le signalement est envoyé en priorité HAUTE Et un modérateur peut écouter le live en temps réel Et le live continue pendant l'écoute de vérification

## 18. Dépassement nombre de lives simultanés autorisés (limite plateforme)

Étant donné que la plateforme héberge actuellement 2000 lives simultanés Et que c'est la limite de l'infrastructure actuelle

Quand j'essaie de démarrer un nouveau live

Alors je vois le message "Capacité maximale atteinte. Veuillez réessayer dans quelques minutes" Et ma demande est mise en file d'attente prioritaire si je suis créateur Premium

## 19. Premier live d'un nouveau créateur

Étant donné que je n'ai jamais diffusé de live auparavant Et que j'ai moins de 3 contenus validés

Quand j'essaie de démarrer mon premier live

Alors je vois le message "Les lives sont disponibles après validation de vos 3 premiers contenus" Et le bouton "Démarrer live" est désactivé

## 20. Créateur avec score de confiance faible

Étant donné que j'ai 2 strikes actifs

Quand j'essaie de démarrer un live

Alors je vois le message "Fonctionnalité live temporairement indisponible suite à vos sanctions" Et je dois attendre la fin de ma suspension

# Recherche de contenu

> En tant qu'utilisateur de RoadWave Je veux rechercher des contenus audio par mots-clés, localisation et filtres Afin de trouver facilement le contenu qui m'intéresse

55 scénarios (49 standards, 6 plans)

> Contexte commun à tous les scénarios

> Étant donné que l'application RoadWave est démarrée Et que l'utilisateur "jean@example.com" est connecté

## 1. Recherche full-text basique

Étant donné que la base contient les contenus suivants:

    | titre | description | créateur |
    |---|---|---|
    | Balade à Paris | Visite du quartier Latin | @paris_stories |
    | Secrets de Montmartre | Histoire de la butte | @explore_paris |
    | Voyage en Normandie | Découverte des plages | @voyages_fr |

Quand l'utilisateur recherche "paris"

Alors 2 résultats sont retournés Et les résultats incluent "Balade à Paris" Et les résultats incluent "Secrets de Montmartre"

## 2. Recherche avec stemming français

Étant donné un contenu avec le titre "Voyage en Bretagne"

Quand l'utilisateur recherche "voyages"

Alors le contenu "Voyage en Bretagne" est trouvé Et le stemming a transformé "voyages" en racine "voyag"

## 3. 📋 Plan: Stemming français sur différentes formes

Étant donné un contenu avec le mot ""

Quand l'utilisateur recherche ""

Alors le contenu est trouvé grâce au stemming français

📊 Exemples de données:

mot_original | recherche
--- | ---
voyage | voyages
voyager | voyage
balades | balade
historique | histoire

## 4. Recherche avec accents ignorés

Étant donné un contenu avec le titre "Découverte de l'Élysée"

Quand l'utilisateur recherche "decouverte elysee"

Alors le contenu est trouvé Et les accents sont normalisés automatiquement

## 5. Champs indexés avec pondération

Étant donné les contenus suivants:

    | titre | description | créateur | tags |
    |---|---|---|---|
    | Voyage Paris | Balade sympa | @user1 | Tourisme |
    | Balade Lyon | Voyage en ville | @paris_guide | Voyage |

Quand l'utilisateur recherche "paris"

Alors "Voyage Paris" est en première position Et "@paris_guide" apparaît en second

## 6. Ranking par pertinence et popularité

Étant donné les contenus suivants:

    | titre | écoutes | rang_texte |
    |---|---|---|
    | Balade Paris | 50000 | 0.8 |
    | Paris la nuit | 1000 | 0.9 |

Quand l'utilisateur recherche "paris"

Alors le score final combine rang_texte × (1 + log(écoutes + 1)) Et "Balade Paris" est mieux classé grâce à sa popularité

## 7. Autocomplete pendant la frappe

Étant donné que l'utilisateur commence à taper "par"

Quand 3 caractères sont saisis

Alors des suggestions apparaissent:

    | suggestion |
    |---|
    | paris |
    | parc naturel |
    | parvis notre-dame |

Et le top 5 des suggestions est affiché

## 8. Historique des 10 dernières recherches

Étant donné que l'utilisateur a effectué les recherches suivantes:

    | recherche | date |
    |---|---|
    | voyage paris | 2026-01-20 |
    | audio-guide louvre | 2026-01-19 |
    | podcast automobile | 2026-01-18 |

Quand l'utilisateur ouvre la barre de recherche

Alors les 10 dernières recherches sont affichées Et elles sont triées par date décroissante

## 9. Correction automatique si aucun résultat

Étant donné que l'utilisateur recherche "ballade paris" (faute d'orthographe) Et qu'aucun résultat n'est trouvé

Quand la page de résultats s'affiche

Alors une suggestion "Essayez plutôt : balade paris" est affichée

## 10. Recherches populaires suggérées

Étant donné qu'aucun résultat n'est trouvé pour une recherche

Quand la page s'affiche

Alors des suggestions populaires sont affichées:

    | suggestion |
    |---|
    | balade paris |
    | audio-guide louvre |
    | visite montmartre |

## 11. Saisie d'un lieu avec autocomplete

Étant donné que l'utilisateur ouvre le filtre "Lieu"

Quand il tape "Louv"

Alors Nominatim retourne des suggestions:

    | suggestion | type |
    |---|---|
    | Musée du Louvre, Paris | monument |
    | Louvres, Val-d'Oise | commune |

## 12. Sélection d'un lieu et définition du rayon

Étant donné que l'utilisateur sélectionne "Paris, France" Et que les coordonnées sont (48.8566, 2.3522)

Quand il définit un rayon de 50 km

Alors la recherche PostGIS utilise ST_DWithin avec 50000 mètres

## 13. 📋 Plan: Recherche géographique avec différents rayons

Étant donné un contenu à 30 km de Paris

Quand l'utilisateur recherche autour de Paris avec un rayon de

Alors le contenu est

📊 Exemples de données:

rayon | résultat
--- | ---
20 km | non trouvé
50 km | trouvé
100 km | trouvé

## 14. Utilisation de "Autour de moi" (GPS actuel)

Étant donné que l'utilisateur active le GPS Et que sa position est (48.8566, 2.3522)

Quand il sélectionne "Autour de moi"

Alors la recherche utilise ses coordonnées GPS actuelles Et un rayon par défaut de 10 km est appliqué

## 15. Curseur de rayon avec limites

Étant donné que l'utilisateur ouvre le curseur de rayon

Quand il ajuste le curseur

Alors les valeurs disponibles vont de 5 km à 500 km Et la valeur s'affiche en temps réel "50 km"

## 16. Affichage de la distance dans les résultats

Étant donné une recherche géographique autour de Paris Et un contenu à 2.3 km de distance

Quand les résultats sont affichés

Alors la distance "À 2.3 km" est indiquée pour chaque résultat

## 17. 📋 Plan: Tri par proximité géographique

Étant donné des contenus à différentes distances de Paris:

    | contenu | distance |
    |---|---|
    | Louvre Guide | 0.5 km |
    | Tour Eiffel | 2.0 km |
    | Versailles | 20 km |

Quand l'utilisateur trie par "Proximité"

Alors les résultats sont affichés dans l'ordre:

    | position | contenu |
    |---|---|
    | 1 | Louvre Guide |
    | 2 | Tour Eiffel |
    | 3 | Versailles |

## 18. Géocodage avec Nominatim (MVP)

Étant donné que l'application est en phase MVP

Quand une requête de géocodage est effectuée

Alors l'API publique Nominatim est utilisée Et le rate limit de 1 req/s est respecté

## 19. Géocodage avec fallback Mapbox

Étant donné que Nominatim ne retourne aucun résultat

Quand l'application tente un fallback

Alors l'API Mapbox Geocoding est utilisée Et le coût de 0.50€ / 1000 requêtes est appliqué

## 20. Ouverture du panneau de filtres

Étant donné que l'utilisateur est sur la page de recherche

Quand il clique sur "Filtres"

Alors un panneau latéral s'ouvre Et 7 catégories de filtres sont affichées:

    | catégorie |
    |---|
    | Type de contenu |
    | Durée |
    | Classification âge |
    | Géo-pertinence |
    | Tags |
    | Date de publication |
    | Abonnement |

## 21. Filtre par type de contenu (multi-sélection)

Étant donné que l'utilisateur ouvre les filtres

Quand il sélectionne:

    | type |
    |---|
    | Contenu court |
    | Audio-guide |

Alors seuls ces types de contenus sont recherchés Et les podcasts et radios live sont exclus

## 22. 📋 Plan: Filtre par durée

Étant donné un contenu de minutes

Quand l'utilisateur filtre par ""

Alors le contenu est

📊 Exemples de données:

durée | tranche | résultat
--- | --- | ---
3 | <5 min | trouvé
3 | 5-15 min | non trouvé
10 | 5-15 min | trouvé
20 | 15-30 min | trouvé
45 | >30 min | trouvé

## 23. Filtre par classification âge

Étant donné des contenus avec différentes classifications:

    | contenu | classification |
    |---|---|
    | Conte enfants | Tout public |
    | Podcast news | 13+ |
    | Débat politique | 16+ |

Quand l'utilisateur filtre "Tout public"

Alors seul "Conte enfants" est affiché

## 24. Filtre par géo-pertinence

Étant donné des contenus avec différents types géo:

    | contenu | type_geo |
    |---|---|
    | Guide Louvre | Ancré |
    | Podcast Paris | Contextuel |
    | News nationales | Neutre |

Quand l'utilisateur filtre "Ancré, Contextuel"

Alors "Guide Louvre" et "Podcast Paris" sont affichés Et "News nationales" est exclu

## 25. Filtre par tags (multi-sélection)

Étant donné des contenus taggés:

    | contenu | tags |
    |---|---|
    | Voyage en Italie | Voyage, Gastronomie |
    | Histoire de Rome | Voyage, Histoire |
    | Économie italienne | Économie |

Quand l'utilisateur sélectionne les tags "Voyage, Histoire"

Alors "Histoire de Rome" est en priorité (2 tags correspondants) Et "Voyage en Italie" est affiché (1 tag correspondant) Et "Économie italienne" est exclu

## 26. 📋 Plan: Filtre par date de publication

Étant donné un contenu publié il y a

Quand l'utilisateur filtre par ""

Alors le contenu est

📊 Exemples de données:

délai | période | résultat
--- | --- | ---
12 heures | Dernières 24h | trouvé
3 jours | Cette semaine | trouvé
15 jours | Ce mois | trouvé
8 mois | Cette année | trouvé
2 ans | Toutes dates | trouvé
2 ans | Cette année | non trouvé

## 27. Filtre par type d'abonnement

Étant donné des contenus gratuits et Premium:

    | contenu | type |
    |---|---|
    | Balade Paris | Gratuit |
    | Visite VIP Louvre | Premium |

Quand l'utilisateur filtre "Premium uniquement 👑"

Alors seul "Visite VIP Louvre" est affiché

## 28. Combinaison de filtres multiples (AND logic)

Étant donné que l'utilisateur applique les filtres:

    | filtre | valeur |
    |---|---|
    | Type | Audio-guide |
    | Durée | 5-15 min |
    | Tags | Voyage |
    | Classification | Tout public |

Quand la recherche est lancée

Alors seuls les contenus respectant TOUS les critères sont affichés

## 29. Réinitialisation des filtres

Étant donné que l'utilisateur a appliqué 5 filtres différents

Quand il clique sur "Réinitialiser"

Alors tous les filtres sont désactivés Et la recherche affiche tous les résultats

## 30. Sauvegarde d'une recherche

Étant donné que l'utilisateur a appliqué plusieurs filtres

Quand il clique sur "💾 Sauvegarder cette recherche" Et qu'il entre le nom "Podcasts voyage Paris"

Alors la recherche est sauvegardée Et elle apparaît dans l'onglet "Recherches sauvegardées"

## 31. Limite de 5 recherches sauvegardées

Étant donné que l'utilisateur a déjà 5 recherches sauvegardées

Quand il tente de sauvegarder une 6ème recherche

Alors un message d'erreur s'affiche Et il doit supprimer une recherche existante avant d'en ajouter une nouvelle

## 32. Notifications pour recherches sauvegardées

Étant donné une recherche sauvegardée "Podcasts voyage Paris" Et que l'utilisateur a activé les notifications

Quand 3 nouveaux contenus correspondants sont publiés

Alors une notification "3 nouveaux contenus dans 'Podcasts voyage Paris'" est envoyée

## 33. 📋 Plan: Options de tri des résultats

Étant donné une recherche avec plusieurs résultats

Quand l'utilisateur sélectionne le tri ""

Alors les résultats sont triés selon

📊 Exemples de données:

option | algorithme
--- | ---
Pertinence | Score recherche × (1 + log(écoutes + 1))
Popularité | Écoutes complètes derniers 30j DESC
Récent | Date publication DESC
Proximité | Distance GPS ASC (si recherche géo)
Durée | Durée audio ASC ou DESC

## 34. Structure d'un résultat de recherche

Étant donné un résultat de recherche

Quand la page est affichée

Alors chaque résultat contient:

    | élément | exemple |
    |---|---|
    | Cover image | 120×68 px (16:9) |
    | Titre | Balade à Paris (2 lignes max) |
    | Créateur | @paris_stories ✓ |
    | Durée | 12 min |
    | Écoutes | 🎧 2.3K |
    | Localisation | 📍 Paris 5e · Ancré |
    | Tags | 🏷️ #Voyage #Histoire |
    | Badge Premium | 👑 (si applicable) |
    | Distance | À 2.3 km (si recherche géo) |
    | Bouton lecture | ▶️ Écouter |
    | Menu contextuel | ⋮ |

## 35. Lazy loading des images

Étant donné une page avec 20 résultats de recherche

Quand la page se charge

Alors seules les 5 premières images sont chargées Et les images suivantes se chargent au scroll

## 36. Troncature du titre sur 2 lignes maximum

Étant donné un contenu avec un titre de 120 caractères

Quand le résultat est affiché

Alors le titre est tronqué après 2 lignes Et "..." est ajouté à la fin

## 37. Lien cliquable vers le profil créateur

Étant donné un résultat de recherche pour "@paris_stories"

Quand l'utilisateur clique sur "@paris_stories"

Alors il est redirigé vers "https://roadwave.fr/@paris_stories"

## 38. Menu contextuel d'un résultat [⋮]

Étant donné que l'utilisateur clique sur [⋮] pour un résultat

Quand le menu s'ouvre

Alors les actions suivantes sont disponibles:

    | action |
    |---|
    | Partager |
    | Ajouter à une playlist |
    | Télécharger (offline) |
    | Signaler |

## 39. Pagination avec 20 résultats par page

Étant donné une recherche retournant 100 résultats

Quand la page est affichée

Alors 20 résultats sont chargés initialement Et un indicateur "1-20 sur 100 résultats" est visible

## 40. Infinite scroll automatique

Étant donné que l'utilisateur scroll dans les résultats

Quand il atteint 80% de la page

Alors les 20 résultats suivants sont chargés automatiquement Et un loader est affiché pendant le chargement

## 41. Bouton fallback "Charger 20 suivants"

Étant donné que l'infinite scroll est désactivé (paramètres)

Quand l'utilisateur atteint la fin de la page

Alors un bouton "Charger 20 suivants" est affiché Et les résultats se chargent au clic

## 42. Basculement entre vue liste et vue carte

Étant donné que l'utilisateur est sur la page de résultats

Quand il clique sur le toggle "Liste / Carte"

Alors la vue carte Leaflet s'affiche Et les résultats sont affichés comme markers sur la carte

## 43. Affichage de la carte Leaflet

Étant donné que la vue carte est activée

Quand la carte se charge

Alors la carte utilise les tuiles OpenStreetMap Et le centre est la position de recherche (ou GPS utilisateur) Et le zoom initial montre tous les résultats

## 44. Markers cliquables sur la carte

Étant donné que 10 résultats sont affichés sur la carte

Quand l'utilisateur clique sur un marker

Alors une popup s'affiche avec:

    | élément |
    |---|
    | Titre |
    | Créateur |
    | Durée |
    | Distance |
    | Bouton ▶️ Écouter |

## 45. Clustering des markers proches

Étant donné que 50 résultats sont très proches géographiquement

Quand la carte est affichée

Alors les markers proches sont groupés en clusters Et le nombre de contenus est affiché sur le cluster Et le cluster se décompose au zoom

## 46. Synchronisation liste / carte

Étant donné que l'utilisateur est en vue carte

Quand il clique sur un marker et écoute le contenu Et qu'il rebascule en vue liste

Alors le contenu écouté est marqué dans la liste Et la position de scroll est maintenue

## 47. Index PostgreSQL full-text pour performances

Étant donné que la base contient 100K contenus

Quand une recherche full-text est effectuée

Alors l'index GIN sur to_tsvector est utilisé Et la requête retourne en moins de 100ms

## 48. Index PostGIS GIST pour recherche géo

Étant donné une recherche géographique avec rayon 50 km

Quand la requête PostGIS ST_DWithin est exécutée

Alors l'index GIST sur la colonne location est utilisé Et la requête retourne en moins de 50ms

## 49. Index composites pour filtres

Étant donné une recherche avec filtres multiples

Quand les filtres type, durée, âge, géo, date sont appliqués

Alors l'index composite idx_content_filters est utilisé Et les performances restent optimales

## 50. Index GIN pour recherche par tags

Étant donné une recherche filtrée par tags "Voyage, Histoire"

Quand la requête est exécutée

Alors l'index GIN sur la colonne tags est utilisé Et la recherche est performante même avec 500K contenus

## 51. Aucun résultat trouvé

Étant donné que l'utilisateur recherche "xyzabc123"

Quand aucun résultat n'est trouvé

Alors un message "Aucun résultat pour 'xyzabc123'" s'affiche Et des suggestions de recherches populaires sont proposées

## 52. Recherche vide

Étant donné que l'utilisateur clique sur "Rechercher" sans saisir de texte

Quand la recherche est lancée

Alors un message "Veuillez entrer au moins 2 caractères" s'affiche

## 53. Erreur de géocodage Nominatim

Étant donné que l'API Nominatim est indisponible

Quand l'utilisateur tente une recherche géographique

Alors un message "Service de localisation temporairement indisponible" s'affiche Et la recherche continue sans filtre géographique

## 54. GPS désactivé pour "Autour de moi"

Étant donné que l'utilisateur a désactivé le GPS

Quand il sélectionne "Autour de moi"

Alors un message "Veuillez activer la localisation" s'affiche Et un bouton "Activer" ouvre les paramètres système

## 55. Timeout de recherche après 10 secondes

Étant donné qu'une recherche complexe est lancée

Quand la requête dépasse 10 secondes

Alors la recherche est annulée Et un message "La recherche a pris trop de temps, veuillez réessayer" s'affiche

# Classification de géo-pertinence des contenus

> En tant que plateforme de contenu géolocalisé Je veux classifier les contenus selon leur pertinence géographique Afin d'adapter l'algorithme de recommandation

10 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible

## 1. Créateur choisit le type géo-ancré pour un audio-guide

Étant donné que je suis un créateur connecté

Quand je publie un audio-guide de la Tour Eiffel Et que je choisis la classification "Géo-ancré"

Alors le contenu est enregistré avec:

    | champ | valeur |
    |---|---|
    | type_geo | geo_ancre |
    | ponderation_geo | 0.7 |
    | ponderation_interets | 0.1 |

## 2. Créateur choisit le type géo-contextuel pour actualité régionale

Étant donné que je suis un créateur connecté

Quand je publie une actualité régionale en Bretagne Et que je choisis la classification "Géo-contextuel"

Alors le contenu est enregistré avec:

    | champ | valeur |
    |---|---|
    | type_geo | geo_contextuel |
    | ponderation_geo | 0.5 |
    | ponderation_interets | 0.3 |

## 3. Créateur choisit le type géo-neutre pour un podcast philosophie

Étant donné que je suis un créateur connecté

Quand je publie un podcast de philosophie Et que je choisis la classification "Géo-neutre"

Alors le contenu est enregistré avec:

    | champ | valeur |
    |---|---|
    | type_geo | geo_neutre |
    | ponderation_geo | 0.2 |
    | ponderation_interets | 0.6 |

## 4. Publication impossible sans classification géographique

Étant donné que je crée un contenu audio

Quand j'essaie de publier sans sélectionner de type géographique

Alors la publication échoue Et je vois le message "Vous devez sélectionner un type de géo-pertinence"

## 5. Modérateur reclassifie un contenu mal catégorisé

Étant donné qu'un contenu podcast générique est classifié "Géo-ancré" Et que le modérateur examine le contenu

Quand le modérateur le reclassifie en "Géo-neutre"

Alors la nouvelle classification est appliquée immédiatement Et l'algorithme utilise la pondération géo = 0.2 Et le créateur reçoit une notification de reclassification

## 6. Créateur modifie la classification après publication

Étant donné que j'ai publié un contenu classifié "Géo-contextuel" Et que je réalise qu'il devrait être "Géo-neutre"

Quand je modifie la classification en "Géo-neutre"

Alors la modification est enregistrée Et l'algorithme utilise la nouvelle pondération Et je vois le message "Classification modifiée avec succès"

## 7. Statistiques de classification dans le profil créateur

Étant donné que je suis un créateur Et que j'ai publié 30 contenus:

    | type | nombre |
    |---|---|
    | Géo-ancré | 10 |
    | Géo-contextuel | 15 |
    | Géo-neutre | 5 |

Quand je consulte mes statistiques

Alors je vois la répartition de mes classifications Et des suggestions pour optimiser la portée

## 8. Contenu géo-ancré fortement pondéré par la proximité

Étant donné qu'un audio-guide "Géo-ancré" existe à la Tour Eiffel Et qu'un utilisateur est à 100m de la Tour Eiffel

Quand l'algorithme calcule le score

Alors la pondération géo est de 0.7 Et le score géo est proche de 1 (très proche) Et le contenu a un score final élevé

## 9. Contenu géo-neutre moins sensible à la distance

Étant donné qu'un podcast philosophie "Géo-neutre" existe à Paris Et qu'un utilisateur est à Marseille (750 km)

Quand l'algorithme calcule le score

Alors la pondération géo est de 0.2 Et le score géo est bas (distance élevée) Mais le score intérêts (0.6) peut compenser Et le contenu peut quand même être recommandé si intérêts match

## 10. Comparaison scores entre types géo pour même distance

Étant donné 3 contenus au même endroit (Paris):

    | type | ponderation_geo |
    |---|---|
    | Géo-ancré | 0.7 |
    | Géo-contextuel | 0.5 |
    | Géo-neutre | 0.2 |

Et qu'un utilisateur est à 50 km de Paris

Quand l'algorithme calcule les scores

Alors le contenu "Géo-ancré" a le score géo le plus élevé Et le contenu "Géo-neutre" a le score géo le plus faible Mais peut avoir un score final plus élevé si forte correspondance intérêts

# Gestion du contenu politique (MVP simplifié)

> En tant qu'utilisateur Je veux pouvoir filtrer le contenu politique Afin de contrôler mon exposition à ce type de contenu

13 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible

## 1. Créateur tagge son contenu comme "Politique"

Étant donné que je suis un créateur connecté

Quand je publie un contenu sur un débat politique Et que je sélectionne le tag "Politique"

Alors le contenu est enregistré avec le tag "Politique" Et aucune classification gauche/droite n'est demandée (MVP)

## 2. Tag "Politique" au même niveau que les autres tags

Étant donné que je crée un contenu

Quand je consulte la liste des tags disponibles

Alors je vois les tags suivants au même niveau:

    | tag |
    |---|
    | Économie |
    | Sport |
    | Culture |
    | Politique |
    | Automobile |
    | Voyage |
    | Musique |

## 3. Par défaut, tous les contenus politiques sont visibles

Étant donné que je suis un nouvel utilisateur Et que je n'ai pas modifié les paramètres de contenu politique

Quand je demande des recommandations

Alors les contenus tagués "Politique" sont inclus normalement Et aucun filtrage n'est appliqué

## 4. Activer le filtrage "Masquer contenu politique"

Étant donné que je suis connecté

Quand j'active l'option "Masquer contenu politique" dans les paramètres

Alors tous les contenus tagués "Politique" sont exclus de mes recommandations Et je vois le message "Contenu politique masqué"

## 5. Filtrage politique actif - aucun contenu politique recommandé

Étant donné que j'ai activé "Masquer contenu politique" Et qu'il existe 100 contenus dont 20 tagués "Politique"

Quand je demande 50 recommandations

Alors je reçois 50 contenus parmi les 80 non-politiques Et 0% de contenus politiques sont proposés

## 6. Désactiver le filtrage "Masquer contenu politique"

Étant donné que j'ai activé "Masquer contenu politique"

Quand je désactive cette option dans les paramètres

Alors les contenus politiques sont à nouveau inclus dans mes recommandations Et le filtrage est levé immédiatement

## 7. Mode Kids filtre automatiquement le contenu politique

Étant donné que je suis un utilisateur de 14 ans Et que le mode Kids est activé

Quand je demande des recommandations

Alors tous les contenus tagués "Politique" sont automatiquement exclus Et ce indépendamment du paramètre "Masquer contenu politique"

## 8. Statistiques créateur sur contenu politique

Étant donné que je suis un créateur Et que j'ai publié 20 contenus dont 5 tagués "Politique"

Quand je consulte mes statistiques

Alors je vois le nombre d'utilisateurs ayant masqué le contenu politique Et le taux d'engagement comparé aux autres tags

## 9. Recherche avec tag "Politique"

Étant donné que je recherche du contenu

Quand je filtre par tag "Politique"

Alors seuls les contenus tagués "Politique" sont affichés Et ce même si j'ai activé "Masquer contenu politique" (recherche explicite)

## 10. Partage de contenu politique avec filtre actif

Étant donné que j'ai activé "Masquer contenu politique" Et qu'un ami me partage un lien vers un contenu tagué "Politique"

Quand j'ouvre le lien

Alors je peux accéder au contenu (partage explicite) Et je vois un avertissement "Ce contenu est tagué Politique"

## 11. Pas de classification gauche/droite en MVP

Étant donné que je suis un créateur

Quand je publie un contenu tagué "Politique"

Alors aucune option de classification idéologique n'est proposée Et je ne peux pas indiquer "Gauche", "Droite", "Centre", etc.

## 12. Pas d'équilibrage imposé en MVP

Étant donné qu'un utilisateur écoute majoritairement du contenu politique de gauche

Quand l'algorithme génère des recommandations

Alors aucun équilibrage droite/gauche n'est appliqué Et les recommandations suivent l'algorithme standard (intérêts, géo, engagement)

## 13. Notification post-MVP pour classification avancée

Étant donné que RoadWave passe en phase post-MVP Et que la classification politique avancée est activée

Quand je me connecte

Alors je reçois une notification m'informant des nouvelles options Et je peux configurer mes préférences d'équilibrage politique

# Contenus géolocalisés en mode voiture

> En tant qu'utilisateur en voiture Je veux recevoir des notifications de contenus géolocalisés au bon moment Afin de découvrir du contenu contextuel sans distraction au volant

36 scénarios (32 standards, 4 plans)

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et que l'application est ouverte (premier plan) Et que le GPS est activé Et que l'utilisateur est en mode voiture (vitesse ≥ 5 km/h)

## 1. Calcul ETA et notification 7 secondes avant le point GPS

Étant donné qu'un contenu géolocalisé existe à la Tour Eiffel (48.8584, 2.2945) Et que je me déplace à 50 km/h vers ce point Et que je suis à 98 mètres du point (ETA = 7 secondes)

Quand le système calcule l'ETA

Alors une notification est déclenchée immédiatement Et le compteur "7" s'affiche avec l'icône 🏛️ Et une notification sonore (bip court) est jouée

## 2. 📋 Plan: Calcul ETA à différentes vitesses

Étant donné qu'un contenu géolocalisé existe à un point GPS Et que je me déplace à km/h

Quand je suis à mètres du point

Alors l'ETA calculé est secondes Et la notification est déclenchée :

📊 Exemples de données:

vitesse | distance | eta | notification
--- | --- | --- | ---
10 | 19 | 7 | Oui
50 | 98 | 7 | Oui
130 | 252 | 7 | Oui
50 | 200 | 14 | Non
10 | 50 | 18 | Non

## 3. Notification immédiate si vitesse <5 km/h ET distance <50m

Étant donné qu'un contenu géolocalisé existe à 30m de ma position Et que ma vitesse est 3 km/h (arrêté à un feu rouge)

Quand le système détecte cette situation

Alors une notification est déclenchée immédiatement Et je n'ai pas besoin d'attendre le calcul ETA

## 4. Notification minimaliste sans texte (sécurité routière)

Étant donné qu'une notification géolocalisée est déclenchée

Quand la notification s'affiche

Alors les éléments suivants sont visibles:

    | élément | présent |
    |---|---|
    | Icône du tag | ✅ |
    | Compteur 7→1 | ✅ |
    | Son bref (bip) | ✅ |
    | Titre texte | ❌ |
    | Description | ❌ |
    | Cover image | ❌ |
    | Bouton Annuler | ❌ |

## 5. 📋 Plan: Icônes selon le tag du contenu

Étant donné qu'un contenu géolocalisé avec le tag est disponible

Quand la notification s'affiche

Alors l'icône est affichée

📊 Exemples de données:

tag | icone
--- | ---
Culture générale | 🏛️
Histoire | 📜
Voyage | ✈️
Famille | 👨‍👩‍👧
Musique | 🎵
Sport | ⚽
Technologie | 💻
Automobile | 🚗

## 6. Compteur décrémentant de 7 à 1

Étant donné qu'une notification géolocalisée s'affiche

Quand le compteur démarre

Alors le compteur affiche "7" Et après 1 seconde, il affiche "6" Et après 2 secondes, il affiche "5" Et après 3 secondes, il affiche "4" Et après 4 secondes, il affiche "3" Et après 5 secondes, il affiche "2" Et après 6 secondes, il affiche "1" Et après 7 secondes, la notification disparaît

## 7. Notification sonore uniquement en mode CarPlay

Étant donné que l'application est connectée à CarPlay Et qu'un contenu géolocalisé est détecté (ETA 7s)

Quand la notification est déclenchée

Alors seule la notification sonore (bip) est jouée Et aucun overlay visuel n'est affiché (icône, compteur) Et l'utilisateur peut valider via le bouton "Suivant" au volant

## 8. Notification sonore uniquement en mode Android Auto

Étant donné que l'application est connectée à Android Auto Et qu'un contenu géolocalisé est détecté (ETA 7s)

Quand la notification est déclenchée

Alors seule la notification sonore (bip) est jouée Et aucun overlay visuel n'est affiché Et l'utilisateur peut valider via le bouton "Suivant" au volant

## 9. Notification complète (sonore + visuelle) en mode normal

Étant donné que l'application n'est PAS connectée à CarPlay/Android Auto Et qu'un contenu géolocalisé est détecté

Quand la notification est déclenchée

Alors la notification sonore (bip) est jouée Et l'overlay visuel s'affiche (icône + compteur 7→1)

## 10. Validation via bouton "Suivant" et décompte 5 secondes

Étant donné qu'une notification géolocalisée est affichée (compteur à 5) Et que j'écoute un podcast

Quand j'appuie sur le bouton "Suivant"

Alors le compteur bascule à "5" (décompte final) Et le contenu actuel continue de jouer normalement Et le compteur décrémente: 5→4→3→2→1 Et après 5 secondes, le contenu géolocalisé démarre (fade in 0.3s)

## 11. Transition fluide avec fade out/in

Étant donné que le décompte atteint "0"

Quand le contenu géolocalisé doit démarrer

Alors le contenu actuel fait un fade out de 0.3s Et le contenu géolocalisé fait un fade in de 0.3s Et il n'y a pas de silence entre les deux

## 12. Contenu actuel se termine pendant le décompte

Étant donné que j'ai validé la notification (décompte 5s démarre) Et que mon contenu actuel se termine après 2 secondes

Quand le contenu actuel se termine

Alors le contenu suivant du buffer démarre immédiatement Et le décompte continue (3→2→1) Et à la fin du décompte, le contenu géolocalisé remplace le buffer

## 13. Ignorance de la notification (pas de clic pendant 7s)

Étant donné qu'une notification géolocalisée s'affiche (compteur 7)

Quand 7 secondes s'écoulent sans que j'appuie sur "Suivant"

Alors la notification disparaît automatiquement Et le contenu géolocalisé est perdu (pas d'insertion dans la file) Et un cooldown de 10 minutes est activé

## 14. Quota de 6 contenus géolocalisés par heure

Étant donné que j'ai validé 6 notifications géolocalisées dans la dernière heure

Quand un 7ème contenu géolocalisé est détecté

Alors aucune notification n'est envoyée Et le contenu n'est pas inséré dans la file

## 15. Fenêtre glissante de 60 minutes

Étant donné que j'ai validé 6 contenus géolocalisés Et que le premier contenu a été validé il y a 61 minutes

Quand un nouveau contenu géolocalisé est détecté

Alors la notification est envoyée (quota libéré : 5/6) Et le compteur horaire est mis à jour

## 16. 📋 Plan: Gestion du quota horaire

Étant donné que notifications ont été validées dans la dernière heure

Quand un nouveau contenu géolocalisé est détecté

Alors la notification est

📊 Exemples de données:

nb_valides | action
--- | ---
0 | envoyée
3 | envoyée
5 | envoyée
6 | non envoyée
7 | non envoyée

## 17. Exception audio-guides multi-séquences (comptent comme 1)

Étant donné que j'ai démarré un audio-guide avec 8 séquences Et que cet audio-guide compte comme 1 contenu dans le quota

Quand toutes les séquences de l'audio-guide sont lues

Alors mon quota reste à 1/6 Et je peux encore valider 5 contenus géolocalisés simples

## 18. Cooldown de 10 minutes après notification ignorée

Étant donné qu'une notification géolocalisée a été ignorée (pas de clic) Et qu'un cooldown de 10 minutes est activé

Quand 5 minutes s'écoulent Et qu'un nouveau contenu géolocalisé est détecté

Alors aucune notification n'est envoyée (cooldown actif)

## 19. Cooldown expire après 10 minutes

Étant donné qu'un cooldown a été activé il y a 10 minutes

Quand un nouveau contenu géolocalisé est détecté

Alors la notification est envoyée (cooldown expiré)

## 20. Pas de cooldown si notification validée

Étant donné qu'une notification géolocalisée est affichée

Quand j'appuie sur "Suivant" dans les 7 secondes

Alors aucun cooldown n'est activé Et la prochaine notification pourra être envoyée normalement

## 21. Contenu géolocalisé dans l'historique de navigation

Étant donné que j'écoute un contenu du buffer Et que j'ai validé un contenu géolocalisé "Tour Eiffel" Et que j'ai écouté 42 secondes du contenu géolocalisé

Quand j'appuie sur "Suivant" (skip) Et que j'appuie ensuite sur "Précédent"

Alors le contenu géolocalisé reprend à 42 secondes

## 22. Contenu ignoré n'entre pas dans l'historique

Étant donné qu'une notification géolocalisée a été ignorée

Quand j'appuie sur "Précédent"

Alors le contenu géolocalisé ignoré n'apparaît PAS dans l'historique Et je reviens au contenu d'avant

## 23. Skip pendant le décompte annule l'insertion

Étant donné que j'ai validé une notification (décompte 5s en cours) Et que le compteur affiche "3"

Quand j'appuie à nouveau sur "Suivant"

Alors le décompte est annulé Et le contenu suivant du buffer démarre Et le contenu géolocalisé n'entre PAS dans l'historique

## 24. Détection mode piéton (vitesse <5 km/h stable 10s)

Étant donné que je suis en mode voiture Et que ma vitesse passe à 3 km/h

Quand cette vitesse reste stable pendant 10 secondes

Alors le mode piéton est activé automatiquement Et les notifications passent en mode push arrière-plan (si permission accordée)

## 25. Détection mode voiture (vitesse ≥5 km/h stable 10s)

Étant donné que je suis en mode piéton Et que ma vitesse passe à 15 km/h

Quand cette vitesse reste stable pendant 10 secondes

Alors le mode voiture est activé automatiquement Et les notifications passent en mode sonore + icône (app premier plan requise)

## 26. Hysteresis pour éviter basculements intempestifs

Étant donné que ma vitesse passe de 20 km/h à 3 km/h (arrêt feu rouge) Et que ma vitesse remonte à 20 km/h après 8 secondes

Quand le système vérifie le mode

Alors aucun basculement n'a lieu (hysteresis de 10s non atteinte) Et je reste en mode voiture

## 27. 📋 Plan: Effets du basculement voiture → piéton

Étant donné que je bascule de voiture à piéton

Quand le basculement est effectué

Alors les paramètres suivants changent:

    | paramètre | voiture | piéton |
    |---|---|---|
    | App requise | Premier plan | Arrière-plan OK |
    | Notification | Sonore + icône + compteur | Push système |
    | Rayon détection | ETA 7s (variable) | 200m fixes |
    | Type contenu | Tous géolocalisés | Audio-guides uniquement |

## 28. Haute vitesse (130 km/h sur autoroute)

Étant donné que je roule à 130 km/h (36.1 m/s) Et qu'un contenu géolocalisé est à 252 mètres

Quand l'ETA de 7s est atteint Et que je valide la notification

Alors le décompte 5s démarre Et le contenu géolocalisé démarre encore avant le point GPS (72m avant)

## 29. Multiples points géolocalisés proches (route touristique)

Étant donné que 3 châteaux sont espacés de 800m chacun Et que je valide la notification du Château A

Quand j'arrive près du Château B (57s plus tard à 50 km/h)

Alors la notification du Château B est envoyée (quota 2/6, pas de cooldown)

## 30. Mode stationnement (vitesse <1 km/h pendant 2 min)

Étant donné que je me gare à 30m d'un château Et que ma vitesse est <1 km/h pendant 2 minutes

Quand le mode stationnement est détecté

Alors aucune notification de contenu géolocalisé n'est envoyée Et le système bascule automatiquement en mode piéton

## 31. Reprise conduite après stationnement

Étant donné que je suis en mode stationnement Et que ma vitesse passe à 20 km/h pendant 10 secondes

Quand le système détecte la reprise de conduite

Alors le mode voiture est réactivé Et les notifications géolocalisées reprennent (si quota non atteint)

## 32. Contenu géolocalisé simple (1 séquence unique)

Étant donné qu'un contenu géolocalisé simple existe à un point GPS

Quand la notification est déclenchée (ETA 7s) Et que je valide

Alors le contenu démarre après décompte 5s Et à la fin du contenu, le buffer normal reprend Et ce contenu compte 1/6 dans le quota

## 33. Audio-guide multi-séquences (2+ séquences enchaînées)

Étant donné qu'un audio-guide avec 8 séquences existe

Quand je démarre l'audio-guide Et que les séquences s'enchaînent automatiquement (GPS ou manuel)

Alors l'audio-guide entier compte 1/6 dans le quota Et les séquences ne déclenchent PAS de notification avec compteur 7s Et elles se déclenchent au point GPS exact (rayon 30m)

## 34. GPS désactivé en mode voiture

Étant donné que je suis en mode voiture

Quand le GPS est désactivé

Alors aucune notification géolocalisée ne peut être envoyée Et un message d'erreur s'affiche: "GPS requis pour les contenus géolocalisés"

## 35. App en arrière-plan en mode voiture

Étant donné que je suis en mode voiture Et que l'app passe en arrière-plan

Quand un contenu géolocalisé est détecté

Alors aucune notification n'est envoyée (app premier plan requise) Et le contenu n'est pas perdu (sera proposé si app rouverte dans le rayon)

## 36. Permission "Always Location" refusée (mode piéton indisponible)

Étant donné que je refuse la permission "Always Location"

Quand ma vitesse passe <5 km/h

Alors le mode piéton n'est PAS activé Et le mode voiture reste actif (avec permission "When In Use") Et aucune notification arrière-plan n'est envoyée

# Gestion de l'historique et reproposition

> En tant que système de recommandation Je veux gérer l'historique d'écoute intelligemment Afin d'éviter les répétitions et offrir une découverte maximale

19 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible

## 1. Contenu écouté complètement (>80%) - jamais reproposé

Étant donné qu'un utilisateur a écouté un contenu à 85%

Quand l'algorithme génère les recommandations

Alors ce contenu n'est jamais reproposé Et il est marqué comme "écouté" dans l'historique

## 2. Contenu écouté à 80% exactement - jamais reproposé

Étant donné qu'un utilisateur a écouté un contenu exactement à 80%

Quand l'algorithme génère les recommandations

Alors ce contenu n'est pas reproposé (seuil >= 80%)

## 3. Contenu skippé rapidement (<10s) - ne pas reproposer

Étant donné qu'un utilisateur a skippé un contenu après 8 secondes

Quand l'algorithme génère les recommandations

Alors ce contenu n'est pas reproposé (signal négatif fort) Et la jauge d'intérêt correspondante est réduite de 0.5%

## 4. Contenu skippé exactement à 10s - ne pas reproposer

Étant donné qu'un utilisateur a skippé un contenu après exactement 10 secondes

Quand l'algorithme génère les recommandations

Alors ce contenu n'est pas reproposé (seuil < 10s strict)

## 5. Contenu partiellement écouté (10-80%) - reproposer avec reprise

Étant donné qu'un utilisateur a écouté un contenu à 45% Et qu'il est arrivé à la position 2:30 (150 secondes)

Quand l'algorithme propose à nouveau ce contenu

Alors le contenu peut être reproposé Et la position de reprise est 150 secondes Et l'utilisateur voit "Reprendre à 2:30"

## 6. Contenu écouté à 11% - reproposition possible

Étant donné qu'un utilisateur a écouté un contenu à 11%

Quand l'algorithme génère les recommandations

Alors ce contenu peut être reproposé (>10%) Et la position de reprise est sauvegardée

## 7. Contenu écouté à 79% - reproposition possible

Étant donné qu'un utilisateur a écouté un contenu à 79%

Quand l'algorithme génère les recommandations

Alors ce contenu peut être reproposé (<80%) Et l'utilisateur peut terminer l'écoute

## 8. Audio-guide avec flag replayable=true

Étant donné qu'un audio-guide a le flag "replayable = true" Et qu'un utilisateur l'a écouté à 95%

Quand l'algorithme génère les recommandations

Alors l'audio-guide peut être reproposé Et il est marqué comme "Écouté - Rejouable"

## 9. Podcast standard sans flag replayable

Étant donné qu'un podcast n'a pas de flag replayable Et qu'un utilisateur l'a écouté à 90%

Quand l'algorithme génère les recommandations

Alors le podcast n'est jamais reproposé

## 10. Stockage dans user_content_history

Étant donné qu'un utilisateur écoute un contenu

Quand l'écoute se termine ou est skippée

Alors les données suivantes sont enregistrées:

    | champ | exemple |
    |---|---|
    | user_id | user-123 |
    | content_id | content-456 |
    | completion_rate | 0.45 (45%) |
    | last_position | 150 (secondes) |
    | listened_at | 2026-01-21 14:30:00 |

## 11. Historique illimité stocké

Étant donné qu'un utilisateur a écouté 5000 contenus

Quand il consulte son historique

Alors tous les 5000 contenus sont disponibles Et aucun contenu n'est supprimé automatiquement

## 12. Algorithme considère les 100 derniers pour performance

Étant donné qu'un utilisateur a écouté 500 contenus

Quand l'algorithme génère les recommandations

Alors il vérifie uniquement les 100 derniers contenus pour exclusion Et cette limite est une optimisation de requête SQL

## 13. Export historique complet (RGPD)

Étant donné qu'un utilisateur demande l'export RGPD

Quand l'export est généré

Alors l'historique complet est inclus avec:

    | information | inclus |
    |---|---|
    | Tous les contenus | ✅ |
    | Dates d'écoute | ✅ |
    | Taux complétion | ✅ |
    | Positions reprise | ✅ |

## 14. Reprise automatique d'un contenu partiellement écouté

Étant donné que j'ai écouté un podcast à 60% (position 10:00)

Quand ce podcast est reproposé par l'algorithme Et que je lance la lecture

Alors l'écoute reprend automatiquement à 10:00 Et je vois une notification "Reprise à 10:00"

## 15. Option "Reprendre du début" pour contenu partiellement écouté

Étant donné que j'ai écouté un podcast à 60%

Quand ce podcast est reproposé

Alors je vois deux options:

    | option | action |
    |---|---|
    | Reprendre à 10:00 | Lecture à partir de 10:00 |
    | Depuis le début | Lecture à partir de 0:00 |

## 16. Contenu écouté il y a 6 mois - toujours en historique

Étant donné qu'un utilisateur a écouté un contenu il y a 6 mois à 90%

Quand l'algorithme génère les recommandations

Alors ce contenu n'est toujours pas reproposé Et l'historique n'a pas de limite temporelle

## 17. Nouveau contenu du même créateur après écoute complète

Étant donné qu'un utilisateur a écouté un contenu de "Créateur A" à 90% Et que "Créateur A" publie un nouveau contenu

Quand l'algorithme génère les recommandations

Alors le nouveau contenu peut être recommandé Et seul l'ancien contenu est exclu (pas tout le créateur)

## 18. Statistiques personnelles d'historique

Étant donné que je consulte mon profil

Quand j'accède à la section "Historique"

Alors je vois:

    | métrique | exemple |
    |---|---|
    | Nombre total d'écoutes | 1,234 |
    | Heures écoutées | 456h |
    | Taux complétion moyen | 72% |
    | Top 5 catégories | Voyage, Sport |

## 19. Filtrer l'historique par date

Étant donné que je consulte mon historique

Quand je filtre par "Dernière semaine"

Alors seuls les contenus écoutés dans les 7 derniers jours sont affichés Et je peux exporter cette sélection

# Médias traditionnels sur RoadWave

> En tant que média établi Je veux publier du contenu géolocalisé sur RoadWave Afin d'atteindre une audience locale et mobile

21 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible

## 1. Création d'un compte média vérifié

Étant donné que je représente Le Monde

Quand je crée un compte média Et que je fournis les justificatifs (SIRET, documents officiels)

Alors mon compte est créé en attente de vérification Et l'équipe RoadWave examine ma demande sous 48-72h

## 2. Validation compte média par l'équipe RoadWave

Étant donné qu'un compte média "Le Parisien" est en attente

Quand l'équipe RoadWave valide le compte

Alors le compte reçoit le badge vérifié ✓ Et le média peut publier sans validation des 3 premiers contenus Et je vois le message "Compte média vérifié avec succès"

## 3. Badge vérifié visible sur profil média

Étant donné que "France Inter" a un compte vérifié

Quand un utilisateur consulte le profil

Alors il voit le badge ✓ à côté du nom Et une mention "Média vérifié"

## 4. Pas de validation des 3 premiers contenus pour médias

Étant donné que je suis un média vérifié

Quand je publie mon premier contenu

Alors le contenu est publié immédiatement sans validation Et il est visible pour tous les utilisateurs Et je ne passe pas par la modération initiale

## 5. Modération a posteriori uniquement

Étant donné que "Libération" publie un contenu

Quand le contenu est publié

Alors il est immédiatement disponible Mais peut être signalé et modéré a posteriori Et suit les mêmes règles de modération que les créateurs

## 6. Publication flash info géolocalisé

Étant donné que je suis "Ouest-France" (média régional)

Quand je publie un flash info sur un événement à Rennes Et que je le géolocalise en Bretagne (géo-contextuel)

Alors le contenu est publié immédiatement Et il est recommandé aux utilisateurs en Bretagne

## 7. Publication chronique thématique

Étant donné que je suis "France Culture"

Quand je publie une chronique philosophie (géo-neutre)

Alors le contenu est disponible partout en France Et suit l'algorithme de recommandation standard

## 8. Publication édito politique

Étant donné que je suis "Le Figaro"

Quand je publie un édito politique Et que je le tague "Politique"

Alors le contenu est publié immédiatement Et la classification politique MVP s'applique (pas gauche/droite) Et les utilisateurs ayant activé "Masquer politique" ne le voient pas

## 9. Formats de contenu autorisés pour médias

Étant donné que je suis un média vérifié

Quand je publie du contenu

Alors je peux publier:

    | format | exemple |
    |---|---|
    | Flash info géolocalisé | Actualité régionale 2-5 min |
    | Chronique thématique | Culture, économie, sport 5-15min |
    | Édito et débats | Opinion 10-30 min |
    | Reportage | Investigation 15-45 min |

## 10. Médias suivent les règles standard de classification âge

Étant donné que je suis "RTL"

Quand je publie un contenu sensible

Alors je dois obligatoirement classifier par âge:

    | classification | type contenu |
    |---|---|
    | Tout public | Info générale |
    | 13+ | Actualité avec sujets sensibles |
    | 16+ | Débats avec violence verbale |
    | 18+ | Sujets adultes |

## 11. Monétisation médias - partage revenus pub standard

Étant donné que je suis un média vérifié Et que mes contenus génèrent des écoutes

Quand le mois se termine

Alors je reçois 3€ / 1000 écoutes complètes (même taux que créateurs) Et le paiement suit les mêmes règles (seuil 50€, mensuel)

## 12. Sponsoring direct non géré par plateforme

Étant donné que je suis "Europe 1" Et que je veux intégrer un sponsor dans mon contenu

Quand je mentionne le sponsor dans l'audio

Alors c'est autorisé (sponsoring éditorial) Mais RoadWave ne gère pas la transaction Et je gère la relation sponsor directement

## 13. Médias peuvent avoir plusieurs comptes créateurs

Étant donné que je suis "Le Monde"

Quand je veux créer des sous-comptes par rubrique

Alors je peux créer:

    | compte | description |
    |---|---|
    | @lemonde_politique | Actualité politique |
    | @lemonde_economie | Économie et entreprises |
    | @lemonde_culture | Culture et spectacles |

Et tous sont liés au compte média principal

## 14. Médias régionaux privilégiés localement

Étant donné que "Sud-Ouest" publie du contenu géo-contextuel en Nouvelle-Aquitaine Et qu'un utilisateur est à Bordeaux

Quand l'algorithme calcule les recommandations

Alors le contenu de "Sud-Ouest" a un score géo élevé Et il est privilégié pour l'audience locale

## 15. Médias nationaux accessibles partout

Étant donné que "France Inter" publie un podcast géo-neutre

Quand des utilisateurs à Paris, Lyon, Marseille demandent des recommandations

Alors le podcast est accessible partout sans distinction géographique Et suit l'algorithme de recommandation standard

## 16. Statistiques détaillées pour médias

Étant donné que je suis un média vérifié

Quand je consulte mes statistiques

Alors je vois:

    | métrique | exemple |
    |---|---|
    | Écoutes par région | Île-de-France: 45% |
    | Taux complétion | 72% |
    | Démographie auditeurs | 25-34 ans: 35% |
    | Top contenus | Flash info Paris |
    | Revenus générés | 1,234€ |

## 17. Médias peuvent exporter analytics

Étant donné que je suis "Libération"

Quand je clique sur "Exporter analytics"

Alors je reçois un CSV avec données détaillées Et je peux analyser les données avec mes outils internes

## 18. Contact prioritaire équipe RoadWave

Étant donné que je suis un média vérifié

Quand j'ai un problème technique ou question

Alors je peux contacter le support média prioritaire Et j'obtiens une réponse sous 24h (vs 48-72h standard)

## 19. Médias peuvent programmer la publication

Étant donné que je suis "France Culture"

Quand je prépare un contenu à l'avance

Alors je peux programmer la publication pour une date/heure future Et le contenu sera publié automatiquement au moment choisi

## 20. API dédiée pour médias (post-MVP)

Étant donné que je suis un grand média avec beaucoup de contenus

Quand RoadWave développe l'API médias

Alors je peux automatiser la publication via API Et intégrer RoadWave dans mon workflow de production

## 21. Signalement d'un contenu média traité en priorité

Étant donné qu'un contenu de "Le Monde" est signalé

Quand le signalement arrive en modération

Alors il est traité avec la même priorité qu'un créateur standard Et le badge vérifié ne donne pas d'immunité modération

# Mode Kids pour utilisateurs 13-15 ans

> En tant que parent ou adolescent Je veux activer un mode Kids avec filtrage de contenu Afin de protéger les mineurs des contenus inappropriés

15 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible

## 1. Activation manuelle du mode Kids

Étant donné que je suis un utilisateur de 14 ans Et que le mode Kids n'est pas activé par défaut

Quand j'active le mode Kids dans les paramètres

Alors le mode Kids est activé sur mon compte Et je vois le message "Mode Kids activé - Contenus filtrés pour 13-15 ans"

## 2. Parent active le mode Kids pour son enfant

Étant donné que je suis le parent d'un utilisateur de 13 ans Et que j'ai accès au compte de mon enfant

Quand j'active le mode Kids

Alors le mode Kids est activé sur le compte enfant Et seuls les contenus "Tous publics" sont accessibles

## 3. Filtrage contenu - uniquement "Tous publics"

Étant donné que le mode Kids est activé sur mon compte Et qu'il existe des contenus avec les classifications:

    | classification | nombre |
    |---|---|
    | Tous publics | 100 |
    | 13+ | 50 |
    | 16+ | 30 |
    | 18+ | 20 |

Quand je demande des recommandations

Alors seuls les 100 contenus "Tous publics" sont proposés Et les contenus 13+, 16+, 18+ sont exclus

## 4. Exclusion automatique du contenu politique

Étant donné que le mode Kids est activé Et qu'il existe 20 contenus "Tous publics" dont 5 tagués "Politique"

Quand je demande des recommandations

Alors seuls les 15 contenus non-politiques sont proposés Et les 5 contenus politiques sont automatiquement exclus

## 5. Pas de publicité en mode Kids

Étant donné que le mode Kids est activé Et que je suis un utilisateur gratuit

Quand j'écoute du contenu

Alors aucune publicité n'est diffusée Et je n'ai pas d'insertion publicitaire (règle 1/5 désactivée)

## 6. Publicité validée manuellement en mode Kids (post-MVP)

Étant donné que le mode Kids est activé Et qu'une publicité a été validée manuellement pour le mode Kids

Quand j'écoute du contenu

Alors cette publicité peut être diffusée Mais la fréquence reste inférieure au mode standard

## 7. Interface standard même en mode Kids

Étant donné que le mode Kids est activé

Quand j'ouvre l'application

Alors l'interface est identique au mode normal Et seul le filtrage de contenu est actif (pas d'UI enfant)

## 8. Désactivation du mode Kids

Étant donné que le mode Kids est activé

Quand je désactive le mode Kids dans les paramètres

Alors tous les contenus sont à nouveau accessibles selon mon âge Et je vois le message "Mode Kids désactivé"

## 9. Utilisateur 16 ans ne peut pas activer le mode Kids 13-15 ans

Étant donné que je suis un utilisateur de 16 ans

Quand j'essaie d'activer le mode Kids

Alors l'activation réussit Et le mode Kids filtre les contenus 16+ et 18+ (pas seulement 13+) Et je vois uniquement les contenus "Tous publics"

## 10. Tentative d'accès direct à contenu 16+ en mode Kids

Étant donné que le mode Kids est activé Et qu'un ami me partage un contenu 16+

Quand j'essaie d'accéder au contenu via le lien

Alors l'accès est refusé Et je vois le message "Ce contenu n'est pas accessible en mode Kids"

## 11. Recherche en mode Kids filtre automatiquement

Étant donné que le mode Kids est activé

Quand je recherche "débat"

Alors seuls les contenus "Tous publics" apparaissent dans les résultats Et les contenus 13+, 16+, 18+ sont exclus de la recherche

## 12. Audio-guide en mode Kids

Étant donné que le mode Kids est activé Et qu'un audio-guide "Tous publics" existe au musée du Louvre

Quand je suis à proximité du Louvre

Alors l'audio-guide est proposé normalement Et toutes les séquences sont accessibles

## 13. Statistiques créateur - audience mode Kids

Étant donné que je suis un créateur Et que mes contenus "Tous publics" sont écoutés par des utilisateurs mode Kids

Quand je consulte mes statistiques

Alors je vois le pourcentage d'écoutes en mode Kids Et je peux adapter mes contenus en conséquence

## 14. Notification lors de l'activation du mode Kids

Quand j'active le mode Kids

Alors je reçois une notification explicative:

    | information | description |
    |---|---|
    | Contenu | Seuls les contenus "Tous publics" accessibles |
    | Politique | Contenus politiques automatiquement masqués |
    | Publicité | Aucune publicité affichée |

## 15. Badge mode Kids visible dans le profil

Étant donné que le mode Kids est activé

Quand je consulte mon profil

Alors je vois un badge "Mode Kids actif 🛡️" Et je peux le désactiver en un clic

# Paramétrabilité admin et A/B testing

> En tant qu'administrateur RoadWave Je veux configurer les paramètres de l'algorithme à chaud Afin d'optimiser l'engagement sans redéploiement

20 scénarios (19 standards, 1 plan)

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et que je suis connecté en tant qu'admin

## 1. Accès au dashboard admin

Quand j'accède au dashboard admin

Alors je vois tous les paramètres configurables de l'algorithme Et je vois les valeurs actuelles et par défaut

## 2. Modifier le poids géo pour contenu ancré

Étant donné que le poids_geo_ancre est à 0.7 (défaut)

Quand je modifie le poids_geo_ancre à 0.8 Et que je sauvegarde

Alors la nouvelle valeur est appliquée immédiatement Et tous les nouveaux calculs utilisent 0.8 Et je vois le message "Paramètre mis à jour avec succès"

## 3. Validation des plages de valeurs

Quand j'essaie de configurer poids_geo_ancre à 1.5 (hors plage 0.5-1.0)

Alors la modification échoue Et je vois le message "Valeur hors plage autorisée (0.5 - 1.0)"

## 4. 📋 Plan: Modification de tous les paramètres configurables

Quand je modifie "" à ""

Alors la modification est appliquée immédiatement Et la nouvelle valeur respecte la plage ""

📊 Exemples de données:

parametre | nouvelle_valeur | plage
--- | --- | ---
poids_geo_ancre | 0.8 | 0.5 - 1.0
poids_geo_contextuel | 0.6 | 0.3 - 0.7
poids_geo_neutre | 0.3 | 0.0 - 0.4
poids_engagement | 0.3 | 0.0 - 0.5
part_aleatoire_global | 0.15 | 0.0 - 0.3
distance_max_km | 150 | 50 - 500
rayon_gps_point_m | 1000 | 100 - 2000
seuil_min_ecoutes_engagement | 100 | 10 - 200

## 5. Aucun recalcul batch après modification

Étant donné que le poids_geo_ancre est modifié de 0.7 à 0.8

Quand la modification est appliquée

Alors aucun recalcul batch n'est lancé (économie CPU) Et seuls les nouveaux calculs utilisent la valeur 0.8

## 6. Versioning des configurations

Étant donné que je modifie plusieurs paramètres

Quand je sauvegarde la configuration

Alors une nouvelle version est créée (ex: v1.2.3) Et je peux voir l'historique des versions Et je peux comparer deux versions

## 7. Rollback en 1 clic

Étant donné que la configuration actuelle est v1.2.3 Et que la version précédente était v1.2.2

Quand je clique sur "Restaurer v1.2.2"

Alors tous les paramètres de v1.2.2 sont réappliqués Et je vois le message "Configuration restaurée à v1.2.2"

## 8. Créer une variante A/B testing

Quand je crée une nouvelle variante "Test engagement élevé" Et que je configure:

    | parametre | valeur |
    |---|---|
    | poids_engagement | 0.4 |
    | poids_geo_ancre | 0.6 |

Et que je lance le test A/B

Alors 50% des utilisateurs reçoivent la Config A (défaut) Et 50% des utilisateurs reçoivent la Config B (test)

## 9. Split utilisateurs aléatoire pour A/B test

Étant donné qu'un test A/B est actif

Quand 1000 nouveaux utilisateurs se connectent

Alors environ 500 sont assignés à la Config A Et environ 500 sont assignés à la Config B Et l'assignation est aléatoire et équilibrée

## 10. Utilisateur reste dans la même variante

Étant donné qu'un utilisateur est assigné à la Config B

Quand il se reconnecte plusieurs fois

Alors il reste toujours dans la Config B Et il ne change pas de variante pendant le test

## 11. Métriques comparatives A/B testing

Étant donné qu'un test A/B est actif depuis 7 jours

Quand je consulte le dashboard A/B testing

Alors je vois les métriques suivantes pour chaque config:

    | metrique | Config A | Config B |
    |---|---|---|
    | Taux complétion moyen | 68% | 72% |
    | Engagement (likes) | 15% | 18% |
    | Durée session moyenne | 23 min | 27 min |
    | Taux skip rapide (<10s) | 12% | 9% |

## 12. Dashboard graphique temps réel

Étant donné qu'un test A/B est actif

Quand je consulte le dashboard

Alors je vois des graphiques temps réel:

    | graphique | type |
    |---|---|
    | Évolution engagement | Ligne |
    | Répartition utilisateurs | Camembert |
    | Taux complétion | Barres |
    | Durée session | Ligne |

## 13. Terminer un test A/B et appliquer la meilleure config

Étant donné qu'un test A/B montre que Config B est meilleure

Quand je clique sur "Appliquer Config B pour tous"

Alors la Config B devient la configuration par défaut Et tous les utilisateurs utilisent maintenant Config B Et l'ancien test est archivé

## 14. Audit engagement global

Quand je consulte la section "Audit engagement"

Alors je vois:

    | metrique | valeur |
    |---|---|
    | Temps écoute moyen/session | 25 min |
    | Temps écoute médian/session | 18 min |
    | Taux complétion moyen | 70% |
    | % sessions avec ≥1 like | 35% |

## 15. Graphiques évolution engagement selon config

Étant donné que plusieurs modifications de config ont été faites

Quand je consulte les graphiques d'évolution

Alors je vois l'impact de chaque changement de config Et je peux corréler changements config avec métriques

## 16. Export CSV pour analyse externe

Quand je clique sur "Exporter données"

Alors je peux télécharger un CSV avec:

    | colonne | exemple |
    |---|---|
    | date | 2026-01-21 |
    | version_config | v1.2.3 |
    | taux_completion | 0.72 |
    | engagement_moyen | 0.45 |
    | duree_session_min | 27 |

## 17. Alerte automatique si métrique critique baisse

Étant donné que le taux de complétion moyen est à 70%

Quand une nouvelle config fait baisser le taux à 55%

Alors je reçois une alerte email "Baisse critique du taux de complétion" Et je peux rollback rapidement

## 18. Prévisualisation impact avant application

Étant donné que je modifie poids_geo_ancre de 0.7 à 0.9

Quand je clique sur "Prévisualiser impact"

Alors je vois une simulation sur échantillon de 1000 utilisateurs Et je vois l'estimation d'impact sur les métriques clés

## 19. Notes et commentaires sur modifications config

Quand je modifie une configuration

Alors je peux ajouter une note "Test pour améliorer contenu local" Et cette note est visible dans l'historique des versions Et l'équipe peut comprendre le contexte des changements

## 20. Permissions admin pour modification config

Étant donné que je suis un admin junior

Quand j'essaie de modifier un paramètre critique

Alors l'accès est refusé Et je vois "Permission admin senior requise" Et seuls les admins seniors peuvent modifier les paramètres

# Paramétrabilité utilisateur et profils

> En tant qu'utilisateur Je veux personnaliser mon expérience de recommandation Afin d'adapter l'application à mes différents contextes d'usage

25 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible Et que je suis connecté

## 1. Accès aux paramètres de personnalisation

Quand j'ouvre les paramètres de personnalisation

Alors je vois trois curseurs disponibles:

    | curseur | description |
    |---|---|
    | Géolocalisation | Local ← slider → National |
    | Découverte | 0% ← slider → 50% |
    | Politique | Masquer / Équilibré / Mes préférences |

## 2. Modifier le curseur Géolocalisation vers Local

Étant donné que le curseur Géolocalisation est au centre (défaut)

Quand je déplace le curseur vers "Local" (gauche)

Alors l'algorithme privilégie fortement les contenus proches Et la pondération géographique augmente Et je vois le message "Recommandations locales privilégiées"

## 3. Modifier le curseur Géolocalisation vers National

Étant donné que le curseur Géolocalisation est au centre

Quand je déplace le curseur vers "National" (droite)

Alors l'algorithme privilégie la découverte nationale Et la pondération géographique diminue Et je reçois des contenus de toute la France

## 4. Curseur Découverte à 0% - aucun aléatoire

Quand je règle le curseur Découverte à 0%

Alors 0% de contenus aléatoires dans mes recommandations Et 100% de contenus calculés selon score combiné Et je vois le message "Personnalisation maximale"

## 5. Curseur Découverte à 10% - défaut équilibré

Quand je règle le curseur Découverte à 10%

Alors 10% de contenus aléatoires Et 90% de contenus calculés Et je vois le message "Équilibre découverte/personnalisation"

## 6. Curseur Découverte à 30% - découverte élevée

Quand je règle le curseur Découverte à 30%

Alors 30% de contenus aléatoires Et 70% de contenus calculés Et je vois le message "Découverte élevée activée"

## 7. Curseur Découverte à 50% - découverte maximale

Quand je règle le curseur Découverte à 50%

Alors 50% de contenus aléatoires Et 50% de contenus calculés Et je vois le message "Découverte maximale (équivaut à national)"

## 8. Créer un profil personnalisé "Trajet quotidien"

Quand je crée un nouveau profil nommé "🚗 Trajet quotidien" Et que je configure:

    | parametre | valeur |
    |---|---|
    | Géolocalisation | Local |
    | Découverte | 5% |
    | Politique | Masquer |

Et que je sauvegarde

Alors le profil "🚗 Trajet quotidien" est créé Et je peux l'activer en un clic

## 9. Créer un profil "Road trip"

Quand je crée un profil "🛣️ Road trip" Et que je configure:

    | parametre | valeur |
    |---|---|
    | Géolocalisation | Régional |
    | Découverte | 30% |
    | Politique | Équilibré |

Alors le profil est sauvegardé Et je peux switcher entre profils facilement

## 10. Créer un profil "Enfants"

Quand je crée un profil "👶 Enfants" Et que j'active le Mode Kids

Alors tous les paramètres sont adaptés pour enfants:

    | parametre | valeur |
    |---|---|
    | Mode Kids | Activé |
    | Politique | Masquer (forcé) |
    | Publicité | Aucune |

## 11. Activer un profil existant

Étant donné que j'ai créé un profil "🚗 Trajet quotidien"

Quand je clique sur "Activer" pour ce profil

Alors tous les paramètres du profil sont appliqués Et je vois le message "Profil 'Trajet quotidien' activé" Et l'algorithme utilise ces paramètres immédiatement

## 12. Synchronisation profils entre devices

Étant donné que j'ai créé 3 profils sur mon iPhone

Quand je me connecte sur mon iPad

Alors mes 3 profils sont automatiquement synchronisés Et je peux les utiliser sur l'iPad

## 13. Modification d'un profil synchronisée

Étant donné que j'ai un profil "Road trip" sur iPhone

Quand je modifie ce profil sur iPhone

Alors la modification est synchronisée sur tous mes devices Et le profil est mis à jour partout en temps réel

## 14. Pas de partage de profils entre utilisateurs

Étant donné que j'ai créé des profils personnalisés Et que ma conjointe a un compte RoadWave

Quand elle se connecte sur son compte

Alors elle ne voit pas mes profils Et chaque utilisateur a ses propres profils

## 15. Auto-switch selon contexte (détection trajet récurrent)

Étant donné que j'utilise toujours le profil "Trajet quotidien" Et que je pars de chez moi vers mon travail tous les matins à 8h

Quand le système détecte ce trajet récurrent

Alors le profil "Trajet quotidien" est activé automatiquement Et je reçois une notification "Profil 'Trajet quotidien' activé"

## 16. Désactiver l'auto-switch

Étant donné que l'auto-switch de profil est actif

Quand je désactive cette option dans les paramètres

Alors les profils ne changent plus automatiquement Et je dois les activer manuellement

## 17. Blocage modification si vitesse GPS >10 km/h

Étant donné que je conduis à 50 km/h

Quand j'essaie de modifier un curseur

Alors la modification est bloquée Et je vois le message "Modification impossible pendant la conduite" Et je dois m'arrêter ou être passager pour modifier

## 18. Modification possible si vitesse <10 km/h

Étant donné que je suis arrêté à un feu rouge (5 km/h)

Quand j'essaie de modifier un curseur

Alors la modification est autorisée Et je peux ajuster les paramètres

## 19. Warning au lancement app

Quand je lance l'application pour la première fois

Alors je vois un warning "Configurez vos préférences avant de prendre la route" Et un bouton "Configurer maintenant" Et je peux accéder rapidement aux paramètres

## 20. Modification uniquement app arrêtée ou mode passager

Étant donné que je suis passager dans une voiture Et que le mode passager est activé

Quand j'essaie de modifier les paramètres

Alors la modification est autorisée Et le blocage vitesse GPS ne s'applique pas

## 21. Statistiques d'utilisation des profils

Étant donné que j'utilise plusieurs profils

Quand je consulte mes statistiques

Alors je vois:

    | metrique | exemple |
    |---|---|
    | Profil le plus utilisé | Trajet quotidien |
    | Heures par profil | 25h / 10h / 5h |
    | Dernier profil actif | Road trip |

## 22. Supprimer un profil

Étant donné que j'ai créé un profil "Test"

Quand je supprime ce profil

Alors le profil est définitivement supprimé Et je vois le message "Profil 'Test' supprimé" Et il disparaît de tous mes devices

## 23. Limite de profils par utilisateur

Étant donné que j'ai créé 10 profils

Quand j'essaie de créer un 11ème profil

Alors la création échoue Et je vois le message "Maximum 10 profils par utilisateur"

## 24. Dupliquer un profil existant

Étant donné que j'ai un profil "Trajet quotidien"

Quand je clique sur "Dupliquer"

Alors un nouveau profil "Trajet quotidien (copie)" est créé Et il a les mêmes paramètres que l'original Et je peux le modifier indépendamment

## 25. Réinitialiser un profil aux valeurs par défaut

Étant donné que j'ai modifié un profil

Quand je clique sur "Réinitialiser"

Alors tous les paramètres reviennent aux valeurs par défaut:

    | parametre | valeur défaut |
    |---|---|
    | Géolocalisation | Équilibré |
    | Découverte | 10% |
    | Politique | Équilibré |

# Formule de scoring et recommandation

> En tant que système de recommandation Je veux calculer un score combiné pour chaque contenu Afin de proposer les contenus les plus pertinents à l'utilisateur

21 scénarios

> Contexte commun à tous les scénarios

> Étant donné que l'API RoadWave est disponible

## 1. Calcul du score géographique linéaire

Étant donné qu'un contenu existe à Paris Et que la distance_max_km est configurée à 200 km

Quand un utilisateur est à 50 km du contenu

Alors le score_geo = 1 - (50 / 200) = 0.75

## 2. Score géo à distance nulle (sur place)

Étant donné qu'un contenu existe à un point GPS précis

Quand un utilisateur est exactement au même point (0 km)

Alors le score_geo = 1.0 (maximum)

## 3. Score géo à distance_max (200 km)

Étant donné qu'un contenu existe à Paris

Quand un utilisateur est à 200 km du contenu

Alors le score_geo = 1 - (200 / 200) = 0.0

## 4. Score géo au-delà de distance_max

Étant donné qu'un contenu existe à Paris

Quand un utilisateur est à 250 km du contenu (au-delà de 200 km max)

Alors le score_geo = 0.0 (minimum) Et le contenu a peu de chances d'être recommandé sauf engagement très élevé

## 5. Calcul du score d'intérêts avec jauges utilisateur

Étant donné qu'un utilisateur a les jauges suivantes:

    | categorie | niveau |
    |---|---|
    | Automobile | 80% |
    | Voyage | 60% |
    | Musique | 40% |

Et qu'un contenu est tagué "Automobile" et "Voyage"

Quand l'algorithme calcule le score_interets

Alors score_interets = (0.8 + 0.6) / 2 = 0.7

## 6. Score d'intérêts avec un seul tag

Étant donné qu'un utilisateur a la jauge "Économie" à 90% Et qu'un contenu est tagué uniquement "Économie"

Quand l'algorithme calcule le score_interets

Alors score_interets = 0.9

## 7. Score d'intérêts avec tags non matchés

Étant donné qu'un utilisateur a des jauges "Sport" et "Politique" élevées Et qu'un contenu est tagué "Musique" et "Philosophie" Et que l'utilisateur n'a pas ces catégories

Quand l'algorithme calcule le score_interets

Alors score_interets = 0.5 (neutre par défaut pour catégories inconnues)

## 8. Calcul du score d'engagement avec métriques

Étant donné qu'un contenu a:

    | metrique | valeur |
    |---|---|
    | ecoutes | 1000 |
    | ecoutes_completes | 700 |
    | likes | 300 |
    | abonnements_apres | 50 |

Quand l'algorithme calcule le score_engagement

Alors taux_completion = 700 / 1000 = 0.7 Et ratio_likes = 300 / 1000 = 0.3 Et ratio_abonnements = 50 / 1000 = 0.05 Et score_engagement = (0.7 × 0.5) + (0.3 × 0.3) + (0.05 × 0.2) = 0.35 + 0.09 + 0.01 = 0.45

## 9. Contenu avec moins de 50 écoutes - score neutre

Étant donné qu'un contenu a seulement 30 écoutes

Quand l'algorithme calcule le score_engagement

Alors score_engagement = 0.5 (neutre par défaut) Et le contenu n'est pas pénalisé pour manque de données

## 10. Contenu avec exactement 50 écoutes - calcul réel

Étant donné qu'un contenu a exactement 50 écoutes Et des métriques d'engagement complètes

Quand l'algorithme calcule le score_engagement

Alors le score est calculé normalement (pas de seuil neutre)

## 11. Bonus aléatoire - 10% des recommandations

Étant donné qu'un utilisateur demande 10 recommandations Et que la part_aleatoire_global est à 10%

Quand l'algorithme génère les recommandations

Alors 1 contenu sur 10 est tiré aléatoirement Et 9 contenus sont calculés avec le score combiné Et le contenu aléatoire n'est pas dans l'historique déjà écouté

## 12. Curseur utilisateur découverte à 0% - aucun aléatoire

Étant donné qu'un utilisateur configure le curseur découverte à 0%

Quand l'utilisateur demande 20 recommandations

Alors les 20 contenus sont calculés avec le score combiné Et aucun contenu aléatoire n'est proposé

## 13. Curseur utilisateur découverte à 50% - découverte max

Étant donné qu'un utilisateur configure le curseur découverte à 50%

Quand l'utilisateur demande 20 recommandations

Alors 10 contenus sont tirés aléatoirement Et 10 contenus sont calculés avec le score combiné

## 14. Score final combiné pour contenu géo-ancré

Étant donné qu'un contenu "Géo-ancré" a:

    | parametre | valeur |
    |---|---|
    | score_geo | 0.9 |
    | score_interets | 0.6 |
    | score_engagement | 0.45 |
    | poids_geo | 0.7 |
    | poids_interets | 0.1 |
    | poids_engagement | 0.2 |

Quand l'algorithme calcule le score_final

Alors score_final = (0.9 × 0.7) + (0.6 × 0.1) + (0.45 × 0.2) Et score_final = 0.63 + 0.06 + 0.09 = 0.78

## 15. Score final combiné pour contenu géo-neutre

Étant donné qu'un contenu "Géo-neutre" a:

    | parametre | valeur |
    |---|---|
    | score_geo | 0.3 |
    | score_interets | 0.9 |
    | score_engagement | 0.6 |
    | poids_geo | 0.2 |
    | poids_interets | 0.6 |
    | poids_engagement | 0.2 |

Quand l'algorithme calcule le score_final

Alors score_final = (0.3 × 0.2) + (0.9 × 0.6) + (0.6 × 0.2) Et score_final = 0.06 + 0.54 + 0.12 = 0.72 Et le contenu peut être recommandé malgré la distance

## 16. Contenu viral lointain peut être recommandé

Étant donné qu'un contenu viral existe à Paris Et qu'il a un score_engagement très élevé de 0.95 Et qu'un utilisateur est à Marseille (score_geo = 0.1)

Quand l'algorithme calcule le score_final

Alors le score_engagement élevé compense le score_geo faible Et le contenu peut apparaître dans les recommandations

## 17. Ordre de recommandation par score décroissant

Étant donné 5 contenus avec les scores suivants:

    | contenu | score_final |
    |---|---|
    | Contenu A | 0.85 |
    | Contenu B | 0.72 |
    | Contenu C | 0.90 |
    | Contenu D | 0.65 |
    | Contenu E | 0.78 |

Quand l'utilisateur demande des recommandations

Alors l'ordre de proposition est:

    | position | contenu |
    |---|---|
    | 1 | Contenu C |
    | 2 | Contenu A |
    | 3 | Contenu E |
    | 4 | Contenu B |
    | 5 | Contenu D |

## 18. Exclusion de l'historique déjà écouté >80%

Étant donné qu'un utilisateur a écouté les contenus suivants:

    | contenu | completion |
    |---|---|
    | Contenu A | 85% |
    | Contenu B | 95% |
    | Contenu C | 30% |

Quand l'algorithme génère les recommandations

Alors "Contenu A" et "Contenu B" ne sont jamais proposés Mais "Contenu C" peut être reproposé

## 19. Pré-calcul de 5 contenus suivants

Étant donné qu'un utilisateur écoute un contenu

Quand l'algorithme prépare les contenus suivants

Alors 5 contenus sont pré-calculés selon le score Et ces contenus sont mis en cache pour performance

## 20. Recalcul si déplacement >10 km

Étant donné que 5 contenus suivants sont pré-calculés Et que l'utilisateur se déplace de 12 km

Quand l'utilisateur demande le contenu suivant

Alors l'algorithme recalcule les scores avec la nouvelle position Et propose de nouveaux contenus plus pertinents géographiquement

## 21. Recalcul après 10 minutes d'inactivité

Étant donné que 5 contenus suivants sont pré-calculés Et que 11 minutes se sont écoulées sans action

Quand l'utilisateur demande le contenu suivant

Alors l'algorithme recalcule les scores Et prend en compte les nouveaux contenus publiés

# Anonymisation des données GPS après 24h

18 scénarios

> Contexte commun à tous les scénarios

> Étant donné que je suis un utilisateur avec le GPS activé Et que j'utilise l'application depuis plusieurs jours

## 1. Conservation des données GPS précises pendant 24h

Étant donné que j'écoute un contenu à la position GPS 48.8566, 2.3522 (Paris, Tour Eiffel) Et qu'il est 10:00 le 2025-01-20

Quand l'événement d'écoute est enregistré en base de données

Alors les coordonnées précises 48.8566, 2.3522 sont stockées Et le champ `anonymized` est à `false` Et le champ `created_at` contient "2025-01-20 10:00:00" Et ces données précises servent à la recommandation personnalisée

## 2. Conversion en geohash après 24h

Étant donné que j'ai écouté un contenu le 2025-01-20 à 10:00 à la position 48.8566, 2.3522

Quand le job quotidien d'anonymisation s'exécute le 2025-01-21 à 02:00

Alors les coordonnées précises sont converties en geohash précision 5 Et le geohash correspond à une zone d'environ 5km² Et les coordonnées originales 48.8566, 2.3522 sont supprimées définitivement Et le champ `anonymized` passe à `true` Et il est impossible de retrouver la position précise d'origine

## 3. Requête SQL d'anonymisation (PostGIS)

Étant donné que le job quotidien d'anonymisation s'exécute

Quand la requête SQL suivante est exécutée:

Alors toutes les positions vieilles de plus de 24h sont anonymisées Et le processus est automatique et irréversible Et les données sont conformes RGPD

## 4. Précision du geohash niveau 5

Étant donné qu'une position GPS est convertie en geohash précision 5

Quand on analyse la zone couverte

Alors la zone fait environ 5km² (4.9km × 4.9km) Et cette précision est suffisante pour des analytics agrégées Et cette précision ne permet pas d'identifier un individu (conformité CNIL)

## 5. Exemple de conversion Paris

Étant donné que ma position précise est 48.8566, 2.3522 (Tour Eiffel)

Quand la conversion en geohash précision 5 est appliquée

Alors le geohash généré est "u09wh" Et ce geohash couvre une zone de ~5km² autour de la Tour Eiffel Et toutes les positions dans cette zone partagent le même geohash Et il est impossible de distinguer deux utilisateurs dans cette zone

## 6. Conservation de l'historique personnel utilisateur

Étant donné que j'ai écouté des contenus aux positions suivantes:

    | date | heure | latitude | longitude | lieu |
    |---|---|---|---|---|
    | 2025-01-15 | 08:30 | 48.8566 | 2.3522 | Paris |
    | 2025-01-16 | 14:00 | 43.6047 | 1.4442 | Toulouse |
    | 2025-01-17 | 19:00 | 45.7640 | 4.8357 | Lyon |

Quand j'ouvre mon historique personnel dans "Profil > Mes trajets"

Alors je vois mes trajets avec les positions précises intégrales Et ces données ne sont pas anonymisées tant que mon compte est actif Et seul moi peut accéder à ces données Et elles ne sont pas utilisées pour des analytics globales

## 7. Anonymisation pour analytics globales uniquement

Étant donné que RoadWave génère des analytics agrégées

Quand l'équipe analyse les zones géographiques populaires

Alors seules les données anonymisées (geohash) sont utilisées Et les positions précises de l'historique personnel ne sont jamais agrégées Et les heatmaps de trafic utilisent uniquement les geohash ~5km²

## 8. Planification du job d'anonymisation

Étant donné que le système est en production

Quand on consulte les jobs planifiés (cron)

Alors un job "anonymize_gps_data" est configuré Et le job s'exécute tous les jours à 02:00 (heure creuse) Et le job traite toutes les positions vieilles de plus de 24h Et un log est généré pour traçabilité

## 9. Exécution du job avec métriques

Étant donné que le job d'anonymisation s'exécute le 2025-01-21 à 02:00

Quand le job se termine

Alors un rapport est généré avec:

    | métrique | valeur |
    |---|---|
    | Nombre de positions traitées | 15420 |
    | Nombre de positions anonymisées | 15420 |
    | Durée d'exécution | 3.5s |
    | Erreurs | 0 |

Et le rapport est loggé dans Sentry/Grafana Et une alerte est envoyée si le job échoue

## 10. Performances du job d'anonymisation

Étant donné que 100 000 positions doivent être anonymisées

Quand le job s'exécute

Alors le traitement se fait en moins de 30 secondes Et la requête PostGIS est optimisée avec index Et aucun impact sur les performances de l'application en production

## 11. Impossibilité de réidentification

Étant donné qu'une position a été anonymisée en geohash "u09wh"

Quand un attaquant tente de retrouver la position précise d'origine

Alors il est impossible de déterminer la position exacte Et des milliers de positions précises correspondent au même geohash Et il n'y a aucune traçabilité vers la position originale Et cette anonymisation est irréversible

## 12. Conformité CNIL - données véritablement anonymisées

Étant donné que les positions sont converties en geohash précision 5

Quand un auditeur CNIL vérifie la conformité

Alors les données sont considérées comme véritablement anonymisées Et elles ne sont plus considérées comme des données personnelles Et aucun consentement n'est requis pour leur traitement analytique Et elles peuvent être conservées indéfiniment

## 13. Heatmap de trafic avec données anonymisées

Étant donné que RoadWave génère une heatmap des zones populaires

Quand on analyse les données utilisées

Alors seules les positions anonymisées (geohash) sont agrégées Et la heatmap montre des zones de ~5km² Et aucune position précise n'est révélée Et cette analyse ne nécessite pas de consentement utilisateur (données anonymes)

## 14. Statistiques géographiques par département

Étant donné que RoadWave analyse l'utilisation par département

Quand les statistiques sont générées

Alors les données anonymisées sont agrégées par département Et les résultats montrent: "Paris (75): 12 500 écoutes, Lyon (69): 8 300 écoutes" Et aucune donnée personnelle n'est révélée Et les statistiques sont RGPD-compliant

## 15. Coût de la solution d'anonymisation

Étant donné que PostGIS est utilisé pour l'anonymisation GPS

Quand on calcule le coût de la solution

Alors le coût est de 0€ (PostGIS inclus dans PostgreSQL) Et aucune librairie tierce n'est nécessaire Et la solution est entièrement maîtrisée (self-hosted)

## 16. Anonymisation respecte les positions en cours de session

Étant donné que je suis en train d'écouter du contenu actuellement Et que certaines de mes positions ont plus de 24h

Quand le job d'anonymisation s'exécute

Alors mes positions de plus de 24h sont anonymisées Mais ma position actuelle (session en cours) reste précise Et la recommandation continue de fonctionner normalement

## 17. Suppression de compte et anonymisation GPS

Étant donné que je demande la suppression de mon compte

Quand le compte est supprimé (après grace period de 30j)

Alors toutes mes positions GPS (précises et anonymisées) sont supprimées Et mon historique personnel de trajets est supprimé Et aucune donnée GPS ne subsiste, même anonymisée

## 18. Export de données avant anonymisation

Étant donné que je demande un export de mes données Et que certaines de mes positions ont été anonymisées

Quand l'export est généré

Alors les positions précises de mon historique personnel sont incluses Mais les positions déjà anonymisées (>24h, analytics) apparaissent en geohash Et l'export précise quelles données ont été anonymisées et pourquoi

# Conformité administrative RGPD (Registre, Breach, DPO)

22 scénarios (21 standards, 1 plan)

## 1. Registre des traitements en Markdown versionné Git

Étant donné que RoadWave doit tenir un registre des traitements

Quand on consulte la documentation

Alors un fichier `docs/rgpd/registre-traitements.md` existe Et le fichier est versionné dans Git Et l'historique des modifications est traçable via Git Et chaque traitement est documenté dans une section dédiée

## 2. Contenu obligatoire pour chaque traitement

Étant donné que le registre des traitements contient le traitement "Géolocalisation utilisateurs"

Quand on lit la section correspondante

Alors les informations suivantes sont présentes:

    | information obligatoire | exemple |
    |---|---|
    | Nom du traitement | Géolocalisation utilisateurs |
    | Finalité | Recommandation de contenu géolocalisé |
    | Catégories de données | Coordonnées GPS, historique de position |
    | Base légale | Consentement (Article 6.1.a RGPD) |
    | Durée de conservation | 24h (précis), puis geohash anonymisé |
    | Destinataires | Aucun tiers |
    | Transferts hors UE | Aucun |
    | Mesures de sécurité | TLS 1.3, anonymisation après 24h |

## 3. 📋 Plan: Traitements documentés dans le registre

Étant donné que le registre des traitements est complet

Quand on liste tous les traitements

Alors le traitement "" est documenté avec la base légale ""

📊 Exemples de données:

traitement | base_legale
--- | ---
Géolocalisation utilisateurs | Consentement
Historique d'écoute | Intérêt légitime
Création de contenu | Exécution du contrat
Analytics (Matomo) | Consentement
Paiements (Mangopay) | Exécution du contrat
Modération contenus | Intérêt légitime
Notifications push | Consentement

## 4. Review trimestrielle du registre

Étant donné que le registre des traitements existe

Quand on consulte l'historique Git

Alors une mise à jour est effectuée au moins tous les 3 mois Et chaque mise à jour a un commit avec message explicite Et un tag Git marque chaque review trimestrielle Et les modifications sont traçables (auteur, date, changements)

## 5. Mise à jour immédiate si nouveau traitement

Étant donné qu'une nouvelle fonctionnalité nécessite un traitement de données

Quand la fonctionnalité est développée

Alors le registre est mis à jour AVANT le déploiement en production Et le nouveau traitement est documenté complètement Et un commit Git enregistre l'ajout Et le DPO valide la conformité RGPD du nouveau traitement

## 6. Migration future vers interface admin PostgreSQL

Étant donné que RoadWave dépasse 100 000 utilisateurs

Quand la complexité du registre augmente

Alors une interface admin PostgreSQL est développée Et le registre Markdown est migré vers la base de données Et l'historique Git est conservé pour audit Et l'interface permet une gestion plus efficace des traitements

## 7. Détection automatique d'événements critiques

Étant donné que le système de monitoring est actif

Quand un événement critique se produit

Alors une alerte est envoyée selon le type d'événement:

    | événement | outil | alerte |
    |---|---|---|
    | Erreur backend critique | Sentry | Discord/Slack immédiat |
    | Pic requêtes anormal | Grafana | Email équipe |
    | Accès non autorisé DB | PostgreSQL logs | SMS fondateur |
    | Authentification suspecte | Zitadel alerts | Email équipe |

Et les alertes permettent une réaction rapide

## 8. Runbook de procédure breach disponible

Étant donné qu'une violation de données potentielle est détectée

Quand l'équipe consulte la documentation

Alors un runbook `docs/rgpd/procedure-breach.md` existe Et le runbook contient une checklist 72h CNIL Et chaque étape est clairement documentée Et les contacts d'urgence sont listés

## 9. Checklist 72h en cas de breach

Étant donné qu'une violation de données est confirmée

Quand l'équipe suit la procédure breach

Alors les étapes suivantes sont exécutées dans les délais:

    | délai | étape |
    |---|---|
    | H+0 | Détection et confinement immédiat |
    | H+24 | Évaluation gravité (données concernées, users impactés) |
    | H+48 | Notification CNIL si risque pour utilisateurs |
    | H+72 | Notification utilisateurs si risque élevé |

Et chaque étape est documentée pour audit

## 10. Évaluation de la gravité du breach

Étant donné qu'une violation de données est détectée

Quand l'équipe évalue la gravité

Alors les critères suivants sont analysés:

    | critère | exemple |
    |---|---|
    | Type de données concernées | Emails, mots de passe, GPS, etc. |
    | Nombre d'utilisateurs impactés | 10, 100, 10000, etc. |
    | Mesures de sécurité existantes | Chiffrement, hachage, anonymisation |
    | Risque pour les droits et libertés | Faible, modéré, élevé |

Et si le risque est élevé, la CNIL est notifiée sous 72h

## 11. Notification CNIL dans les 72h

Étant donné qu'un breach avec risque élevé est confirmé à 10:00 le 2025-01-20

Quand la gravité est évaluée comme nécessitant une notification

Alors la CNIL est notifiée avant 10:00 le 2025-01-23 (72h) Et la notification contient:

    | information |
    |---|
    | Nature de la violation |
    | Données concernées |
    | Nombre d'utilisateurs impactés |
    | Conséquences probables |
    | Mesures prises |
    | Mesures de remédiation |

Et un email pré-rédigé (template) est utilisé pour gagner du temps

## 12. Notification des utilisateurs si risque élevé

Étant donné qu'un breach impacte 5000 utilisateurs Et que le risque est élevé (mots de passe non chiffrés exposés)

Quand la CNIL est notifiée

Alors les utilisateurs impactés sont notifiés dans les 72h Et l'email contient: Et un lien de réinitialisation de mot de passe est inclus

## 13. Aucune notification si risque faible

Étant donné qu'un breach mineur est détecté (logs techniques exposés, aucune donnée personnelle)

Quand l'équipe évalue la gravité

Alors le risque est jugé faible Et aucune notification CNIL n'est requise (Article 33.1 RGPD) Et aucune notification utilisateur n'est envoyée Et un log interne est créé pour traçabilité

## 14. Monitoring proactif pour éviter découverte tardive

Étant donné que Sentry et Grafana sont configurés

Quand un comportement anormal est détecté

Alors une alerte est envoyée en temps réel Et l'équipe peut réagir avant qu'un breach majeur ne se produise Et les logs sont analysés quotidiennement pour détecter des anomalies Et cette approche proactive limite les risques de découverte tardive

## 15. Fondateur = DPO temporaire (MVP)

Étant donné que RoadWave est en phase MVP Et que l'entreprise a moins de 250 employés

Quand on vérifie l'obligation légale d'avoir un DPO

Alors le DPO n'est pas obligatoire selon le RGPD Article 37 Et le fondateur assume temporairement le rôle de DPO Et le fondateur suit la formation CNIL gratuite (4h)

## 16. Formation CNIL du DPO temporaire

Étant donné que le fondateur est DPO temporaire

Quand on vérifie sa formation

Alors le fondateur a suivi la formation CNIL en ligne (4h) Et le fondateur a obtenu la certification "Atelier RGPD" (gratuit) Et le certificat est conservé pour audit Et la formation couvre:

    | sujet |
    |---|
    | Principes fondamentaux du RGPD |
    | Droits des personnes |
    | Sécurité des données |
    | Violations de données (breach) |
    | Registre des traitements |

## 17. Contact DPO publié et accessible

Étant donné que je consulte les mentions légales de RoadWave

Quand je cherche le contact du DPO

Alors l'email "dpo@roadwave.fr" est clairement affiché Et cet email est également dans les CGU Et le délai de réponse garanti est de 1 mois maximum (RGPD Article 12.3) Et une adresse postale est également fournie

## 18. Demande d'exercice de droits RGPD au DPO

Étant donné que je veux exercer mon droit d'accès à mes données

Quand j'envoie un email à dpo@roadwave.fr

Alors je reçois un accusé de réception dans les 48h Et ma demande est traitée dans un délai maximum de 1 mois Et si le délai dépasse 1 mois, je suis informé de la prolongation (max 2 mois supplémentaires) Et la réponse est complète et conforme au RGPD

## 19. Types de demandes gérées par le DPO

Étant donné que je contacte le DPO

Quand j'envoie une demande

Alors le DPO peut traiter les demandes suivantes:

    | type de demande |
    |---|
    | Droit d'accès (Article 15) |
    | Droit de rectification (Article 16) |
    | Droit à l'effacement (Article 17) |
    | Droit à la portabilité (Article 20) |
    | Droit d'opposition (Article 21) |
    | Plainte RGPD |
    | Question sur le traitement des données |

Et chaque demande reçoit une réponse personnalisée

## 20. Migration vers DPO externe si croissance

Étant donné que RoadWave dépasse 100 000 utilisateurs

Quand la charge de travail DPO augmente

Alors un DPO externe mutualisé est engagé Et le coût est d'environ 200€/mois Et le DPO externe a les certifications CNIL requises Et un contrat de sous-traitance RGPD est signé

## 21. Recrutement DPO interne si >10 employés

Étant donné que RoadWave a plus de 10 employés

Quand l'entreprise se structure

Alors un DPO interne peut être recruté Et le DPO interne a une certification CNIL (AFCDP ou équivalent) Et le DPO est indépendant et ne peut être licencié pour ses fonctions Et le DPO a un accès direct à la direction

## 22. Récapitulatif des coûts RGPD

Étant donné que toutes les mesures RGPD sont en place

Quand on calcule le coût total mensuel

Alors le récapitulatif est le suivant:

    | mesure | implémentation | coût |
    |---|---|---|
    | Consentement | Tarteaucitron.js + PostgreSQL | 0€ |
    | Anonymisation GPS | Geohash PostGIS (24h) | 0€ |
    | Export données | JSON+HTML+ZIP asynchrone | 0€ |
    | Suppression compte | Grace period 30j + anonymisation | 0€ |
    | Mode dégradé | GeoIP MaxMind + GPS optionnel | 0€ |
    | Conservation | Purge auto 5 ans inactivité | 0€ |
    | Analytics | Matomo self-hosted | ~5€/mois |
    | Registre traitements | Markdown Git | 0€ |
    | Breach detection | Sentry + Grafana + runbook | 0€ (< 5K events) |
    | DPO | Fondateur formé CNIL | 0€ |

Et le coût total est d'environ 5€/mois Et cette conformité est 100% opensource et maîtrisée

# Gestion du consentement RGPD

16 scénarios

> Contexte commun à tous les scénarios

> Étant donné que je suis un nouvel utilisateur Et que j'accède à l'application pour la première fois

## 1. Affichage du banner de consentement au premier lancement web

Étant donné que j'accède à l'application web pour la première fois

Quand la page se charge

Alors un banner RGPD Tarteaucitron.js s'affiche Et le banner est en français Et le banner propose les options suivantes:

    | option | description |
    |---|---|
    | Tout accepter | Active tous les consentements |
    | Tout refuser | Refuse tous les consentements optionnels |
    | Personnaliser | Ouvre le panneau de personnalisation |

Et le banner est customisé aux couleurs de RoadWave

## 2. Granularité des consentements

Étant donné que le banner RGPD est affiché

Quand je clique sur "Personnaliser"

Alors je vois les catégories de consentements suivantes:

    | catégorie | type | requis |
    |---|---|---|
    | Fonctionnel | Nécessaire | oui |
    | Analytique | Optionnel | non |
    | Marketing | Optionnel | non |

Et chaque catégorie a une description claire de son usage Et je peux accepter ou refuser chaque catégorie individuellement

## 3. Consentement géolocalisation précise - obligatoire

Étant donné que je suis sur l'application mobile Et que l'onboarding est terminé

Quand l'application a besoin d'accéder à ma position précise

Alors un écran de demande de consentement s'affiche Et le message explique clairement l'usage: Et je peux accepter ou refuser Et si je refuse, l'application bascule en mode dégradé (GeoIP uniquement)

## 4. Double consentement GPS - banner app + permission OS

Étant donné que je veux activer la géolocalisation précise

Quand j'accepte le consentement dans l'application

Alors l'application demande également la permission au système d'exploitation Et sur iOS, la popup système s'affiche: "Autoriser RoadWave à accéder à votre position ?" Et sur Android, la popup système s'affiche avec les options "Toujours autoriser / Autoriser seulement pendant l'utilisation / Refuser" Et les deux consentements (app + OS) doivent être acceptés pour activer le GPS précis

## 5. Enregistrement du consentement en base de données

Étant donné que j'ai accepté les consentements suivants:

    | type | accepté |
    |---|---|
    | Fonctionnel | oui |
    | Analytique | oui |
    | Marketing | non |
    | GPS précis | oui |

Quand je valide mes choix

Alors un enregistrement est créé dans la table `user_consents` Et l'enregistrement contient les champs suivants:

    | champ | valeur |
    |---|---|
    | user_id | [mon ID utilisateur] |
    | consent_type | fonctionnel / analytique / gps |
    | version | 1 |
    | accepted | true / false |
    | timestamp | [date et heure exacte] |

Et chaque type de consentement a un enregistrement séparé

## 6. Versioning des consentements

Étant donné que j'ai accepté le consentement "Analytique" version 1 le 2025-01-01 Et que les CGU sont mises à jour le 2025-06-01

Quand je me connecte après la mise à jour

Alors un nouveau consentement version 2 m'est demandé Et mon ancien consentement version 1 reste dans l'historique Et je dois accepter la nouvelle version pour continuer à utiliser les analytics

## 7. Historique complet conservé pour preuve légale

Étant donné que j'ai modifié mes consentements plusieurs fois:

    | date | consent_type | accepted | version |
    |---|---|---|---|
    | 2025-01-01 | Analytique | oui | 1 |
    | 2025-03-15 | Analytique | non | 1 |
    | 2025-06-01 | Analytique | oui | 2 |

Quand un auditeur CNIL consulte mon historique de consentements

Alors tous les enregistrements sont conservés Et l'historique prouve que chaque consentement a été donné librement Et les timestamps permettent de prouver la conformité à tout moment

## 8. Consentement analytique - optionnel

Étant donné que je refuse le consentement "Analytique"

Quand j'utilise l'application

Alors aucun cookie Matomo `_pk_id` n'est déposé Et aucune donnée d'usage n'est envoyée à Matomo Et l'application fonctionne normalement sans analytics

## 9. Consentement notifications push - optionnel

Étant donné que je refuse le consentement "Notifications push"

Quand un créateur que je suis publie un nouveau contenu

Alors je ne reçois pas de notification push Mais je peux voir le nouveau contenu dans l'application Et l'application fonctionne normalement

## 10. Consentement GPS précis - requis pour fonctionnalités géo

Étant donné que je refuse le consentement "GPS précis"

Quand j'utilise l'application

Alors je peux accéder aux contenus nationaux Mais les contenus géolocalisés précis (Ancré, Contextuel) ne sont pas disponibles Et les audio-guides nécessitent l'activation du GPS Et un banner permanent me rappelle que l'activation du GPS améliore l'expérience

## 11. Révocation d'un consentement depuis les paramètres

Étant donné que j'ai accepté le consentement "Analytique" Et que j'utilise l'application depuis 3 mois

Quand j'ouvre "Paramètres > Confidentialité > Gérer mes consentements"

Alors je vois la liste de tous mes consentements actuels Et je peux révoquer le consentement "Analytique"

Quand je révoque le consentement

Alors un nouvel enregistrement est créé avec `accepted = false` Et le cookie Matomo est supprimé immédiatement Et les analytics sont désactivées à partir de ce moment

## 12. Acceptation d'un consentement précédemment refusé

Étant donné que j'avais refusé le consentement "GPS précis"

Quand j'ouvre "Paramètres > Confidentialité > Gérer mes consentements" Et que je clique sur "Activer la géolocalisation précise"

Alors un nouvel enregistrement est créé avec `accepted = true` Et la permission OS est demandée si ce n'est pas déjà fait Et l'application bascule en mode géolocalisation précise Et les contenus géolocalisés deviennent disponibles immédiatement

## 13. Export de l'historique des consentements pour audit

Étant donné qu'un contrôle CNIL est en cours

Quand l'équipe RoadWave exporte l'historique des consentements

Alors l'export contient pour chaque utilisateur:

    | champ | description |
    |---|---|
    | user_id | ID anonymisé |
    | consent_type | Type de consentement |
    | version | Version des CGU/consentement |
    | accepted | Accepté ou refusé |
    | timestamp | Date et heure exacte |
    | ip_address | IP (anonymisée) au moment du consentement |
    | user_agent | Navigateur/app utilisé |

Et l'export est au format CSV pour analyse Et les données prouvent la conformité RGPD

## 14. Conformité recommandations CNIL

Étant donné que le système de consentement est implémenté

Quand un auditeur CNIL vérifie la conformité

Alors le système respecte les critères suivants:

    | critère CNIL | respecté |
    |---|---|
    | Consentement libre | oui |
    | Consentement spécifique (granulaire) | oui |
    | Consentement éclairé (information claire) | oui |
    | Consentement univoque (action positive) | oui |
    | Révocable à tout moment | oui |
    | Preuve du consentement conservée | oui |

## 15. Tarteaucitron.js self-hosted

Étant donné que l'application web utilise Tarteaucitron.js

Quand je consulte les sources JavaScript chargées

Alors le script Tarteaucitron.js est hébergé sur les serveurs RoadWave Et aucun script tiers (CDN externe) n'est chargé Et le code source de Tarteaucitron.js est vérifiable Et aucune donnée n'est envoyée à un tiers lors de l'affichage du banner

## 16. Coût de la solution - 0€

Étant donné que Tarteaucitron.js est opensource Et que PostgreSQL est utilisé pour le backend

Quand on calcule le coût de la solution de consentement

Alors le coût est de 0€ Et la solution est entièrement maîtrisée (self-hosted) Et aucune dépendance à un service SaaS tiers

# Durée de conservation des données et purge automatique

19 scénarios

> Contexte commun à tous les scénarios

> Étant donné que le système de purge automatique est actif

## 1. Auditeur inactif depuis 5 ans - suppression automatique

Étant donné que je suis un auditeur (sans contenu créé) Et que je ne me suis pas connecté depuis le 2020-01-01 Et que la date actuelle est 2025-01-02 (>5 ans)

Quand le job de purge automatique s'exécute

Alors mon compte est automatiquement supprimé Et toutes mes données personnelles sont effacées Et aucune trace ne subsiste dans la base de données

## 2. Créateur avec contenus actifs - conservation indéfinie

Étant donné que je suis un créateur Et que j'ai créé 10 contenus qui reçoivent encore des écoutes Et que je ne me suis pas connecté depuis 6 ans

Quand le job de purge automatique s'exécute

Alors mon compte n'est pas supprimé Et mes données personnelles sont conservées tant que mes contenus sont écoutés Et mes contenus continuent d'être diffusés normalement

## 3. Créateur inactif sans écoutes - suppression automatique

Étant donné que je suis un créateur Et que j'ai créé 5 contenus Et que je ne me suis pas connecté depuis 5 ans (depuis 2020-01-01) Et que mes contenus n'ont reçu aucune écoute depuis 2 ans (depuis 2023-01-01) Et que la date actuelle est 2025-01-02

Quand le job de purge automatique s'exécute

Alors mon compte est automatiquement supprimé Et mes contenus sont anonymisés (créateur = "Utilisateur supprimé") Et les fichiers audio restent disponibles mais anonymisés

## 4. Notifications par email avant purge

Étant donné que je suis inactif depuis 4 ans et 9 mois

Quand le système détecte que je suis éligible à la purge dans 90 jours

Alors je reçois un email avec le sujet "Votre compte RoadWave sera supprimé dans 90 jours" Et l'email contient: Et un lien de connexion est inclus dans l'email

## 5. Rappels à 90j, 30j et 7j avant suppression

Étant donné que je suis éligible à la purge automatique

Quand les délais s'écoulent

Alors je reçois les emails suivants:

    | délai | sujet email |
    |---|---|
    | 90 jours | Votre compte sera supprimé dans 90 jours |
    | 30 jours | Rappel: Votre compte sera supprimé dans 30 jours |
    | 7 jours | Dernière alerte: suppression dans 7 jours |

Et chaque email contient un lien de connexion pour réactiver le compte Et les notifications push sont également envoyées si activées

## 6. Connexion annule la suppression programmée

Étant donné que je suis éligible à la purge dans 15 jours Et que j'ai reçu plusieurs emails d'avertissement

Quand je me connecte à mon compte

Alors la suppression programmée est annulée immédiatement Et le compteur d'inactivité est remis à zéro Et je reçois un email de confirmation: "Votre compte a été réactivé" Et je peux continuer à utiliser l'application normalement

## 7. Exécution quotidienne du job de purge

Étant donné que le système est en production

Quand on consulte les jobs planifiés

Alors un job "purge_inactive_accounts" est configuré Et le job s'exécute tous les jours à 03:00 (heure creuse) Et le job identifie les comptes éligibles à la purge Et le job traite les suppressions automatiques

## 8. Critères d'éligibilité à la purge

Étant donné que le job de purge s'exécute

Quand le système identifie les comptes éligibles

Alors les critères suivants sont appliqués:

    | type_compte | critères |
    |---|---|
    | Auditeur uniquement | 5 ans sans connexion |
    | Créateur avec contenus actifs | Jamais (tant qu'écoutes) |
    | Créateur inactif | 5 ans sans connexion + 2 ans sans écoute |

Et seuls les comptes remplissant tous les critères sont supprimés

## 9. Métriques du job de purge

Étant donné que le job de purge s'exécute le 2025-01-15

Quand le job se termine

Alors un rapport est généré avec:

    | métrique | exemple |
    |---|---|
    | Comptes analysés | 150 000 |
    | Comptes éligibles à la purge | 350 |
    | Auditeurs supprimés | 300 |
    | Créateurs inactifs supprimés | 50 |
    | Créateurs conservés (actifs) | 0 |
    | Erreurs | 0 |
    | Durée d'exécution | 45s |

Et le rapport est loggé pour audit

## 10. Contenus de comptes purgés conservés anonymement

Étant donné que mon compte créateur est purgé automatiquement

Quand la suppression est effective

Alors mes contenus créés sont conservés indéfiniment Et les contenus sont anonymisés (créateur = "Utilisateur supprimé") Et les fichiers audio restent sur le CDN Et les statistiques d'écoute sont préservées Et les utilisateurs peuvent toujours écouter mes contenus

## 11. Créateur inactif mais contenus populaires - pas de purge

Étant donné que je suis un créateur inactif depuis 6 ans Mais que mes contenus reçoivent 500+ écoutes par mois

Quand le job de purge s'exécute

Alors mon compte n'est pas supprimé Et je continue de recevoir les emails d'avertissement tous les 6 mois Et mes contenus continuent d'être diffusés Et je peux me reconnecter à tout moment

## 12. Qu'est-ce qu'une "écoute" pour le calcul d'inactivité

Étant donné que je suis un créateur

Quand le système calcule si mes contenus sont "actifs"

Alors une "écoute" est comptabilisée si:

    | condition | comptabilisée |
    |---|---|
    | Écoute complète (>80%) | oui |
    | Écoute partielle (>30%) | oui |
    | Skip rapide (<30%) | non |
    | Écoute par un bot (détecté) | non |

Et au moins 1 écoute valide dans les 2 dernières années maintient le compte actif

## 13. Conformité principe de minimisation

Étant donné que le système de purge automatique est en place

Quand un auditeur RGPD vérifie la conformité

Alors le système respecte le principe de minimisation:

    | principe | respecté |
    |---|---|
    | Conservation limitée dans le temps | oui |
    | Suppression automatique après inactivité | oui |
    | Délai raisonnable (5 ans) | oui |
    | Notifications préalables | oui |
    | Exception justifiée (contenus actifs) | oui |

Et le délai de 5 ans est conforme aux standards de l'industrie

## 14. Actions qui réinitialisent le compteur d'inactivité

Étant donné que je suis inactif depuis 4 ans

Quand j'effectue l'une des actions suivantes:

    | action |
    |---|
    | Connexion à l'application |
    | Publication d'un nouveau contenu |
    | Like d'un contenu |
    | Abonnement à un créateur |
    | Modification de mon profil |

Alors le compteur d'inactivité est remis à zéro Et la suppression programmée est annulée Et je ne suis plus éligible à la purge pour 5 ans

## 15. Traçabilité des suppressions automatiques

Étant donné qu'un compte est supprimé automatiquement

Quand la suppression est effective

Alors un log d'audit est créé avec:

    | champ | valeur |
    |---|---|
    | user_id | [ID anonymisé] |
    | account_type | auditeur / créateur |
    | last_login | 2020-01-15T10:00:00Z |
    | last_content_listen | 2023-06-01T14:30:00Z |
    | purge_date | 2025-01-15T03:00:00Z |
    | notifications_sent | 3 (90j, 30j, 7j) |
    | reason | 5_years_inactivity |

Et le log est conservé 5 ans pour audit RGPD Et l'user_id est pseudonymisé pour anonymat

## 16. Compte Premium inactif - pas de privilège spécial

Étant donné que je suis un utilisateur Premium Et que je suis inactif depuis 5 ans

Quand le job de purge s'exécute

Alors mon compte est supprimé comme un compte gratuit Et l'abonnement Premium ne prolonge pas la durée de conservation Et aucun remboursement n'est effectué (compte inactif depuis 5 ans)

## 17. Compte avec signalements de modération - purge différée

Étant donné que je suis éligible à la purge Mais que j'ai des signalements de modération en cours

Quand le job de purge s'exécute

Alors ma purge est différée de 90 jours Et les signalements sont traités en priorité Et si les signalements aboutissent à un ban, le compte est supprimé immédiatement Et si les signalements sont infondés, la purge automatique reprend son cours

## 18. Pourquoi 5 ans d'inactivité

Étant donné que le délai de purge est fixé à 5 ans

Quand on justifie ce choix

Alors les raisons suivantes sont avancées:

    | justification |
    |---|
    | Standard de l'industrie (Google, Facebook: 2-3 ans) |
    | Équilibre raisonnable entre minimisation et utilité |
    | Conforme aux recommandations CNIL |
    | Laisse une marge de réactivation pour utilisateurs |
    | Exception pour créateurs = intérêt légitime communauté |

## 19. Politique de conservation visible dans les CGU

Étant donné que je consulte les CGU de RoadWave

Quand je lis la section "Conservation des données"

Alors la politique de purge automatique est clairement expliquée: Et les utilisateurs sont informés dès l'inscription

# Cookies et analytics avec Matomo self-hosted

20 scénarios

> Contexte commun à tous les scénarios

> Étant donné que je suis un utilisateur de l'application web RoadWave

## 1. Cookies strictement nécessaires - pas de consentement requis

Étant donné que j'accède à l'application web

Quand je me connecte

Alors les cookies techniques suivants sont déposés:

    | cookie | type | durée | finalité | consentement |
    |---|---|---|---|---|
    | session | Technique | 30j | Authentification | Non requis |
    | refresh_token | Technique | 30j | Session persistante | Non requis |

Et ces cookies sont essentiels au fonctionnement de l'application Et ils sont exemptés de consentement selon l'article 82 de la loi Informatique et Libertés

## 2. Cookie analytique Matomo - consentement requis

Étant donné que j'ai accepté le consentement "Analytique"

Quand je navigue sur l'application web

Alors le cookie `_pk_id` est déposé Et la durée de conservation est de 13 mois Et ce cookie sert à Matomo pour analytics Et mon IP est automatiquement anonymisée (2 derniers octets)

## 3. Refus du consentement analytique - pas de cookie Matomo

Étant donné que j'ai refusé le consentement "Analytique"

Quand je navigue sur l'application web

Alors aucun cookie `_pk_id` n'est déposé Et aucune donnée d'usage n'est collectée Et l'application fonctionne normalement sans analytics

## 4. Matomo hébergé sur les serveurs RoadWave

Étant donné que RoadWave utilise Matomo pour les analytics

Quand on analyse l'infrastructure

Alors Matomo est installé sur les serveurs RoadWave (Docker) Et aucune donnée n'est envoyée à un service tiers Et toutes les données restent dans l'UE Et l'accès à Matomo est restreint à l'équipe RoadWave

## 5. IP anonymisées automatiquement

Étant donné que Matomo collecte des données d'usage

Quand une requête est enregistrée

Alors l'adresse IP est automatiquement anonymisée Et les 2 derniers octets sont remplacés par des zéros Et une IP 192.168.1.100 devient 192.168.0.0 Et cette anonymisation est irréversible Et elle est conforme aux recommandations CNIL

## 6. Configuration Matomo conforme RGPD

Étant donné que Matomo est configuré pour RoadWave

Quand on vérifie les paramètres

Alors les configurations suivantes sont activées:

    | paramètre | valeur |
    |---|---|
    | Anonymisation IP (2 octets) | activé |
    | Respect Do Not Track | activé |
    | Suppression auto anciens logs (25 mois) | activé |
    | Géolocalisation IP désactivée | activé |
    | User ID anonymisé | activé |

Et la configuration est RGPD-compliant

## 7. Aucun tracker tiers utilisé

Étant donné que j'accède à l'application web

Quand j'inspecte les requêtes réseau avec les DevTools

Alors aucune requête n'est envoyée vers les domaines suivants:

    | domaine tiers interdit |
    |---|
    | google-analytics.com |
    | facebook.com (Pixel) |
    | hotjar.com |
    | mixpanel.com |
    | segment.io |
    | amplitude.com |

Et toutes les requêtes analytics vont uniquement vers matomo.roadwave.fr

## 8. Conformité zéro cookie tiers

Étant donné que j'analyse les cookies déposés sur roadwave.fr

Quand je consulte la liste des cookies

Alors tous les cookies sont first-party (domaine roadwave.fr) Et aucun cookie tiers (third-party) n'est présent Et cette politique respecte les recommandations CNIL 2020

## 9. Alternative Plausible SaaS (EU-hosted)

Étant donné que RoadWave pourrait utiliser Plausible au lieu de Matomo

Quand on compare les deux solutions

Alors Plausible a les caractéristiques suivantes:

    | caractéristique | valeur |
    |---|---|
    | Hébergement | UE (Allemagne) |
    | Conformité RGPD | Natif (pas de cookie) |
    | Coût | 9€/mois (50K pageviews) |
    | IP anonymisées | Automatique |
    | Consentement requis | Non (selon CNIL 2020) |

Mais Matomo self-hosted reste le choix prioritaire (0€, contrôle total)

## 10. Aucun transfert de données hors UE

Étant donné que Matomo est self-hosted

Quand on analyse les flux de données

Alors aucune donnée d'analytics n'est transférée hors de l'UE Et les serveurs sont localisés en France Et aucun transfert vers les US (pas de Privacy Shield / DPF requis) Et la souveraineté des données est garantie

## 11. Matomo self-hosted - coût estimé

Étant donné que Matomo est hébergé sur l'infrastructure RoadWave

Quand on calcule le coût mensuel

Alors le coût est d'environ 5€/mois:

    | composant | coût |
    |---|---|
    | Serveur supplémentaire | 0€ (mutualisé) |
    | Base de données MySQL | 0€ (mutualisé) |
    | Stockage logs (25 mois) | ~5€/mois |
    | License Matomo | 0€ (opensource) |

Et ce coût est marginal comparé à un SaaS tiers (9-50€/mois)

## 12. Respect du signal Do Not Track (DNT)

Étant donné que mon navigateur envoie le header "DNT: 1"

Quand j'accède à l'application web

Alors Matomo détecte le signal DNT Et aucune donnée d'usage n'est collectée Et aucun cookie `_pk_id` n'est déposé Et l'application fonctionne normalement Et un message discret s'affiche: "Vos préférences de confidentialité sont respectées (DNT activé)"

## 13. Logs Matomo supprimés après 25 mois

Étant donné que Matomo collecte des données d'usage

Quand les logs atteignent 25 mois d'ancienneté

Alors un job automatique supprime les anciens logs Et seules les données agrégées (rapports) sont conservées Et les données brutes (logs) sont supprimées définitivement Et cette politique respecte le principe de minimisation RGPD

## 14. Données collectées par Matomo

Étant donné que j'ai accepté le consentement "Analytique"

Quand je navigue sur l'application web

Alors Matomo collecte les données suivantes:

    | donnée collectée | anonymisée |
    |---|---|
    | Pages visitées | non |
    | Durée de visite | non |
    | Navigateur / OS | non |
    | Résolution écran | non |
    | Provenance (referrer) | non |
    | IP (2 derniers octets) | oui |
    | User ID (hashé) | oui |

Et aucune donnée personnelle identifiable n'est collectée

## 15. User ID hashé pour analytics

Étant donné que je suis connecté à l'application Et que j'ai accepté le consentement "Analytique"

Quand Matomo enregistre mes actions

Alors mon user_id est hashé (SHA-256) Et le hash est 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 Et il est impossible de retrouver mon user_id original depuis ce hash Et ce processus garantit l'anonymat

## 16. Conformité recommandations CNIL sur les cookies

Étant donné que RoadWave utilise Matomo self-hosted

Quand un auditeur CNIL vérifie la conformité

Alors le système respecte les recommandations CNIL 2020:

    | recommandation CNIL | respecté |
    |---|---|
    | Consentement requis pour cookies analytics | oui |
    | IP anonymisées | oui |
    | Pas de transfert hors UE | oui |
    | Durée conservation limitée (25 mois) | oui |
    | Respect Do Not Track | oui |
    | Transparence (liste cookies dans CGU) | oui |

## 17. Intégration Tarteaucitron.js pour gérer Matomo

Étant donné que Tarteaucitron.js gère les consentements

Quand je personnalise mes consentements

Alors je vois l'option "Analytique (Matomo)" Et une description est affichée: Et je peux activer ou désactiver Matomo indépendamment Et si je désactive, le cookie `_pk_id` est supprimé immédiatement

## 18. Analytics sur application mobile

Étant donné que j'utilise l'application mobile

Quand j'accepte le consentement "Analytique"

Alors l'app utilise le SDK Matomo Mobile Et les données sont envoyées à la même instance Matomo self-hosted Et les mêmes règles d'anonymisation s'appliquent Et aucun SDK tiers (Google Analytics, Firebase) n'est utilisé

## 19. Refus analytics sur mobile

Étant donné que j'ai refusé le consentement "Analytique" sur mobile

Quand j'utilise l'application

Alors aucune donnée d'usage n'est collectée Et le SDK Matomo est désactivé Et l'application fonctionne normalement sans différence d'UX

## 20. Matomo opensource et auditable

Étant donné que Matomo est opensource

Quand on consulte le code source

Alors le code est disponible publiquement sur GitHub Et le code peut être audité par des experts indépendants Et aucune backdoor ou collecte cachée n'est possible Et cette transparence renforce la confiance utilisateur

# Mode dégradé avec GeoIP (sans GPS précis)

20 scénarios (19 standards, 1 plan)

> Contexte commun à tous les scénarios

> Étant donné que je suis un nouvel utilisateur Et que je lance l'application pour la première fois

## 1. 📋 Plan: Trois niveaux de géolocalisation disponibles

Étant donné que j'utilise le niveau de géolocalisation ""

Quand le système détermine ma position

Alors la technologie utilisée est "" Et les contenus accessibles sont "" Et le consentement RGPD est ""

📊 Exemples de données:

niveau | technologie | contenus | consentement
--- | --- | --- | ---
Pays | Aucune géoloc | Contenus nationaux uniquement | Non requis
Ville | GeoIP (MaxMind) | Contenus régionaux/ville | Non requis
Précis | GPS | Tous contenus (hyperlocaux inclus) | Requis

## 2. GeoIP activé par défaut au premier lancement

Étant donné que je lance l'application pour la première fois Et que je n'ai pas encore accepté le GPS précis

Quand l'application démarre

Alors le système utilise automatiquement GeoIP basé sur mon adresse IP Et ma position est détectée au niveau ville: "Paris, France" Et aucun consentement n'est requis (GeoIP ne collecte pas de données personnelles) Et je peux accéder aux contenus régionaux et de ville

## 3. Détection de ville avec MaxMind GeoLite2

Étant donné que mon adresse IP est 93.184.216.34

Quand le système utilise GeoIP MaxMind GeoLite2

Alors ma ville est détectée: "Paris" Et ma région est détectée: "Île-de-France" Et mon pays est détecté: "France" Et la précision est d'environ 80% au niveau ville Et aucune coordonnée GPS précise n'est révélée

## 4. Banner d'invitation à activer le GPS

Étant donné que j'utilise l'application en mode GeoIP

Quand je suis sur l'écran principal

Alors un banner discret s'affiche en haut: Et le banner n'est pas intrusif (pas de popup modale) Et je peux le fermer temporairement avec un bouton X Et le banner réapparaît tous les 7 jours si je ne l'active pas

## 5. Upgrade volontaire vers GPS depuis le banner

Étant donné que le banner d'invitation au GPS est affiché

Quand je clique sur "Activer"

Alors un écran de consentement GPS s'affiche Et l'écran explique les avantages: Et je peux accepter ou refuser Et si j'accepte, la permission OS est demandée

## 6. Contenus disponibles en mode Pays (aucune géoloc)

Étant donné que je n'autorise aucune géolocalisation

Quand le système recherche du contenu à me proposer

Alors seuls les contenus "National" sont disponibles Et les contenus géolocalisés (Ancré, Contextuel) ne sont pas proposés Et je vois un message: "Activez la géolocalisation pour plus de contenu local"

## 7. Contenus disponibles en mode Ville (GeoIP)

Étant donné que j'utilise le mode GeoIP et que je suis détecté à Paris

Quand le système recherche du contenu à me proposer

Alors les contenus suivants sont disponibles:

    | type_contenu | disponible |
    |---|---|
    | National | oui |
    | Région Île-de-France | oui |
    | Ville Paris | oui |
    | Hyperlocal (GPS) | non |
    | Audio-guides | non |

Et je reçois des recommandations pertinentes pour Paris

## 8. Tous contenus disponibles en mode Précis (GPS)

Étant donné que j'ai activé la géolocalisation précise

Quand le système recherche du contenu à me proposer

Alors tous les types de contenus sont disponibles:

    | type_contenu | disponible |
    |---|---|
    | National | oui |
    | Régional | oui |
    | Ville | oui |
    | Hyperlocal (Ancré) | oui |
    | Contextuel | oui |
    | Audio-guides | oui |

## 9. GeoIP ne nécessite pas de consentement RGPD

Étant donné que j'utilise le mode GeoIP

Quand un auditeur CNIL vérifie la conformité

Alors GeoIP n'est pas considéré comme une donnée personnelle Et l'adresse IP n'est pas conservée après détection de la ville Et seule la ville est stockée (non identifiant) Et aucun consentement n'est requis conformément au RGPD

## 10. Base de données MaxMind self-hosted

Étant donné que RoadWave utilise MaxMind GeoLite2

Quand on analyse l'infrastructure

Alors la base de données GeoLite2 est hébergée sur les serveurs RoadWave Et aucune requête n'est envoyée à un service tiers Et la base de données est mise à jour automatiquement chaque mois Et le coût est de 0€ (GeoLite2 est gratuit)

## 11. Mise à jour mensuelle de la base GeoIP

Étant donné que MaxMind publie des mises à jour mensuelles

Quand le 1er du mois arrive

Alors un job automatique télécharge la nouvelle base GeoLite2 Et la base est mise à jour sans interruption de service Et un log est créé pour traçabilité Et si la mise à jour échoue, une alerte est envoyée

## 12. UX acceptable en mode GeoIP

Étant donné que j'utilise le mode GeoIP à Paris

Quand je parcours l'application

Alors je peux écouter du contenu pertinent pour Paris et l'Île-de-France Et l'expérience est satisfaisante même sans GPS précis Et je ne suis pas bloqué dans l'utilisation de l'application Et je peux choisir d'activer le GPS quand je le souhaite

## 13. Incitation progressive à activer le GPS

Étant donné que j'utilise le mode GeoIP depuis 2 semaines Et que je n'ai pas activé le GPS

Quand je consulte un audio-guide dans les résultats de recherche

Alors un message s'affiche: Et si je clique "Plus tard", je peux continuer à utiliser l'app normalement Et l'incitation reste douce et non intrusive

## 14. Upgrade GeoIP vers GPS

Étant donné que j'utilise le mode GeoIP

Quand j'active la géolocalisation précise

Alors le système bascule immédiatement en mode GPS Et les contenus hyperlocaux deviennent disponibles Et mon feed se rafraîchit avec du contenu plus précis Et un toast de confirmation s'affiche: "Géolocalisation activée"

## 15. Downgrade GPS vers GeoIP

Étant donné que j'utilise le mode GPS précis

Quand je désactive la géolocalisation dans les paramètres OS

Alors le système bascule automatiquement en mode GeoIP Et les contenus hyperlocaux ne sont plus proposés Et un banner s'affiche: "Géolocalisation désactivée. Seul le contenu régional est disponible." Et l'application continue de fonctionner normalement

## 16. Détection automatique au démarrage de l'app

Étant donné que j'ouvre l'application

Quand l'app vérifie les permissions de géolocalisation

Alors le système détecte automatiquement le mode disponible:

    | permission GPS | consentement app | mode activé |
    |---|---|---|
    | Refusée | Non demandé | Pays |
    | Refusée | Accepté | GeoIP |
    | Accordée | Accepté | GPS précis |

Et le mode est appliqué sans interaction utilisateur

## 17. Précision acceptable pour la plupart des cas

Étant donné que j'habite à Lyon Et que mon IP est une IP résidentielle standard

Quand le système utilise GeoIP pour me localiser

Alors la ville détectée est "Lyon" (correct à 80%) Et dans 20% des cas, la ville peut être légèrement erronée (banlieue proche) Et cette précision est suffisante pour proposer du contenu régional pertinent

## 18. GeoIP avec VPN ou proxy

Étant donné que j'utilise un VPN avec une IP sortante à Paris Mais que je suis physiquement à Lyon

Quand le système utilise GeoIP

Alors la ville détectée est "Paris" (IP du VPN) Et les contenus proposés sont pour Paris Et je peux activer le GPS précis pour corriger la localisation

## 19. Pas de donnée personnelle collectée avec GeoIP

Étant donné que j'utilise le mode GeoIP

Quand le système détermine ma ville via mon IP

Alors l'adresse IP n'est pas conservée après détection Et seule la ville "Paris" est stockée en base de données Et la ville seule n'est pas une donnée personnelle (RGPD) Et aucun consentement n'est donc requis

## 20. Solution GeoIP gratuite et self-hosted

Étant donné que RoadWave utilise MaxMind GeoLite2

Quand on calcule le coût de la solution

Alors le coût est de 0€ Et la solution est opensource Et la base de données est hébergée sur les serveurs RoadWave Et aucun coût SaaS tiers

# Portabilité des données (Article 20 RGPD)

22 scénarios (21 standards, 1 plan)

> Contexte commun à tous les scénarios

> Étant donné que je suis un utilisateur connecté Et que j'ai utilisé l'application depuis 6 mois

## 1. Demande d'export depuis les paramètres

Étant donné que je suis dans "Paramètres > Confidentialité"

Quand je clique sur "Exporter mes données"

Alors une page d'information s'affiche expliquant: Et un bouton "Confirmer l'export" est disponible

## 2. Confirmation et démarrage de l'export

Étant donné que je clique sur "Confirmer l'export"

Quand la demande est validée

Alors un message de confirmation s'affiche: Et un worker background démarre la génération de l'export Et le statut de l'export est "En cours de génération" Et je peux voir le statut dans "Paramètres > Confidentialité > Mes exports"

## 3. Contenu de l'archive ZIP

Étant donné que mon export est généré

Quand je télécharge et ouvre l'archive

Alors l'archive a la structure suivante: Et tous les fichiers sont inclus

## 4. Contenu du fichier export.json

Étant donné que j'ouvre le fichier export.json

Quand j'analyse le contenu

Alors le JSON contient les sections suivantes:

    | section | description |
    |---|---|
    | profile | Email, pseudo, date inscription, bio |
    | listening_history | Historique complet d'écoute |
    | created_contents | Métadonnées des contenus créés |
    | subscriptions | Liste des créateurs suivis |
    | likes | Liste des contenus likés |
    | interest_gauges | Valeurs des jauges d'intérêt |
    | consent_history | Historique des consentements |
    | premium_subscription | Informations abonnement Premium |

Et le JSON est formaté de manière lisible (indentation) Et toutes les dates sont au format ISO 8601

## 5. Contenu du fichier index.html

Étant donné que j'ouvre le fichier index.html dans un navigateur

Quand la page se charge

Alors je vois un site web stylé avec navigation Et les sections suivantes sont affichées:

    | section | contenu |
    |---|---|
    | Mon profil | Email, pseudo, date inscription, statistiques |
    | Historique d'écoute | Liste paginée avec dates, titres, durées |
    | Mes contenus | Liste avec lectures audio intégrées |
    | Mes abonnements | Grille des créateurs suivis |
    | Mes likes | Liste des contenus likés avec liens |
    | Centres d'intérêt | Graphiques des jauges |
    | Consentements | Historique des acceptations/refus |

Et la navigation est intuitive (menu latéral) Et le design est responsive (mobile/desktop)

## 6. Fichiers audio inclus dans l'export

Étant donné que j'ai créé 5 contenus audio

Quand mon export est généré

Alors le dossier `audio/` contient mes 5 fichiers Et les fichiers sont au format Opus original Et chaque fichier est nommé: `content-[id].opus` Et les fichiers audio correspondent aux métadonnées dans export.json

## 7. Fichier README.txt explicatif

Étant donné que j'ouvre le fichier README.txt

Quand je lis le contenu

Alors le fichier explique:

## 8. 📋 Plan: Données de profil exportées

Étant donné que mon export est généré

Quand j'ouvre export.json et lis la section "profile"

Alors je trouve les données suivantes:

    | champ | exemple |
    |---|---|
    | email | user@example.com |
    | pseudo | @roadwave_user |
    | date_inscription | 2025-01-15T10:30:00Z |
    | bio | Passionné d'automobile... |
    | avatar_url | https://cdn.roadwave.fr/... |
    | compte_verifie | false |
    | premium | true |

## 9. Historique d'écoute exporté

Étant donné que j'ai écouté 150 contenus depuis 6 mois

Quand mon export est généré

Alors la section "listening_history" contient 150 entrées Et chaque entrée contient:

    | champ | exemple |
    |---|---|
    | content_id | C123 |
    | content_title | Histoire de la Tour Eiffel |
    | creator_name | @historien_paris |
    | listened_at | 2025-01-20T15:30:00Z |
    | duration_listened | 180 (secondes) |
    | completion_rate | 0.85 (85%) |
    | location | [geohash ou coords précises] |

Et les contenus sont triés par date décroissante

## 10. Centres d'intérêt exportés

Étant donné que mes jauges d'intérêt sont:

    | catégorie | valeur |
    |---|---|
    | Automobile | 78% |
    | Voyage | 65% |
    | Musique | 52% |
    | Politique | 30% |

Quand mon export est généré

Alors la section "interest_gauges" contient ces valeurs Et chaque jauge indique la date de dernière modification

## 11. Historique des consentements exporté

Étant donné que j'ai modifié mes consentements plusieurs fois

Quand mon export est généré

Alors la section "consent_history" contient:

    | date | consent_type | accepted | version |
    |---|---|---|---|
    | 2025-01-15T10:00 | Fonctionnel | oui | 1 |
    | 2025-01-15T10:00 | Analytique | oui | 1 |
    | 2025-01-15T10:00 | Marketing | non | 1 |
    | 2025-03-20T14:30 | Analytique | non | 1 |

Et l'historique complet est visible

## 12. Génération asynchrone pour éviter timeout

Étant donné que j'ai beaucoup de données (500 contenus créés, 10 000 écoutes)

Quand je demande un export

Alors la génération se fait en arrière-plan via un worker Et la page web ne timeout pas Et je peux continuer à utiliser l'application pendant la génération Et je reçois un email quand l'export est prêt

## 13. Délai de génération conforme RGPD

Étant donné que je demande un export le 2025-01-20 à 10:00

Quand le worker génère l'export

Alors l'export est disponible maximum 48h plus tard (avant le 2025-01-22 à 10:00) Et la plupart des exports sont prêts en moins de 6h Et le délai respecte l'article 20 du RGPD

## 14. Email de notification avec lien de téléchargement

Étant donné que mon export est terminé

Quand le worker finalise la génération

Alors je reçois un email avec le sujet "Votre export de données RoadWave est prêt" Et l'email contient: Et le lien de téléchargement est sécurisé (token unique)

## 15. Lien de téléchargement expire après 7 jours

Étant donné que mon export a été généré le 2025-01-20 Et que je reçois le lien de téléchargement

Quand j'essaie d'accéder au lien le 2025-01-28 (8 jours plus tard)

Alors le lien est expiré Et je reçois un message "Ce lien a expiré. Veuillez demander un nouvel export." Et je peux demander un nouvel export si nécessaire

## 16. Limite de 1 export par mois

Étant donné que j'ai demandé un export le 2025-01-15

Quand j'essaie de demander un nouvel export le 2025-01-20

Alors je reçois un message d'erreur: Et le bouton "Confirmer l'export" est désactivé Et la date du prochain export possible est affichée

## 17. Nouvel export possible après 1 mois

Étant donné que j'ai demandé un export le 2025-01-15

Quand la date atteint le 2025-02-15

Alors je peux demander un nouvel export Et le bouton "Confirmer l'export" est actif Et aucune limite ne s'applique

## 18. Lien de téléchargement sécurisé avec token unique

Étant donné que mon export est prêt

Quand je reçois le lien de téléchargement

Alors le lien contient un token unique et non devinable Et le format du lien est: `https://roadwave.fr/exports/download/[token_unique]` Et le token est valide uniquement pour mon compte Et le token expire après 7 jours ou après 3 téléchargements

## 19. Vérification de l'authentification avant téléchargement

Étant donné que je reçois le lien d'export

Quand je clique sur le lien

Alors le système vérifie que je suis connecté Et si je ne suis pas connecté, je suis redirigé vers la page de connexion Et après connexion, le téléchargement démarre automatiquement Et seul le propriétaire du compte peut télécharger l'export

## 20. Conformité portabilité des données

Étant donné que mon export est généré

Quand un auditeur RGPD vérifie la conformité

Alors l'export respecte les exigences de l'article 20:

    | exigence RGPD | respecté |
    |---|---|
    | Format structuré (JSON) | oui |
    | Format couramment utilisé | oui |
    | Format lisible par machine | oui |
    | Format interopérable | oui |
    | Délai raisonnable (48h max) | oui |
    | Exhaustivité des données | oui |
    | Gratuité pour l'utilisateur | oui |

## 21. Gratuité de l'export

Étant donné que je demande un export de mes données

Quand l'export est généré et téléchargé

Alors aucun coût n'est facturé Et l'export est entièrement gratuit Et aucune inscription Premium n'est requise Et le droit à la portabilité est accessible à tous les utilisateurs

## 22. Suivi du statut de génération

Étant donné que j'ai demandé un export

Quand j'ouvre "Paramètres > Confidentialité > Mes exports"

Alors je vois le statut actuel:

    | statut | description |
    |---|---|
    | En cours de génération | Worker en train de générer l'archive |
    | Prêt au téléchargement | Lien de téléchargement disponible |
    | Expiré | Lien expiré (>7j), nouvel export requis |

Et la date de demande est affichée Et la taille estimée de l'archive est visible

# Suppression du compte utilisateur (Article 17 RGPD - Droit à l'effacement)

21 scénarios

> Contexte commun à tous les scénarios

> Étant donné que je suis un utilisateur connecté Et que j'ai utilisé l'application depuis plusieurs mois

## 1. Demande de suppression depuis les paramètres

Étant donné que je suis dans "Paramètres > Compte"

Quand je clique sur "Supprimer mon compte"

Alors une page d'avertissement s'affiche avec le message: Et deux boutons sont disponibles: "Annuler" et "Confirmer la suppression"

## 2. Confirmation de suppression avec mot de passe

Étant donné que je clique sur "Confirmer la suppression"

Quand un formulaire de confirmation s'affiche

Alors je dois entrer mon mot de passe pour confirmer Et je dois cocher "Je comprends que cette action est définitive" Et un captcha peut être requis pour éviter les suppressions automatisées

Quand je valide le formulaire

Alors la suppression est initiée

## 3. Compte désactivé immédiatement après confirmation

Étant donné que j'ai confirmé la suppression de mon compte

Quand la demande est traitée

Alors mon compte est désactivé immédiatement Et je suis déconnecté de toutes mes sessions Et je ne peux plus me reconnecter Et si j'essaie de me connecter, je reçois le message:

## 4. Contenus cachés pendant le grace period

Étant donné que mon compte est en cours de suppression

Quand un autre utilisateur recherche mes contenus

Alors mes contenus ne sont plus diffusés dans l'application Et mes contenus n'apparaissent plus dans les recherches Et mes contenus ne sont plus recommandés Mais mes contenus ne sont pas encore supprimés définitivement

## 5. Email de confirmation envoyé immédiatement

Étant donné que j'ai confirmé la suppression de mon compte

Quand la demande est traitée

Alors je reçois un email avec le sujet "Confirmation de suppression de votre compte RoadWave" Et l'email contient: Et le lien d'annulation est valide 30 jours

## 6. Annulation de la suppression dans les 30 jours

Étant donné que j'ai demandé la suppression de mon compte le 2025-01-20 Et que je reçois l'email de confirmation

Quand je clique sur le lien "Annuler la suppression" le 2025-02-05 (16 jours plus tard)

Alors mon compte est réactivé immédiatement Et je peux me reconnecter normalement Et mes contenus redeviennent visibles dans l'application Et toutes mes données sont restaurées Et je reçois un email de confirmation: "Votre compte a été réactivé"

## 7. Lien d'annulation expire après 30 jours

Étant donné que j'ai demandé la suppression de mon compte le 2025-01-20

Quand j'essaie de cliquer sur le lien d'annulation le 2025-02-25 (36 jours plus tard)

Alors le lien est expiré Et je reçois un message "Ce lien a expiré. Votre compte a été définitivement supprimé." Et la suppression effective a déjà eu lieu

## 8. Suppression effective sans annulation

Étant donné que j'ai demandé la suppression de mon compte le 2025-01-20 Et que je n'ai pas cliqué sur le lien d'annulation

Quand la date atteint le 2025-02-19 (30 jours plus tard)

Alors un job automatique exécute la suppression définitive Et toutes mes données personnelles sont supprimées

## 9. Liste des données supprimées définitivement

Étant donné que la suppression effective est exécutée

Quand le job de suppression se termine

Alors les données suivantes sont supprimées:

    | données | supprimé |
    |---|---|
    | Compte utilisateur (email, mdp) | oui |
    | Profil (pseudo, bio, avatar) | oui |
    | Historique d'écoute | oui |
    | Historique GPS | oui |
    | Centres d'intérêt (jauges) | oui |
    | Sessions et tokens | oui |
    | Likes et abonnements | oui |
    | Notifications non lues | oui |
    | Historique consentements | oui |
    | Données de paiement | oui |

Et ces suppressions sont irréversibles

## 10. Anonymisation des contenus créés

Étant donné que j'ai créé 10 contenus audio

Quand la suppression effective est exécutée

Alors mes contenus audio restent disponibles dans l'application Et le nom du créateur devient "Utilisateur supprimé" Et mon pseudo n'est plus visible Et les métadonnées (titre, description, tags, géolocalisation) sont conservées Et les fichiers audio restent sur le CDN Et les statistiques d'écoute sont conservées

## 11. Justification de l'anonymisation (intérêt légitime)

Étant donné que mes contenus sont conservés anonymement

Quand un auditeur RGPD vérifie la conformité

Alors la conservation est justifiée par l'intérêt légitime de la communauté Et les contenus ne contiennent plus de données personnelles identifiables Et la suppression complète nuirait à l'expérience des autres utilisateurs Et cette pratique est conforme au RGPD si anonymisation réelle

## 12. Contenu anonymisé visible pour les autres utilisateurs

Étant donné que mon compte a été supprimé Et que mes contenus ont été anonymisés

Quand un utilisateur consulte un de mes anciens contenus

Alors le créateur affiché est "Utilisateur supprimé" Et le profil du créateur n'est plus accessible Et le contenu reste écoutable normalement Et les likes et statistiques sont conservés

## 13. Suppression de mes likes avec conservation des compteurs

Étant donné que j'avais liké 50 contenus

Quand la suppression effective est exécutée

Alors mes likes sont supprimés de la base de données Mais les compteurs de likes sur les contenus sont préservés Et les créateurs ne perdent pas leurs statistiques Et seule la relation "user X a liké content Y" est supprimée

## 14. Suppression de mes abonnements

Étant donné que je suivais 20 créateurs

Quand la suppression effective est exécutée

Alors mes abonnements sont supprimés Et les compteurs d'abonnés des créateurs sont décrémentés de 1 Et les créateurs ne reçoivent pas de notification de désabonnement

## 15. Révocation de tous les tokens immédiatement

Étant donné que je suis connecté sur 3 appareils (mobile, tablette, web)

Quand je demande la suppression de mon compte

Alors tous mes tokens d'authentification sont révoqués immédiatement Et je suis déconnecté de tous mes appareils Et toute tentative de reconnexion échoue

## 16. Rappels par email pendant le grace period

Étant donné que j'ai demandé la suppression de mon compte le 2025-01-20

Quand le grace period s'écoule

Alors je reçois des emails de rappel:

    | date | jours restants | sujet email |
    |---|---|---|
    | 2025-02-04 | 15 jours | Plus que 15 jours pour annuler la suppression |
    | 2025-02-12 | 7 jours | Dernière semaine pour annuler la suppression |
    | 2025-02-17 | 2 jours | Attention: suppression définitive dans 2 jours |

Et chaque email contient le lien d'annulation

## 17. Conformité droit à l'effacement

Étant donné que la suppression de mon compte est complète

Quand un auditeur RGPD vérifie la conformité

Alors le processus respecte l'article 17 du RGPD:

    | exigence RGPD | respecté |
    |---|---|
    | Suppression de toutes les données personnelles | oui |
    | Délai raisonnable (30j grace period acceptable) | oui |
    | Possibilité d'annulation (bonne pratique) | oui |
    | Anonymisation des contenus (intérêt légitime) | oui |
    | Révocation des tokens et sessions | oui |
    | Suppression irréversible | oui |

## 18. Suppression d'un compte Premium

Étant donné que j'ai un abonnement Premium actif

Quand je demande la suppression de mon compte

Alors mon abonnement est annulé immédiatement Et aucun remboursement n'est effectué (conformément aux CGV) Et je reçois un email de confirmation d'annulation de l'abonnement Et le reste du processus de suppression se déroule normalement

## 19. Suppression d'un compte créateur avec revenus en attente

Étant donné que je suis un créateur avec 75€ de revenus en attente de paiement

Quand je demande la suppression de mon compte

Alors un message m'informe: Et je peux choisir "Recevoir le paiement et attendre" ou "Renoncer au paiement" Et si je choisis "Recevoir le paiement", la suppression est repoussée de 7 jours

## 20. Suppression avec signalements de modération en cours

Étant donné que j'ai 2 signalements en cours de traitement

Quand je demande la suppression de mon compte

Alors les signalements sont automatiquement clôturés Et les contenus signalés sont masqués immédiatement Et aucune sanction n'est appliquée (compte déjà en suppression)

## 21. Log de la suppression pour traçabilité

Étant donné que la suppression effective est exécutée

Quand le job de suppression se termine

Alors un log est créé avec:

    | champ | valeur |
    |---|---|
    | user_id | [ID anonymisé] |
    | deletion_requested_at | 2025-01-20T10:00:00Z |
    | deletion_executed_at | 2025-02-19T02:00:00Z |
    | deletion_cancelled | false |
    | data_deleted | [liste des tables] |
    | contents_anonymized | 10 |

Et ce log est conservé 5 ans pour audit RGPD Et l'user_id est pseudonymisé pour anonymat